• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
ScopedPopenFile(const char * command)42     ScopedPopenFile(const char* command) {
43         mFile = ::popen(command, "r");
44     }
45 
get() const46     FILE* get() const { return mFile; }
47 
~ScopedPopenFile()48     ~ScopedPopenFile() {
49         if (mFile) {
50             ::pclose(mFile);
51         }
52     }
53 
54 private:
55     FILE* mFile;
56 };
57 #endif  // !_WIN32
58 
getFileDescription(void * opaque,const char * filePath,String * text)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 
setFileDescriptionFunction(GetFileDescriptionFunction * file_func,void * file_opaque)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 
android_pathProbeKernelType(const char * kernelPath,KernelType * ktype)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 
android_kernelSerialDevicePrefix(KernelType ktype)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