Home | History | Annotate | Download | only in kernel
      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