blob: ae57807a91ce4bfb50220c679b45b201f8f09ccf [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 Hong6aef2fc2020-01-09 14:41:11 -080025#include <android-base/logging.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080026#include <android-base/parseint.h>
Yifan Hong07292852019-08-21 15:40:30 -070027#include <android-base/strings.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080028#include <utils/Errors.h>
Yifan Hong07292852019-08-21 15:40:30 -070029#include <vintf/KernelConfigParser.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080030#include <vintf/VintfObject.h>
Yifan Hong07292852019-08-21 15:40:30 -070031#include <vintf/parse_string.h>
Yifan Honga72bde72017-09-28 13:36:48 -070032#include <vintf/parse_xml.h>
33#include "utils.h"
34
35namespace android {
36namespace vintf {
Yifan Hong69c1b112018-02-27 17:06:00 -080037namespace details {
Yifan Honga72bde72017-09-28 13:36:48 -070038
Yifan Hong69c1b112018-02-27 17:06:00 -080039// fake sysprops
40using Properties = std::map<std::string, std::string>;
41
Yifan Hong07292852019-08-21 15:40:30 -070042using Dirmap = std::map<std::string, std::string>;
43
Yifan Hong69c1b112018-02-27 17:06:00 -080044enum Option : int {
45 DUMP_FILE_LIST = 1,
46 ROOTDIR,
47 HELP,
48 PROPERTY,
49 CHECK_COMPAT,
Yifan Hong07292852019-08-21 15:40:30 -070050 DIR_MAP,
51 KERNEL,
Yifan Hong69c1b112018-02-27 17:06:00 -080052};
53// command line arguments
54using Args = std::multimap<Option, std::string>;
55
Yifan Hong07292852019-08-21 15:40:30 -070056class HostFileSystem : public details::FileSystemImpl {
Yifan Hong69c1b112018-02-27 17:06:00 -080057 public:
Yifan Hong07292852019-08-21 15:40:30 -070058 HostFileSystem(const Dirmap& dirmap) : mDirMap(dirmap) {}
Yifan Hong10d86222018-04-06 15:41:05 -070059 status_t fetch(const std::string& path, std::string* fetched,
60 std::string* error) const override {
Yifan Hong6aef2fc2020-01-09 14:41:11 -080061 auto resolved = resolve(path, error);
Yifan Hong07292852019-08-21 15:40:30 -070062 if (resolved.empty()) {
Yifan Hong07292852019-08-21 15:40:30 -070063 return UNKNOWN_ERROR;
64 }
65 status_t status = details::FileSystemImpl::fetch(resolved, fetched, error);
Yifan Hong6aef2fc2020-01-09 14:41:11 -080066 LOG(INFO) << "Fetch '" << resolved << "': " << toString(status);
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 Hong6aef2fc2020-01-09 14:41:11 -080071 auto resolved = resolve(path, error);
Yifan Hong07292852019-08-21 15:40:30 -070072 if (resolved.empty()) {
Yifan Hong07292852019-08-21 15:40:30 -070073 return UNKNOWN_ERROR;
74 }
75 status_t status = details::FileSystemImpl::listFiles(resolved, out, error);
Yifan Hong6aef2fc2020-01-09 14:41:11 -080076 LOG(INFO) << "List '" << resolved << "': " << toString(status);
Yifan Hong69c1b112018-02-27 17:06:00 -080077 return status;
78 }
79
80 private:
Yifan Hong69c1b112018-02-27 17:06:00 -080081 static std::string toString(status_t status) {
82 return status == OK ? "SUCCESS" : strerror(-status);
83 }
Yifan Hong6aef2fc2020-01-09 14:41:11 -080084 std::string resolve(const std::string& path, std::string* error) const {
Yifan Hong07292852019-08-21 15:40:30 -070085 for (auto [prefix, mappedPath] : mDirMap) {
86 if (path == prefix) {
87 return mappedPath;
88 }
89 if (android::base::StartsWith(path, prefix + "/")) {
90 return mappedPath + "/" + path.substr(prefix.size() + 1);
91 }
92 }
Yifan Hong6aef2fc2020-01-09 14:41:11 -080093 if (error) {
94 *error = "Cannot resolve path " + path;
95 } else {
96 LOG(ERROR) << "Cannot resolve path " << path;
97 }
Yifan Hong07292852019-08-21 15:40:30 -070098 return "";
99 }
100
101 Dirmap mDirMap;
Yifan Hong69c1b112018-02-27 17:06:00 -0800102};
103
104class PresetPropertyFetcher : public PropertyFetcher {
105 public:
106 std::string getProperty(const std::string& key,
107 const std::string& defaultValue) const override {
108 auto it = mProps.find(key);
109 if (it == mProps.end()) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800110 LOG(INFO) << "Sysprop " << key << " is missing, default to '" << defaultValue << "'";
Yifan Hong69c1b112018-02-27 17:06:00 -0800111 return defaultValue;
112 }
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800113 LOG(INFO) << "Sysprop " << key << "=" << it->second;
Yifan Hong69c1b112018-02-27 17:06:00 -0800114 return it->second;
115 }
116 uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
117 uint64_t max) const override {
118 uint64_t result;
119 std::string value = getProperty(key, "");
120 if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
121 return defaultValue;
122 }
123 bool getBoolProperty(const std::string& key, bool defaultValue) const override {
124 std::string value = getProperty(key, "");
125 if (value == "1" || value == "true") {
126 return true;
127 } else if (value == "0" || value == "false") {
128 return false;
129 }
130 return defaultValue;
131 }
132 void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
133
134 private:
135 std::map<std::string, std::string> mProps;
136};
137
Yifan Hong07292852019-08-21 15:40:30 -0700138struct StaticRuntimeInfo : public RuntimeInfo {
139 KernelVersion kernelVersion;
140 std::string kernelConfigFile;
141
142 status_t fetchAllInformation(FetchFlags flags) override {
143 if (flags & RuntimeInfo::FetchFlag::CPU_VERSION) {
144 mKernel.mVersion = kernelVersion;
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800145 LOG(INFO) << "fetched kernel version " << kernelVersion;
Yifan Hong07292852019-08-21 15:40:30 -0700146 }
147 if (flags & RuntimeInfo::FetchFlag::CONFIG_GZ) {
148 std::string content;
149 if (!android::base::ReadFileToString(kernelConfigFile, &content)) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800150 LOG(ERROR) << "Cannot read " << kernelConfigFile;
Yifan Hong07292852019-08-21 15:40:30 -0700151 return UNKNOWN_ERROR;
152 }
153 KernelConfigParser parser;
154 auto status = parser.processAndFinish(content);
155 if (status != OK) {
156 return status;
157 }
158 mKernel.mConfigs = std::move(parser.configs());
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800159 LOG(INFO) << "read kernel configs from " << kernelConfigFile;
Yifan Hong07292852019-08-21 15:40:30 -0700160 }
161 if (flags & RuntimeInfo::FetchFlag::POLICYVERS) {
162 mKernelSepolicyVersion = SIZE_MAX;
163 }
164 return OK;
165 }
166};
167
168struct StubRuntimeInfo : public RuntimeInfo {
169 status_t fetchAllInformation(FetchFlags) override { return UNKNOWN_ERROR; }
170};
171
172struct StaticRuntimeInfoFactory : public ObjectFactory<RuntimeInfo> {
173 std::shared_ptr<RuntimeInfo> info;
174 StaticRuntimeInfoFactory(std::shared_ptr<RuntimeInfo> i) : info(i) {}
175 std::shared_ptr<RuntimeInfo> make_shared() const override {
176 if (info) return info;
177 return std::make_shared<StubRuntimeInfo>();
178 }
179};
180
Yifan Hong69c1b112018-02-27 17:06:00 -0800181// helper functions
Yifan Honga72bde72017-09-28 13:36:48 -0700182template <typename T>
Yifan Hong9f78c182018-07-12 14:45:52 -0700183std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path,
184 const XmlConverter<T>& converter) {
Yifan Honga72bde72017-09-28 13:36:48 -0700185 std::string xml;
Yifan Hong60217032018-01-08 16:19:42 -0800186 std::string error;
Yifan Hong9f78c182018-07-12 14:45:52 -0700187 status_t err = fileSystem->fetch(path, &xml, &error);
Yifan Honga72bde72017-09-28 13:36:48 -0700188 if (err != OK) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800189 LOG(ERROR) << "Cannot read '" << path << "' (" << strerror(-err) << "): " << error;
Yifan Honga72bde72017-09-28 13:36:48 -0700190 return nullptr;
191 }
192 auto ret = std::make_unique<T>();
Yifan Hong94757062018-02-09 16:36:31 -0800193 if (!converter(ret.get(), xml, &error)) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800194 LOG(ERROR) << "Cannot parse '" << path << "': " << error;
Yifan Honga72bde72017-09-28 13:36:48 -0700195 return nullptr;
196 }
197 return ret;
198}
199
Yifan Hong69c1b112018-02-27 17:06:00 -0800200int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
Yifan Hong9f78c182018-07-12 14:45:52 -0700201 auto fileSystem = std::make_unique<FileSystemImpl>();
202 auto manifest = readObject(fileSystem.get(), manifestPath, gHalManifestConverter);
203 auto matrix = readObject(fileSystem.get(), matrixPath, gCompatibilityMatrixConverter);
Yifan Honga72bde72017-09-28 13:36:48 -0700204 if (manifest == nullptr || matrix == nullptr) {
205 return -1;
206 }
207
208 std::string error;
209 if (!manifest->checkCompatibility(*matrix, &error)) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800210 LOG(ERROR) << "Incompatible: " << error;
Yifan Honga72bde72017-09-28 13:36:48 -0700211 std::cout << "false" << std::endl;
212 return 1;
213 }
214
215 std::cout << "true" << std::endl;
216 return 0;
217}
Yifan Hong69c1b112018-02-27 17:06:00 -0800218
219Args parseArgs(int argc, char** argv) {
220 int longOptFlag;
221 int optionIndex;
222 Args ret;
223 std::vector<struct option> longopts{
224 {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
225 {"rootdir", required_argument, &longOptFlag, ROOTDIR},
226 {"help", no_argument, &longOptFlag, HELP},
227 {"property", required_argument, &longOptFlag, PROPERTY},
228 {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
Yifan Hong07292852019-08-21 15:40:30 -0700229 {"dirmap", required_argument, &longOptFlag, DIR_MAP},
230 {"kernel", required_argument, &longOptFlag, KERNEL},
Yifan Hong69c1b112018-02-27 17:06:00 -0800231 {0, 0, 0, 0}};
232 std::map<int, Option> shortopts{
233 {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
234 };
235 for (;;) {
236 int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
237 if (c == -1) {
238 break;
239 }
240 std::string argValue = optarg ? optarg : std::string{};
241 if (c == 0) {
242 ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
243 } else {
244 ret.emplace(shortopts[c], std::move(argValue));
245 }
246 }
247 if (optind < argc) {
248 // see non option
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800249 LOG(ERROR) << "unrecognized option `" << argv[optind] << "'";
Yifan Hong69c1b112018-02-27 17:06:00 -0800250 return {{HELP, ""}};
251 }
252 return ret;
253}
254
255template <typename T>
Yifan Hong07292852019-08-21 15:40:30 -0700256std::map<std::string, std::string> splitArgs(const T& args, char split) {
257 std::map<std::string, std::string> ret;
Yifan Hong69c1b112018-02-27 17:06:00 -0800258 for (const auto& arg : args) {
Yifan Hong07292852019-08-21 15:40:30 -0700259 auto pos = arg.find(split);
Yifan Hong69c1b112018-02-27 17:06:00 -0800260 auto key = arg.substr(0, pos);
261 auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1);
262 ret[key] = value;
263 }
264 return ret;
265}
Yifan Hong07292852019-08-21 15:40:30 -0700266template <typename T>
267Properties getProperties(const T& args) {
268 return splitArgs(args, '=');
269}
270
271template <typename T>
272Dirmap getDirmap(const T& args) {
273 return splitArgs(args, ':');
274}
275
276template <typename T>
277std::shared_ptr<StaticRuntimeInfo> getRuntimeInfo(const T& args) {
278 auto ret = std::make_shared<StaticRuntimeInfo>();
279 if (std::distance(args.begin(), args.end()) > 1) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800280 LOG(ERROR) << "Can't have multiple --kernel options";
Yifan Hong07292852019-08-21 15:40:30 -0700281 return nullptr;
282 }
283 auto pair = android::base::Split(*args.begin(), ":");
284 if (pair.size() != 2) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800285 LOG(ERROR) << "Invalid --kernel";
Yifan Hong07292852019-08-21 15:40:30 -0700286 return nullptr;
287 }
288 if (!parse(pair[0], &ret->kernelVersion)) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800289 LOG(ERROR) << "Cannot parse " << pair[0] << " as kernel version";
Yifan Hong07292852019-08-21 15:40:30 -0700290 return nullptr;
291 }
292 ret->kernelConfigFile = std::move(pair[1]);
293 return ret;
294}
Yifan Hong69c1b112018-02-27 17:06:00 -0800295
296int usage(const char* me) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800297 LOG(ERROR)
Yifan Hong69c1b112018-02-27 17:06:00 -0800298 << me << ": check VINTF metadata." << std::endl
299 << " Options:" << std::endl
300 << " --dump-file-list: Dump a list of directories / files on device" << std::endl
301 << " that is required to be used by --check-compat." << std::endl
302 << " -c, --check-compat: check compatibility for files under the root" << std::endl
303 << " directory specified by --root-dir." << std::endl
Yifan Hong07292852019-08-21 15:40:30 -0700304 << " --rootdir=<dir>: specify root directory for all metadata. Same as " << std::endl
305 << " --dirmap /:<dir>" << std::endl
Yifan Hong69c1b112018-02-27 17:06:00 -0800306 << " -D, --property <key>=<value>: specify sysprops." << std::endl
Yifan Hong07292852019-08-21 15:40:30 -0700307 << " --dirmap </system:/dir/to/system> [--dirmap </vendor:/dir/to/vendor>[...]]"
308 << std::endl
309 << " Map partitions to directories. Cannot be specified with --rootdir."
310 << " --kernel <x.y.z:path/to/config>" << std::endl
311 << " Use the given kernel version and config to check. If" << std::endl
312 << " unspecified, kernel requirements are skipped." << std::endl
313 << std::endl
Yifan Hong69c1b112018-02-27 17:06:00 -0800314 << " --help: show this message." << std::endl
315 << std::endl
316 << " Example:" << std::endl
317 << " # Get the list of required files." << std::endl
318 << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
319 << " # Pull from ADB, or use your own command to extract files from images"
320 << std::endl
321 << " ROOTDIR=/tmp/device/" << std::endl
322 << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
323 "pull {} $ROOTDIR{}\""
324 << std::endl
325 << " # Check compatibility." << std::endl
326 << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
327 << " --property ro.product.first_api_level=`adb shell getprop "
328 "ro.product.first_api_level` \\"
329 << std::endl
330 << " --property ro.boot.product.hardware.sku=`adb shell getprop "
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800331 "ro.boot.product.hardware.sku`";
Yifan Hong62503e12019-08-26 12:47:41 -0700332 return EX_USAGE;
Yifan Hong69c1b112018-02-27 17:06:00 -0800333}
334
Yifan Hong07292852019-08-21 15:40:30 -0700335int checkAllFiles(const Dirmap& dirmap, const Properties& props,
336 std::shared_ptr<StaticRuntimeInfo> runtimeInfo, std::string* error) {
Yifan Hong9f78c182018-07-12 14:45:52 -0700337 auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
338 hostPropertyFetcher->setProperties(props);
Yifan Hong07292852019-08-21 15:40:30 -0700339
340 CheckFlags::Type flags = CheckFlags::DEFAULT;
341 if (!runtimeInfo) flags = flags.disableRuntimeInfo();
342
343 auto vintfObject =
344 VintfObject::Builder()
345 .setFileSystem(std::make_unique<HostFileSystem>(dirmap))
346 .setPropertyFetcher(std::move(hostPropertyFetcher))
347 .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
348 .build();
349 return vintfObject->checkCompatibility(error, flags);
Yifan Hong69c1b112018-02-27 17:06:00 -0800350}
351
352} // namespace details
353} // namespace vintf
354} // namespace android
355
356int main(int argc, char** argv) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800357 android::base::SetLogger(android::base::StderrLogger);
358
Yifan Hong69c1b112018-02-27 17:06:00 -0800359 using namespace android::vintf;
360 using namespace android::vintf::details;
361 // legacy usage: check_vintf <manifest.xml> <matrix.xml>
362 if (argc == 3) {
363 int ret = checkCompatibilityForFiles(argv[1], argv[2]);
364 if (ret >= 0) return ret;
365 }
366
367 Args args = parseArgs(argc, argv);
368
369 if (!iterateValues(args, HELP).empty()) {
370 return usage(argv[0]);
371 }
372
373 if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
374 for (const auto& file : dumpFileList()) {
375 std::cout << file << std::endl;
376 }
377 return 0;
378 }
379
Yifan Hong69c1b112018-02-27 17:06:00 -0800380 auto checkCompat = iterateValues(args, CHECK_COMPAT);
Yifan Hong07292852019-08-21 15:40:30 -0700381 if (checkCompat.empty()) {
382 return usage(argv[0]);
Yifan Hong69c1b112018-02-27 17:06:00 -0800383 }
384
Yifan Hong07292852019-08-21 15:40:30 -0700385 auto rootdirs = iterateValues(args, ROOTDIR);
386 if (!rootdirs.empty()) {
387 if (std::distance(rootdirs.begin(), rootdirs.end()) > 1) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800388 LOG(ERROR) << "Can't have multiple --rootdir options";
Yifan Hong07292852019-08-21 15:40:30 -0700389 return usage(argv[0]);
390 }
391 args.emplace(DIR_MAP, "/:" + *rootdirs.begin());
392 }
393
394 auto properties = getProperties(iterateValues(args, PROPERTY));
395 auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
396
397 std::shared_ptr<StaticRuntimeInfo> runtimeInfo;
398 auto kernelArgs = iterateValues(args, KERNEL);
399 if (!kernelArgs.empty()) {
400 runtimeInfo = getRuntimeInfo(kernelArgs);
401 if (runtimeInfo == nullptr) {
402 return usage(argv[0]);
403 }
404 }
405
406 std::string error;
407 if (dirmap.empty()) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800408 LOG(ERROR) << "Missing --rootdir or --dirmap option.";
Yifan Hong07292852019-08-21 15:40:30 -0700409 return usage(argv[0]);
410 }
411
412 int compat = checkAllFiles(dirmap, properties, runtimeInfo, &error);
Yifan Hong62503e12019-08-26 12:47:41 -0700413
414 if (compat == COMPATIBLE) {
415 std::cout << "COMPATIBLE" << std::endl;
416 return EX_OK;
417 }
418 if (compat == INCOMPATIBLE) {
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800419 LOG(ERROR) << "files are incompatible: " << error;
Yifan Hong62503e12019-08-26 12:47:41 -0700420 std::cout << "INCOMPATIBLE" << std::endl;
421 return EX_DATAERR;
422 }
Yifan Hong6aef2fc2020-01-09 14:41:11 -0800423 LOG(ERROR) << strerror(-compat) << ": " << error;
Yifan Hong62503e12019-08-26 12:47:41 -0700424 return EX_SOFTWARE;
Yifan Hong69c1b112018-02-27 17:06:00 -0800425}