blob: 50b7094a70feade357bdc0f8ed9647a85b89bb18 [file] [log] [blame]
David 'Digit' Turnerc6e0cae2014-03-07 23:08:30 +01001// 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
33using android::base::String;
34
35namespace {
36
37#ifndef _WIN32
38// Helper class to perform launch a command through popen() and call
39// pclose() on destruction.
40class ScopedPopenFile {
41public:
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
54private:
55 FILE* mFile;
56};
57#endif // !_WIN32
58
59bool 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
101android::kernel::GetFileDescriptionFunction* sGetFileDescription =
102 getFileDescription;
103
104void* sGetFileDescriptionOpaque = NULL;
105
106} // namespace
107
108namespace android {
109namespace kernel {
110
111void 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
120bool android_pathProbeKernelType(const char* kernelPath, KernelType* ktype) {
121 String description;
122
123 if (!sGetFileDescription(sGetFileDescriptionOpaque,
124 kernelPath,
125 &description)) {
126 return false;
127 }
David 'Digit' Turnere1ae4002014-03-27 11:30:19 +0100128 const char* bzImage = ::strstr(description.c_str(), "bzImage");
David 'Digit' Turnerc6e0cae2014-03-07 23:08:30 +0100129 if (!bzImage) {
130 KERNEL_ERROR << "Not a compressed Linux kernel image!";
131 return false;
132 }
David 'Digit' Turnere1ae4002014-03-27 11:30:19 +0100133 const char* version = ::strstr(bzImage, "version ");
David 'Digit' Turnerc6e0cae2014-03-07 23:08:30 +0100134 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
167const 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}