• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
setRootDir(const std::string & rootdir)48     void setRootDir(const std::string& rootdir) {
49         mRootDir = rootdir;
50         if (!mRootDir.empty() && mRootDir.back() != '/') {
51             mRootDir.push_back('/');
52         }
53     }
fetch(const std::string & path,std::string & fetched,std::string * error)54     virtual status_t fetch(const std::string& path, std::string& fetched, std::string* error) {
55         return HostFileFetcher::fetchInternal(path, fetched, error);
56     }
fetch(const std::string & path,std::string & fetched)57     virtual status_t fetch(const std::string& path, std::string& fetched) {
58         return HostFileFetcher::fetchInternal(path, fetched, nullptr);
59     }
listFiles(const std::string & path,std::vector<std::string> * out,std::string * error)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:
fetchInternal(const std::string & path,std::string & fetched,std::string * error)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     }
toString(status_t status)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:
getProperty(const std::string & key,const std::string & defaultValue) const81     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     }
getUintProperty(const std::string & key,uint64_t defaultValue,uint64_t max) const92     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     }
getBoolProperty(const std::string & key,bool defaultValue) const99     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     }
setProperties(const Properties & props)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;
getPropertyFetcher()125 const PropertyFetcher& getPropertyFetcher() {
126     return hostPropertyFetcher;
127 }
128 
129 // helper functions
130 template <typename T>
readObject(const std::string & path,const XmlConverter<T> & converter)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 
checkCompatibilityForFiles(const std::string & manifestPath,const std::string & matrixPath)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 
parseArgs(int argc,char ** argv)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>
getProperties(const T & args)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 
usage(const char * me)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 
checkAllFiles(const std::string & rootdir,const Properties & props,std::string * error)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 
main(int argc,char ** argv)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