blob: 1c9f92e02e521e199a06b2f3997eb94ba6e5cbc7 [file] [log] [blame]
Yifan Honga72bde72017-09-28 13:36:48 -07001/*
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
Yifan Hong69c1b112018-02-27 17:06:00 -080017#include <getopt.h>
Yifan Hong62503e12019-08-26 12:47:41 -070018#include <sysexits.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080019#include <unistd.h>
Yifan Honga72bde72017-09-28 13:36:48 -070020
Yifan Hong69c1b112018-02-27 17:06:00 -080021#include <iostream>
22#include <map>
23
Yifan Hong07292852019-08-21 15:40:30 -070024#include <android-base/file.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080025#include <android-base/parseint.h>
Yifan Hong07292852019-08-21 15:40:30 -070026#include <android-base/strings.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080027#include <utils/Errors.h>
Yifan Hong07292852019-08-21 15:40:30 -070028#include <vintf/KernelConfigParser.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080029#include <vintf/VintfObject.h>
Yifan Hong07292852019-08-21 15:40:30 -070030#include <vintf/parse_string.h>
Yifan Honga72bde72017-09-28 13:36:48 -070031#include <vintf/parse_xml.h>
32#include "utils.h"
33
34namespace android {
35namespace vintf {
Yifan Hong69c1b112018-02-27 17:06:00 -080036namespace details {
Yifan Honga72bde72017-09-28 13:36:48 -070037
Yifan Hong69c1b112018-02-27 17:06:00 -080038// fake sysprops
39using Properties = std::map<std::string, std::string>;
40
Yifan Hong07292852019-08-21 15:40:30 -070041using Dirmap = std::map<std::string, std::string>;
42
Yifan Hong69c1b112018-02-27 17:06:00 -080043enum Option : int {
44 DUMP_FILE_LIST = 1,
45 ROOTDIR,
46 HELP,
47 PROPERTY,
48 CHECK_COMPAT,
Yifan Hong07292852019-08-21 15:40:30 -070049 DIR_MAP,
50 KERNEL,
Yifan Hong69c1b112018-02-27 17:06:00 -080051};
52// command line arguments
53using Args = std::multimap<Option, std::string>;
54
Yifan Hong07292852019-08-21 15:40:30 -070055class HostFileSystem : public details::FileSystemImpl {
Yifan Hong69c1b112018-02-27 17:06:00 -080056 public:
Yifan Hong07292852019-08-21 15:40:30 -070057 HostFileSystem(const Dirmap& dirmap) : mDirMap(dirmap) {}
Yifan Hong10d86222018-04-06 15:41:05 -070058 status_t fetch(const std::string& path, std::string* fetched,
59 std::string* error) const override {
Yifan Hong07292852019-08-21 15:40:30 -070060 auto resolved = resolve(path);
61 if (resolved.empty()) {
62 std::cerr << "Error: Cannot resolve path " << path;
63 return UNKNOWN_ERROR;
64 }
65 status_t status = details::FileSystemImpl::fetch(resolved, fetched, error);
66 std::cerr << "Debug: Fetch '" << resolved << "': " << toString(status) << std::endl;
Yifan Hong10d86222018-04-06 15:41:05 -070067 return status;
Yifan Hong69c1b112018-02-27 17:06:00 -080068 }
Yifan Hong10d86222018-04-06 15:41:05 -070069 status_t listFiles(const std::string& path, std::vector<std::string>* out,
70 std::string* error) const override {
Yifan Hong07292852019-08-21 15:40:30 -070071 auto resolved = resolve(path);
72 if (resolved.empty()) {
73 std::cerr << "Error: Cannot resolve path " << path;
74 return UNKNOWN_ERROR;
75 }
76 status_t status = details::FileSystemImpl::listFiles(resolved, out, error);
77 std::cerr << "Debug: List '" << resolved << "': " << toString(status) << std::endl;
Yifan Hong69c1b112018-02-27 17:06:00 -080078 return status;
79 }
80
81 private:
Yifan Hong69c1b112018-02-27 17:06:00 -080082 static std::string toString(status_t status) {
83 return status == OK ? "SUCCESS" : strerror(-status);
84 }
Yifan Hong07292852019-08-21 15:40:30 -070085 std::string resolve(const std::string& path) const {
86 for (auto [prefix, mappedPath] : mDirMap) {
87 if (path == prefix) {
88 return mappedPath;
89 }
90 if (android::base::StartsWith(path, prefix + "/")) {
91 return mappedPath + "/" + path.substr(prefix.size() + 1);
92 }
93 }
94 return "";
95 }
96
97 Dirmap mDirMap;
Yifan Hong69c1b112018-02-27 17:06:00 -080098};
99
100class PresetPropertyFetcher : public PropertyFetcher {
101 public:
102 std::string getProperty(const std::string& key,
103 const std::string& defaultValue) const override {
104 auto it = mProps.find(key);
105 if (it == mProps.end()) {
106 std::cerr << "Debug: Sysprop " << key << " is missing, default to '" << defaultValue
107 << "'" << std::endl;
108 return defaultValue;
109 }
110 std::cerr << "Debug: Sysprop " << key << "=" << it->second << std::endl;
111 return it->second;
112 }
113 uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
114 uint64_t max) const override {
115 uint64_t result;
116 std::string value = getProperty(key, "");
117 if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
118 return defaultValue;
119 }
120 bool getBoolProperty(const std::string& key, bool defaultValue) const override {
121 std::string value = getProperty(key, "");
122 if (value == "1" || value == "true") {
123 return true;
124 } else if (value == "0" || value == "false") {
125 return false;
126 }
127 return defaultValue;
128 }
129 void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
130
131 private:
132 std::map<std::string, std::string> mProps;
133};
134
Yifan Hong07292852019-08-21 15:40:30 -0700135struct StaticRuntimeInfo : public RuntimeInfo {
136 KernelVersion kernelVersion;
137 std::string kernelConfigFile;
138
139 status_t fetchAllInformation(FetchFlags flags) override {
140 if (flags & RuntimeInfo::FetchFlag::CPU_VERSION) {
141 mKernel.mVersion = kernelVersion;
142 std::cerr << "Debug: fetched kernel version " << kernelVersion << std::endl;
143 }
144 if (flags & RuntimeInfo::FetchFlag::CONFIG_GZ) {
145 std::string content;
146 if (!android::base::ReadFileToString(kernelConfigFile, &content)) {
147 std::cerr << "Error: Cannot read " << kernelConfigFile << std::endl;
148 return UNKNOWN_ERROR;
149 }
150 KernelConfigParser parser;
151 auto status = parser.processAndFinish(content);
152 if (status != OK) {
153 return status;
154 }
155 mKernel.mConfigs = std::move(parser.configs());
156 std::cerr << "Debug: read kernel configs from " << kernelConfigFile << std::endl;
157 }
158 if (flags & RuntimeInfo::FetchFlag::POLICYVERS) {
159 mKernelSepolicyVersion = SIZE_MAX;
160 }
161 return OK;
162 }
163};
164
165struct StubRuntimeInfo : public RuntimeInfo {
166 status_t fetchAllInformation(FetchFlags) override { return UNKNOWN_ERROR; }
167};
168
169struct StaticRuntimeInfoFactory : public ObjectFactory<RuntimeInfo> {
170 std::shared_ptr<RuntimeInfo> info;
171 StaticRuntimeInfoFactory(std::shared_ptr<RuntimeInfo> i) : info(i) {}
172 std::shared_ptr<RuntimeInfo> make_shared() const override {
173 if (info) return info;
174 return std::make_shared<StubRuntimeInfo>();
175 }
176};
177
Yifan Hong69c1b112018-02-27 17:06:00 -0800178// helper functions
Yifan Honga72bde72017-09-28 13:36:48 -0700179template <typename T>
Yifan Hong9f78c182018-07-12 14:45:52 -0700180std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path,
181 const XmlConverter<T>& converter) {
Yifan Honga72bde72017-09-28 13:36:48 -0700182 std::string xml;
Yifan Hong60217032018-01-08 16:19:42 -0800183 std::string error;
Yifan Hong9f78c182018-07-12 14:45:52 -0700184 status_t err = fileSystem->fetch(path, &xml, &error);
Yifan Honga72bde72017-09-28 13:36:48 -0700185 if (err != OK) {
Yifan Hong60217032018-01-08 16:19:42 -0800186 std::cerr << "Error: Cannot read '" << path << "' (" << strerror(-err) << "): " << error
187 << std::endl;
Yifan Honga72bde72017-09-28 13:36:48 -0700188 return nullptr;
189 }
190 auto ret = std::make_unique<T>();
Yifan Hong94757062018-02-09 16:36:31 -0800191 if (!converter(ret.get(), xml, &error)) {
192 std::cerr << "Error: Cannot parse '" << path << "': " << error << std::endl;
Yifan Honga72bde72017-09-28 13:36:48 -0700193 return nullptr;
194 }
195 return ret;
196}
197
Yifan Hong69c1b112018-02-27 17:06:00 -0800198int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
Yifan Hong9f78c182018-07-12 14:45:52 -0700199 auto fileSystem = std::make_unique<FileSystemImpl>();
200 auto manifest = readObject(fileSystem.get(), manifestPath, gHalManifestConverter);
201 auto matrix = readObject(fileSystem.get(), matrixPath, gCompatibilityMatrixConverter);
Yifan Honga72bde72017-09-28 13:36:48 -0700202 if (manifest == nullptr || matrix == nullptr) {
203 return -1;
204 }
205
206 std::string error;
207 if (!manifest->checkCompatibility(*matrix, &error)) {
208 std::cerr << "Error: Incompatible: " << error << std::endl;
209 std::cout << "false" << std::endl;
210 return 1;
211 }
212
213 std::cout << "true" << std::endl;
214 return 0;
215}
Yifan Hong69c1b112018-02-27 17:06:00 -0800216
217Args parseArgs(int argc, char** argv) {
218 int longOptFlag;
219 int optionIndex;
220 Args ret;
221 std::vector<struct option> longopts{
222 {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
223 {"rootdir", required_argument, &longOptFlag, ROOTDIR},
224 {"help", no_argument, &longOptFlag, HELP},
225 {"property", required_argument, &longOptFlag, PROPERTY},
226 {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
Yifan Hong07292852019-08-21 15:40:30 -0700227 {"dirmap", required_argument, &longOptFlag, DIR_MAP},
228 {"kernel", required_argument, &longOptFlag, KERNEL},
Yifan Hong69c1b112018-02-27 17:06:00 -0800229 {0, 0, 0, 0}};
230 std::map<int, Option> shortopts{
231 {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
232 };
233 for (;;) {
234 int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
235 if (c == -1) {
236 break;
237 }
238 std::string argValue = optarg ? optarg : std::string{};
239 if (c == 0) {
240 ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
241 } else {
242 ret.emplace(shortopts[c], std::move(argValue));
243 }
244 }
245 if (optind < argc) {
246 // see non option
247 std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
248 return {{HELP, ""}};
249 }
250 return ret;
251}
252
253template <typename T>
Yifan Hong07292852019-08-21 15:40:30 -0700254std::map<std::string, std::string> splitArgs(const T& args, char split) {
255 std::map<std::string, std::string> ret;
Yifan Hong69c1b112018-02-27 17:06:00 -0800256 for (const auto& arg : args) {
Yifan Hong07292852019-08-21 15:40:30 -0700257 auto pos = arg.find(split);
Yifan Hong69c1b112018-02-27 17:06:00 -0800258 auto key = arg.substr(0, pos);
259 auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1);
260 ret[key] = value;
261 }
262 return ret;
263}
Yifan Hong07292852019-08-21 15:40:30 -0700264template <typename T>
265Properties getProperties(const T& args) {
266 return splitArgs(args, '=');
267}
268
269template <typename T>
270Dirmap getDirmap(const T& args) {
271 return splitArgs(args, ':');
272}
273
274template <typename T>
275std::shared_ptr<StaticRuntimeInfo> getRuntimeInfo(const T& args) {
276 auto ret = std::make_shared<StaticRuntimeInfo>();
277 if (std::distance(args.begin(), args.end()) > 1) {
278 std::cerr << "Error: Can't have multiple --kernel options" << std::endl;
279 return nullptr;
280 }
281 auto pair = android::base::Split(*args.begin(), ":");
282 if (pair.size() != 2) {
283 std::cerr << "Error: Invalid --kernel" << std::endl;
284 return nullptr;
285 }
286 if (!parse(pair[0], &ret->kernelVersion)) {
287 std::cerr << "Error: Cannot parse " << pair[0] << " as kernel version" << std::endl;
288 return nullptr;
289 }
290 ret->kernelConfigFile = std::move(pair[1]);
291 return ret;
292}
Yifan Hong69c1b112018-02-27 17:06:00 -0800293
294int usage(const char* me) {
295 std::cerr
296 << me << ": check VINTF metadata." << std::endl
297 << " Options:" << std::endl
298 << " --dump-file-list: Dump a list of directories / files on device" << std::endl
299 << " that is required to be used by --check-compat." << std::endl
300 << " -c, --check-compat: check compatibility for files under the root" << std::endl
301 << " directory specified by --root-dir." << std::endl
Yifan Hong07292852019-08-21 15:40:30 -0700302 << " --rootdir=<dir>: specify root directory for all metadata. Same as " << std::endl
303 << " --dirmap /:<dir>" << std::endl
Yifan Hong69c1b112018-02-27 17:06:00 -0800304 << " -D, --property <key>=<value>: specify sysprops." << std::endl
Yifan Hong07292852019-08-21 15:40:30 -0700305 << " --dirmap </system:/dir/to/system> [--dirmap </vendor:/dir/to/vendor>[...]]"
306 << std::endl
307 << " Map partitions to directories. Cannot be specified with --rootdir."
308 << " --kernel <x.y.z:path/to/config>" << std::endl
309 << " Use the given kernel version and config to check. If" << std::endl
310 << " unspecified, kernel requirements are skipped." << std::endl
311 << std::endl
Yifan Hong69c1b112018-02-27 17:06:00 -0800312 << " --help: show this message." << std::endl
313 << std::endl
314 << " Example:" << std::endl
315 << " # Get the list of required files." << std::endl
316 << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
317 << " # Pull from ADB, or use your own command to extract files from images"
318 << std::endl
319 << " ROOTDIR=/tmp/device/" << std::endl
320 << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
321 "pull {} $ROOTDIR{}\""
322 << std::endl
323 << " # Check compatibility." << std::endl
324 << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
325 << " --property ro.product.first_api_level=`adb shell getprop "
326 "ro.product.first_api_level` \\"
327 << std::endl
328 << " --property ro.boot.product.hardware.sku=`adb shell getprop "
329 "ro.boot.product.hardware.sku`"
330 << std::endl;
Yifan Hong62503e12019-08-26 12:47:41 -0700331 return EX_USAGE;
Yifan Hong69c1b112018-02-27 17:06:00 -0800332}
333
Yifan Hong07292852019-08-21 15:40:30 -0700334int checkAllFiles(const Dirmap& dirmap, const Properties& props,
335 std::shared_ptr<StaticRuntimeInfo> runtimeInfo, std::string* error) {
Yifan Hong9f78c182018-07-12 14:45:52 -0700336 auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
337 hostPropertyFetcher->setProperties(props);
Yifan Hong07292852019-08-21 15:40:30 -0700338
339 CheckFlags::Type flags = CheckFlags::DEFAULT;
340 if (!runtimeInfo) flags = flags.disableRuntimeInfo();
341
342 auto vintfObject =
343 VintfObject::Builder()
344 .setFileSystem(std::make_unique<HostFileSystem>(dirmap))
345 .setPropertyFetcher(std::move(hostPropertyFetcher))
346 .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
347 .build();
348 return vintfObject->checkCompatibility(error, flags);
Yifan Hong69c1b112018-02-27 17:06:00 -0800349}
350
351} // namespace details
352} // namespace vintf
353} // namespace android
354
355int main(int argc, char** argv) {
356 using namespace android::vintf;
357 using namespace android::vintf::details;
358 // legacy usage: check_vintf <manifest.xml> <matrix.xml>
359 if (argc == 3) {
360 int ret = checkCompatibilityForFiles(argv[1], argv[2]);
361 if (ret >= 0) return ret;
362 }
363
364 Args args = parseArgs(argc, argv);
365
366 if (!iterateValues(args, HELP).empty()) {
367 return usage(argv[0]);
368 }
369
370 if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
371 for (const auto& file : dumpFileList()) {
372 std::cout << file << std::endl;
373 }
374 return 0;
375 }
376
Yifan Hong69c1b112018-02-27 17:06:00 -0800377 auto checkCompat = iterateValues(args, CHECK_COMPAT);
Yifan Hong07292852019-08-21 15:40:30 -0700378 if (checkCompat.empty()) {
379 return usage(argv[0]);
Yifan Hong69c1b112018-02-27 17:06:00 -0800380 }
381
Yifan Hong07292852019-08-21 15:40:30 -0700382 auto rootdirs = iterateValues(args, ROOTDIR);
383 if (!rootdirs.empty()) {
384 if (std::distance(rootdirs.begin(), rootdirs.end()) > 1) {
385 std::cerr << "Error: Can't have multiple --rootdir options" << std::endl;
386 return usage(argv[0]);
387 }
388 args.emplace(DIR_MAP, "/:" + *rootdirs.begin());
389 }
390
391 auto properties = getProperties(iterateValues(args, PROPERTY));
392 auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
393
394 std::shared_ptr<StaticRuntimeInfo> runtimeInfo;
395 auto kernelArgs = iterateValues(args, KERNEL);
396 if (!kernelArgs.empty()) {
397 runtimeInfo = getRuntimeInfo(kernelArgs);
398 if (runtimeInfo == nullptr) {
399 return usage(argv[0]);
400 }
401 }
402
403 std::string error;
404 if (dirmap.empty()) {
405 std::cerr << "Missing --rootdir or --dirmap option." << std::endl;
406 return usage(argv[0]);
407 }
408
409 int compat = checkAllFiles(dirmap, properties, runtimeInfo, &error);
Yifan Hong62503e12019-08-26 12:47:41 -0700410
411 if (compat == COMPATIBLE) {
412 std::cout << "COMPATIBLE" << std::endl;
413 return EX_OK;
414 }
415 if (compat == INCOMPATIBLE) {
416 std::cerr << "Error: files are incompatible: " << error << std::endl;
417 std::cout << "INCOMPATIBLE" << std::endl;
418 return EX_DATAERR;
419 }
420 std::cerr << "Error: " << strerror(-compat) << ": " << error << std::endl;
421 return EX_SOFTWARE;
Yifan Hong69c1b112018-02-27 17:06:00 -0800422}