Home | History | Annotate | Download | only in libvintf
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <getopt.h>
     18 #include <unistd.h>
     19 
     20 #include <iostream>
     21 #include <map>
     22 
     23 #include <android-base/parseint.h>
     24 #include <utils/Errors.h>
     25 #include <vintf/VintfObject.h>
     26 #include <vintf/parse_xml.h>
     27 #include "utils.h"
     28 
     29 namespace android {
     30 namespace vintf {
     31 namespace details {
     32 
     33 // fake sysprops
     34 using Properties = std::map<std::string, std::string>;
     35 
     36 enum Option : int {
     37     DUMP_FILE_LIST = 1,
     38     ROOTDIR,
     39     HELP,
     40     PROPERTY,
     41     CHECK_COMPAT,
     42 };
     43 // command line arguments
     44 using Args = std::multimap<Option, std::string>;
     45 
     46 class HostFileFetcher : public FileFetcher {
     47    public:
     48     void setRootDir(const std::string& rootdir) {
     49         mRootDir = rootdir;
     50         if (!mRootDir.empty() && mRootDir.back() != '/') {
     51             mRootDir.push_back('/');
     52         }
     53     }
     54     virtual status_t fetch(const std::string& path, std::string& fetched, std::string* error) {
     55         return HostFileFetcher::fetchInternal(path, fetched, error);
     56     }
     57     virtual status_t fetch(const std::string& path, std::string& fetched) {
     58         return HostFileFetcher::fetchInternal(path, fetched, nullptr);
     59     }
     60     virtual status_t listFiles(const std::string& path, std::vector<std::string>* out,
     61                                std::string* error) {
     62         status_t status = FileFetcher::listFiles(mRootDir + path, out, error);
     63         std::cerr << "Debug: List '" << mRootDir << path << "': " << toString(status) << std::endl;
     64         return status;
     65     }
     66 
     67    private:
     68     status_t fetchInternal(const std::string& path, std::string& fetched, std::string* error) {
     69         status_t status = FileFetcher::fetchInternal(mRootDir + path, fetched, error);
     70         std::cerr << "Debug: Fetch '" << mRootDir << path << "': " << toString(status) << std::endl;
     71         return status;
     72     }
     73     static std::string toString(status_t status) {
     74         return status == OK ? "SUCCESS" : strerror(-status);
     75     }
     76     std::string mRootDir;
     77 };
     78 
     79 class PresetPropertyFetcher : public PropertyFetcher {
     80    public:
     81     std::string getProperty(const std::string& key,
     82                             const std::string& defaultValue) const override {
     83         auto it = mProps.find(key);
     84         if (it == mProps.end()) {
     85             std::cerr << "Debug: Sysprop " << key << " is missing, default to '" << defaultValue
     86                       << "'" << std::endl;
     87             return defaultValue;
     88         }
     89         std::cerr << "Debug: Sysprop " << key << "=" << it->second << std::endl;
     90         return it->second;
     91     }
     92     uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
     93                              uint64_t max) const override {
     94         uint64_t result;
     95         std::string value = getProperty(key, "");
     96         if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
     97         return defaultValue;
     98     }
     99     bool getBoolProperty(const std::string& key, bool defaultValue) const override {
    100         std::string value = getProperty(key, "");
    101         if (value == "1" || value == "true") {
    102             return true;
    103         } else if (value == "0" || value == "false") {
    104             return false;
    105         }
    106         return defaultValue;
    107     }
    108     void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
    109 
    110    private:
    111     std::map<std::string, std::string> mProps;
    112 };
    113 
    114 // globals
    115 static HostFileFetcher hostFileFetcher;
    116 FileFetcher* gFetcher = &hostFileFetcher;
    117 
    118 static PartitionMounter partitionMounter;
    119 PartitionMounter* gPartitionMounter = &partitionMounter;
    120 
    121 static ObjectFactory<RuntimeInfo> runtimeInfoFactory;
    122 ObjectFactory<RuntimeInfo>* gRuntimeInfoFactory = &runtimeInfoFactory;
    123 
    124 static PresetPropertyFetcher hostPropertyFetcher;
    125 const PropertyFetcher& getPropertyFetcher() {
    126     return hostPropertyFetcher;
    127 }
    128 
    129 // helper functions
    130 template <typename T>
    131 std::unique_ptr<T> readObject(const std::string& path, const XmlConverter<T>& converter) {
    132     std::string xml;
    133     std::string error;
    134     status_t err = details::gFetcher->fetch(path, xml, &error);
    135     if (err != OK) {
    136         std::cerr << "Error: Cannot read '" << path << "' (" << strerror(-err) << "): " << error
    137                   << std::endl;
    138         return nullptr;
    139     }
    140     auto ret = std::make_unique<T>();
    141     if (!converter(ret.get(), xml, &error)) {
    142         std::cerr << "Error: Cannot parse '" << path << "': " << error << std::endl;
    143         return nullptr;
    144     }
    145     return ret;
    146 }
    147 
    148 int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
    149     auto manifest = readObject(manifestPath, gHalManifestConverter);
    150     auto matrix = readObject(matrixPath, gCompatibilityMatrixConverter);
    151     if (manifest == nullptr || matrix == nullptr) {
    152         return -1;
    153     }
    154 
    155     std::string error;
    156     if (!manifest->checkCompatibility(*matrix, &error)) {
    157         std::cerr << "Error: Incompatible: " << error << std::endl;
    158         std::cout << "false" << std::endl;
    159         return 1;
    160     }
    161 
    162     std::cout << "true" << std::endl;
    163     return 0;
    164 }
    165 
    166 Args parseArgs(int argc, char** argv) {
    167     int longOptFlag;
    168     int optionIndex;
    169     Args ret;
    170     std::vector<struct option> longopts{
    171         {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
    172         {"rootdir", required_argument, &longOptFlag, ROOTDIR},
    173         {"help", no_argument, &longOptFlag, HELP},
    174         {"property", required_argument, &longOptFlag, PROPERTY},
    175         {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
    176         {0, 0, 0, 0}};
    177     std::map<int, Option> shortopts{
    178         {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
    179     };
    180     for (;;) {
    181         int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
    182         if (c == -1) {
    183             break;
    184         }
    185         std::string argValue = optarg ? optarg : std::string{};
    186         if (c == 0) {
    187             ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
    188         } else {
    189             ret.emplace(shortopts[c], std::move(argValue));
    190         }
    191     }
    192     if (optind < argc) {
    193         // see non option
    194         std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
    195         return {{HELP, ""}};
    196     }
    197     return ret;
    198 }
    199 
    200 template <typename T>
    201 Properties getProperties(const T& args) {
    202     Properties ret;
    203     for (const auto& arg : args) {
    204         auto pos = arg.find('=');
    205         auto key = arg.substr(0, pos);
    206         auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1);
    207         ret[key] = value;
    208     }
    209     return ret;
    210 }
    211 
    212 int usage(const char* me) {
    213     std::cerr
    214         << me << ": check VINTF metadata." << std::endl
    215         << "    Options:" << std::endl
    216         << "        --dump-file-list: Dump a list of directories / files on device" << std::endl
    217         << "                that is required to be used by --check-compat." << std::endl
    218         << "        -c, --check-compat: check compatibility for files under the root" << std::endl
    219         << "                directory specified by --root-dir." << std::endl
    220         << "        --rootdir=<dir>: specify root directory for all metadata." << std::endl
    221         << "        -D, --property <key>=<value>: specify sysprops." << std::endl
    222         << "        --help: show this message." << std::endl
    223         << std::endl
    224         << "    Example:" << std::endl
    225         << "        # Get the list of required files." << std::endl
    226         << "        " << me << " --dump-file-list > /tmp/files.txt" << std::endl
    227         << "        # Pull from ADB, or use your own command to extract files from images"
    228         << std::endl
    229         << "        ROOTDIR=/tmp/device/" << std::endl
    230         << "        cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
    231            "pull {} $ROOTDIR{}\""
    232         << std::endl
    233         << "        # Check compatibility." << std::endl
    234         << "        " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
    235         << "            --property ro.product.first_api_level=`adb shell getprop "
    236            "ro.product.first_api_level` \\"
    237         << std::endl
    238         << "            --property ro.boot.product.hardware.sku=`adb shell getprop "
    239            "ro.boot.product.hardware.sku`"
    240         << std::endl;
    241     return 1;
    242 }
    243 
    244 int checkAllFiles(const std::string& rootdir, const Properties& props, std::string* error) {
    245     hostFileFetcher.setRootDir(rootdir);
    246     hostPropertyFetcher.setProperties(props);
    247 
    248     return VintfObject::CheckCompatibility({} /* packageInfo */, error, DISABLE_RUNTIME_INFO);
    249 }
    250 
    251 }  // namespace details
    252 }  // namespace vintf
    253 }  // namespace android
    254 
    255 int main(int argc, char** argv) {
    256     using namespace android::vintf;
    257     using namespace android::vintf::details;
    258     // legacy usage: check_vintf <manifest.xml> <matrix.xml>
    259     if (argc == 3) {
    260         int ret = checkCompatibilityForFiles(argv[1], argv[2]);
    261         if (ret >= 0) return ret;
    262     }
    263 
    264     Args args = parseArgs(argc, argv);
    265 
    266     if (!iterateValues(args, HELP).empty()) {
    267         return usage(argv[0]);
    268     }
    269 
    270     if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
    271         for (const auto& file : dumpFileList()) {
    272             std::cout << file << std::endl;
    273         }
    274         return 0;
    275     }
    276 
    277     auto rootdirs = iterateValues(args, ROOTDIR);
    278     auto properties = getProperties(iterateValues(args, PROPERTY));
    279 
    280     auto checkCompat = iterateValues(args, CHECK_COMPAT);
    281     if (!checkCompat.empty()) {
    282         if (rootdirs.empty()) {
    283             std::cerr << "Missing --rootdir option." << std::endl;
    284             return usage(argv[0]);
    285         }
    286         int ret = COMPATIBLE;
    287         for (const auto& rootdir : rootdirs) {
    288             std::cerr << "Debug: checking files under " << rootdir << "..." << std::endl;
    289             std::string error;
    290             int compat = checkAllFiles(rootdir, properties, &error);
    291             std::cerr << "Debug: files under " << rootdir
    292                       << (compat == COMPATIBLE
    293                               ? " is compatible"
    294                               : compat == INCOMPATIBLE ? " are incompatible"
    295                                                        : (" has encountered an error: " + error))
    296                       << std::endl;
    297         }
    298         if (ret == COMPATIBLE) {
    299             std::cout << "true" << std::endl;
    300         }
    301         return ret;
    302     }
    303 
    304     return usage(argv[0]);
    305 }
    306