1 // Copyright 2014 The Android Open Source Project 2 // 3 // This software is licensed under the terms of the GNU General Public 4 // License version 2, as published by the Free Software Foundation, and 5 // may be copied, distributed, and modified under those terms. 6 // 7 // This program is distributed in the hope that it will be useful, 8 // but WITHOUT ANY WARRANTY; without even the implied warranty of 9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 // GNU General Public License for more details. 11 12 #include "android/kernel/kernel_utils.h" 13 14 #include "android/base/Log.h" 15 #include "android/base/files/ScopedStdioFile.h" 16 #include "android/base/String.h" 17 #include "android/kernel/kernel_utils_testing.h" 18 #include "android/utils/path.h" 19 20 #include <stdint.h> 21 #include <stdio.h> 22 #include <stdlib.h> 23 #include <string.h> 24 #include <strings.h> 25 26 #define DEBUG_KERNEL 0 27 28 #define KERNEL_LOG LOG_IF(INFO, DEBUG_KERNEL) 29 #define KERNEL_PLOG PLOG_IF(INFO, DEBUG_KERNEL) 30 #define KERNEL_ERROR LOG_IF(ERROR, DEBUG_KERNEL) 31 #define KERNEL_PERROR PLOG_IF(ERROR, DEBUG_KERNEL) 32 33 using android::base::String; 34 35 namespace { 36 37 #ifndef _WIN32 38 // Helper class to perform launch a command through popen() and call 39 // pclose() on destruction. 40 class ScopedPopenFile { 41 public: 42 ScopedPopenFile(const char* command) { 43 mFile = ::popen(command, "r"); 44 } 45 46 FILE* get() const { return mFile; } 47 48 ~ScopedPopenFile() { 49 if (mFile) { 50 ::pclose(mFile); 51 } 52 } 53 54 private: 55 FILE* mFile; 56 }; 57 #endif // !_WIN32 58 59 bool getFileDescription(void* opaque, const char* filePath, String* text) { 60 if (!filePath) { 61 KERNEL_ERROR << "NULL path parameter"; 62 return false; 63 } 64 65 if (!path_exists(filePath)) { 66 KERNEL_ERROR << "Kernel file doesn't exist: " << filePath; 67 return false; 68 } 69 70 #ifdef _WIN32 71 // TODO(digit): Better/portable detection based on libmagic or something. 72 KERNEL_ERROR << "Can't detect kernel version on Windows!"; 73 return false; 74 #else 75 // NOTE: Use /usr/bin/file instead of 'file' because the latter can 76 // be broken in certain environments (e.g. some versions of MacPorts). 77 String command("/usr/bin/file "); 78 command += filePath; 79 80 ScopedPopenFile file(command.c_str()); 81 if (!file.get()) { 82 KERNEL_PERROR << "Could not launch command: " << command.c_str(); 83 return false; 84 } 85 86 String result; 87 const size_t kReserveSize = 256U; 88 result.resize(kReserveSize); 89 90 int ret = ::fread(&result[0], 1, kReserveSize, file.get()); 91 if (ret < static_cast<int>(kReserveSize) && ferror(file.get())) { 92 KERNEL_ERROR << "Could not read file command output!?"; 93 return false; 94 } 95 result.resize(ret); 96 text->assign(result); 97 return true; 98 #endif 99 } 100 101 android::kernel::GetFileDescriptionFunction* sGetFileDescription = 102 getFileDescription; 103 104 void* sGetFileDescriptionOpaque = NULL; 105 106 } // namespace 107 108 namespace android { 109 namespace kernel { 110 111 void setFileDescriptionFunction(GetFileDescriptionFunction* file_func, 112 void* file_opaque) { 113 sGetFileDescription = file_func ? file_func : &getFileDescription; 114 sGetFileDescriptionOpaque = file_func ? file_opaque : NULL; 115 } 116 117 } // namespace kernel 118 } // namespace android 119 120 bool android_pathProbeKernelType(const char* kernelPath, KernelType* ktype) { 121 String description; 122 123 if (!sGetFileDescription(sGetFileDescriptionOpaque, 124 kernelPath, 125 &description)) { 126 return false; 127 } 128 const char* bzImage = ::strstr(description.c_str(), "bzImage"); 129 if (!bzImage) { 130 KERNEL_ERROR << "Not a compressed Linux kernel image!"; 131 return false; 132 } 133 const char* version = ::strstr(bzImage, "version "); 134 if (!version) { 135 KERNEL_ERROR << "Could not determine version!"; 136 return false; 137 } 138 version += ::strlen("version "); 139 KERNEL_LOG << "Found kernel version " << version; 140 141 char* end; 142 unsigned long major = ::strtoul(version, &end, 10); 143 if (end == version || *end != '.') { 144 KERNEL_ERROR << "Could not find kernel major version!"; 145 return false; 146 } 147 KERNEL_LOG << "Kernel major version: " << major; 148 if (major > 3) { 149 *ktype = KERNEL_TYPE_3_10_OR_ABOVE; 150 } else if (major < 3) { 151 *ktype = KERNEL_TYPE_LEGACY; 152 } else /* major == 3 */ { 153 version = end + 1; 154 unsigned long minor = ::strtoul(version, &end, 10); 155 if (end == version) { 156 KERNEL_ERROR << "Could not find kernel minor version!"; 157 return false; 158 } 159 KERNEL_LOG << "Kernel minor version: " << minor; 160 161 *ktype = (minor >= 10) 162 ? KERNEL_TYPE_3_10_OR_ABOVE : KERNEL_TYPE_LEGACY; 163 } 164 return true; 165 } 166 167 const char* android_kernelSerialDevicePrefix(KernelType ktype) { 168 switch (ktype) { 169 case KERNEL_TYPE_LEGACY: return "ttyS"; 170 case KERNEL_TYPE_3_10_OR_ABOVE: return "ttyGF"; 171 default: return ""; 172 } 173 } 174