blob: e2fdb5818ef65866e6859d2eb23e37de14b04c2d [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>
18#include <unistd.h>
Yifan Honga72bde72017-09-28 13:36:48 -070019
Yifan Hong69c1b112018-02-27 17:06:00 -080020#include <iostream>
21#include <map>
22
Yifan Hong07292852019-08-21 15:40:30 -070023#include <android-base/file.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080024#include <android-base/parseint.h>
Yifan Hong07292852019-08-21 15:40:30 -070025#include <android-base/strings.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080026#include <utils/Errors.h>
Yifan Hong07292852019-08-21 15:40:30 -070027#include <vintf/KernelConfigParser.h>
Yifan Hong69c1b112018-02-27 17:06:00 -080028#include <vintf/VintfObject.h>
Yifan Hong07292852019-08-21 15:40:30 -070029#include <vintf/parse_string.h>
Yifan Honga72bde72017-09-28 13:36:48 -070030#include <vintf/parse_xml.h>
31#include "utils.h"
32
33namespace android {
34namespace vintf {
Yifan Hong69c1b112018-02-27 17:06:00 -080035namespace details {
Yifan Honga72bde72017-09-28 13:36:48 -070036
Yifan Hong69c1b112018-02-27 17:06:00 -080037// fake sysprops
38using Properties = std::map<std::string, std::string>;
39
Yifan Hong07292852019-08-21 15:40:30 -070040using Dirmap = std::map<std::string, std::string>;
41
Yifan Hong69c1b112018-02-27 17:06:00 -080042enum Option : int {
43 DUMP_FILE_LIST = 1,
44 ROOTDIR,
45 HELP,
46 PROPERTY,
47 CHECK_COMPAT,
Yifan Hong07292852019-08-21 15:40:30 -070048 DIR_MAP,
49 KERNEL,
Yifan Hong69c1b112018-02-27 17:06:00 -080050};
51// command line arguments
52using Args = std::multimap<Option, std::string>;
53
Yifan Hong07292852019-08-21 15:40:30 -070054class HostFileSystem : public details::FileSystemImpl {
Yifan Hong69c1b112018-02-27 17:06:00 -080055 public:
Yifan Hong07292852019-08-21 15:40:30 -070056 HostFileSystem(const Dirmap& dirmap) : mDirMap(dirmap) {}
Yifan Hong10d86222018-04-06 15:41:05 -070057 status_t fetch(const std::string& path, std::string* fetched,
58 std::string* error) const override {
Yifan Hong07292852019-08-21 15:40:30 -070059 auto resolved = resolve(path);
60 if (resolved.empty()) {
61 std::cerr << "Error: Cannot resolve path " << path;
62 return UNKNOWN_ERROR;
63 }
64 status_t status = details::FileSystemImpl::fetch(resolved, fetched, error);
65 std::cerr << "Debug: Fetch '" << resolved << "': " << toString(status) << std::endl;
Yifan Hong10d86222018-04-06 15:41:05 -070066 return status;
Yifan Hong69c1b112018-02-27 17:06:00 -080067 }
Yifan Hong10d86222018-04-06 15:41:05 -070068 status_t listFiles(const std::string& path, std::vector<std::string>* out,
69 std::string* error) const override {
Yifan Hong07292852019-08-21 15:40:30 -070070 auto resolved = resolve(path);
71 if (resolved.empty()) {
72 std::cerr << "Error: Cannot resolve path " << path;
73 return UNKNOWN_ERROR;
74 }
75 status_t status = details::FileSystemImpl::listFiles(resolved, out, error);
76 std::cerr << "Debug: List '" << resolved << "': " << toString(status) << std::endl;
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 Hong07292852019-08-21 15:40:30 -070084 std::string resolve(const std::string& path) const {
85 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 }
93 return "";
94 }
95
96 Dirmap mDirMap;
Yifan Hong69c1b112018-02-27 17:06:00 -080097};
98
99class PresetPropertyFetcher : public PropertyFetcher {
100 public:
101 std::string getProperty(const std::string& key,
102 const std::string& defaultValue) const override {
103 auto it = mProps.find(key);
104 if (it == mProps.end()) {
105 std::cerr << "Debug: Sysprop " << key << " is missing, default to '" << defaultValue
106 << "'" << std::endl;
107 return defaultValue;
108 }
109 std::cerr << "Debug: Sysprop " << key << "=" << it->second << std::endl;
110 return it->second;
111 }
112 uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
113 uint64_t max) const override {
114 uint64_t result;
115 std::string value = getProperty(key, "");
116 if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
117 return defaultValue;
118 }
119 bool getBoolProperty(const std::string& key, bool defaultValue) const override {
120 std::string value = getProperty(key, "");
121 if (value == "1" || value == "true") {
122 return true;
123 } else if (value == "0" || value == "false") {
124 return false;
125 }
126 return defaultValue;
127 }
128 void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
129
130 private:
131 std::map<std::string, std::string> mProps;
132};
133
Yifan Hong07292852019-08-21 15:40:30 -0700134struct StaticRuntimeInfo : public RuntimeInfo {
135 KernelVersion kernelVersion;
136 std::string kernelConfigFile;
137
138 status_t fetchAllInformation(FetchFlags flags) override {
139 if (flags & RuntimeInfo::FetchFlag::CPU_VERSION) {
140 mKernel.mVersion = kernelVersion;
141 std::cerr << "Debug: fetched kernel version " << kernelVersion << std::endl;
142 }
143 if (flags & RuntimeInfo::FetchFlag::CONFIG_GZ) {
144 std::string content;
145 if (!android::base::ReadFileToString(kernelConfigFile, &content)) {
146 std::cerr << "Error: Cannot read " << kernelConfigFile << std::endl;
147 return UNKNOWN_ERROR;
148 }
149 KernelConfigParser parser;
150 auto status = parser.processAndFinish(content);
151 if (status != OK) {
152 return status;
153 }
154 mKernel.mConfigs = std::move(parser.configs());
155 std::cerr << "Debug: read kernel configs from " << kernelConfigFile << std::endl;
156 }
157 if (flags & RuntimeInfo::FetchFlag::POLICYVERS) {
158 mKernelSepolicyVersion = SIZE_MAX;
159 }
160 return OK;
161 }
162};
163
164struct StubRuntimeInfo : public RuntimeInfo {
165 status_t fetchAllInformation(FetchFlags) override { return UNKNOWN_ERROR; }
166};
167
168struct StaticRuntimeInfoFactory : public ObjectFactory<RuntimeInfo> {
169 std::shared_ptr<RuntimeInfo> info;
170 StaticRuntimeInfoFactory(std::shared_ptr<RuntimeInfo> i) : info(i) {}
171 std::shared_ptr<RuntimeInfo> make_shared() const override {
172 if (info) return info;
173 return std::make_shared<StubRuntimeInfo>();
174 }
175};
176
Yifan Hong69c1b112018-02-27 17:06:00 -0800177// helper functions
Yifan Honga72bde72017-09-28 13:36:48 -0700178template <typename T>
Yifan Hong9f78c182018-07-12 14:45:52 -0700179std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path,
180 const XmlConverter<T>& converter) {
Yifan Honga72bde72017-09-28 13:36:48 -0700181 std::string xml;
Yifan Hong60217032018-01-08 16:19:42 -0800182 std::string error;
Yifan Hong9f78c182018-07-12 14:45:52 -0700183 status_t err = fileSystem->fetch(path, &xml, &error);
Yifan Honga72bde72017-09-28 13:36:48 -0700184 if (err != OK) {
Yifan Hong60217032018-01-08 16:19:42 -0800185 std::cerr << "Error: Cannot read '" << path << "' (" << strerror(-err) << "): " << error
186 << std::endl;
Yifan Honga72bde72017-09-28 13:36:48 -0700187 return nullptr;
188 }
189 auto ret = std::make_unique<T>();
Yifan Hong94757062018-02-09 16:36:31 -0800190 if (!converter(ret.get(), xml, &error)) {
191 std::cerr << "Error: Cannot parse '" << path << "': " << error << std::endl;
Yifan Honga72bde72017-09-28 13:36:48 -0700192 return nullptr;
193 }
194 return ret;
195}
196
Yifan Hong69c1b112018-02-27 17:06:00 -0800197int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
Yifan Hong9f78c182018-07-12 14:45:52 -0700198 auto fileSystem = std::make_unique<FileSystemImpl>();
199 auto manifest = readObject(fileSystem.get(), manifestPath, gHalManifestConverter);
200 auto matrix = readObject(fileSystem.get(), matrixPath, gCompatibilityMatrixConverter);
Yifan Honga72bde72017-09-28 13:36:48 -0700201 if (manifest == nullptr || matrix == nullptr) {
202 return -1;
203 }
204
205 std::string error;
206 if (!manifest->checkCompatibility(*matrix, &error)) {
207 std::cerr << "Error: Incompatible: " << error << std::endl;
208 std::cout << "false" << std::endl;
209 return 1;
210 }
211
212 std::cout << "true" << std::endl;
213 return 0;
214}
Yifan Hong69c1b112018-02-27 17:06:00 -0800215
216Args parseArgs(int argc, char** argv) {
217 int longOptFlag;
218 int optionIndex;
219 Args ret;
220 std::vector<struct option> longopts{
221 {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
222 {"rootdir", required_argument, &longOptFlag, ROOTDIR},
223 {"help", no_argument, &longOptFlag, HELP},
224 {"property", required_argument, &longOptFlag, PROPERTY},
225 {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
Yifan Hong07292852019-08-21 15:40:30 -0700226 {"dirmap", required_argument, &longOptFlag, DIR_MAP},
227 {"kernel", required_argument, &longOptFlag, KERNEL},
Yifan Hong69c1b112018-02-27 17:06:00 -0800228 {0, 0, 0, 0}};
229 std::map<int, Option> shortopts{
230 {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
231 };
232 for (;;) {
233 int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
234 if (c == -1) {
235 break;
236 }
237 std::string argValue = optarg ? optarg : std::string{};
238 if (c == 0) {
239 ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
240 } else {
241 ret.emplace(shortopts[c], std::move(argValue));
242 }
243 }
244 if (optind < argc) {
245 // see non option
246 std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl;
247 return {{HELP, ""}};
248 }
249 return ret;
250}
251
252template <typename T>
Yifan Hong07292852019-08-21 15:40:30 -0700253std::map<std::string, std::string> splitArgs(const T& args, char split) {
254 std::map<std::string, std::string> ret;
Yifan Hong69c1b112018-02-27 17:06:00 -0800255 for (const auto& arg : args) {
Yifan Hong07292852019-08-21 15:40:30 -0700256 auto pos = arg.find(split);
Yifan Hong69c1b112018-02-27 17:06:00 -0800257 auto key = arg.substr(0, pos);
258 auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1);
259 ret[key] = value;
260 }
261 return ret;
262}
Yifan Hong07292852019-08-21 15:40:30 -0700263template <typename T>
264Properties getProperties(const T& args) {
265 return splitArgs(args, '=');
266}
267
268template <typename T>
269Dirmap getDirmap(const T& args) {
270 return splitArgs(args, ':');
271}
272
273template <typename T>
274std::shared_ptr<StaticRuntimeInfo> getRuntimeInfo(const T& args) {
275 auto ret = std::make_shared<StaticRuntimeInfo>();
276 if (std::distance(args.begin(), args.end()) > 1) {
277 std::cerr << "Error: Can't have multiple --kernel options" << std::endl;
278 return nullptr;
279 }
280 auto pair = android::base::Split(*args.begin(), ":");
281 if (pair.size() != 2) {
282 std::cerr << "Error: Invalid --kernel" << std::endl;
283 return nullptr;
284 }
285 if (!parse(pair[0], &ret->kernelVersion)) {
286 std::cerr << "Error: Cannot parse " << pair[0] << " as kernel version" << std::endl;
287 return nullptr;
288 }
289 ret->kernelConfigFile = std::move(pair[1]);
290 return ret;
291}
Yifan Hong69c1b112018-02-27 17:06:00 -0800292
293int usage(const char* me) {
294 std::cerr
295 << me << ": check VINTF metadata." << std::endl
296 << " Options:" << std::endl
297 << " --dump-file-list: Dump a list of directories / files on device" << std::endl
298 << " that is required to be used by --check-compat." << std::endl
299 << " -c, --check-compat: check compatibility for files under the root" << std::endl
300 << " directory specified by --root-dir." << std::endl
Yifan Hong07292852019-08-21 15:40:30 -0700301 << " --rootdir=<dir>: specify root directory for all metadata. Same as " << std::endl
302 << " --dirmap /:<dir>" << std::endl
Yifan Hong69c1b112018-02-27 17:06:00 -0800303 << " -D, --property <key>=<value>: specify sysprops." << std::endl
Yifan Hong07292852019-08-21 15:40:30 -0700304 << " --dirmap </system:/dir/to/system> [--dirmap </vendor:/dir/to/vendor>[...]]"
305 << std::endl
306 << " Map partitions to directories. Cannot be specified with --rootdir."
307 << " --kernel <x.y.z:path/to/config>" << std::endl
308 << " Use the given kernel version and config to check. If" << std::endl
309 << " unspecified, kernel requirements are skipped." << std::endl
310 << std::endl
Yifan Hong69c1b112018-02-27 17:06:00 -0800311 << " --help: show this message." << std::endl
312 << std::endl
313 << " Example:" << std::endl
314 << " # Get the list of required files." << std::endl
315 << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
316 << " # Pull from ADB, or use your own command to extract files from images"
317 << std::endl
318 << " ROOTDIR=/tmp/device/" << std::endl
319 << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
320 "pull {} $ROOTDIR{}\""
321 << std::endl
322 << " # Check compatibility." << std::endl
323 << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
324 << " --property ro.product.first_api_level=`adb shell getprop "
325 "ro.product.first_api_level` \\"
326 << std::endl
327 << " --property ro.boot.product.hardware.sku=`adb shell getprop "
328 "ro.boot.product.hardware.sku`"
329 << std::endl;
330 return 1;
331}
332
Yifan Hong07292852019-08-21 15:40:30 -0700333int checkAllFiles(const Dirmap& dirmap, const Properties& props,
334 std::shared_ptr<StaticRuntimeInfo> runtimeInfo, std::string* error) {
Yifan Hong9f78c182018-07-12 14:45:52 -0700335 auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
336 hostPropertyFetcher->setProperties(props);
Yifan Hong07292852019-08-21 15:40:30 -0700337
338 CheckFlags::Type flags = CheckFlags::DEFAULT;
339 if (!runtimeInfo) flags = flags.disableRuntimeInfo();
340
341 auto vintfObject =
342 VintfObject::Builder()
343 .setFileSystem(std::make_unique<HostFileSystem>(dirmap))
344 .setPropertyFetcher(std::move(hostPropertyFetcher))
345 .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
346 .build();
347 return vintfObject->checkCompatibility(error, flags);
Yifan Hong69c1b112018-02-27 17:06:00 -0800348}
349
350} // namespace details
351} // namespace vintf
352} // namespace android
353
354int main(int argc, char** argv) {
355 using namespace android::vintf;
356 using namespace android::vintf::details;
357 // legacy usage: check_vintf <manifest.xml> <matrix.xml>
358 if (argc == 3) {
359 int ret = checkCompatibilityForFiles(argv[1], argv[2]);
360 if (ret >= 0) return ret;
361 }
362
363 Args args = parseArgs(argc, argv);
364
365 if (!iterateValues(args, HELP).empty()) {
366 return usage(argv[0]);
367 }
368
369 if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
370 for (const auto& file : dumpFileList()) {
371 std::cout << file << std::endl;
372 }
373 return 0;
374 }
375
Yifan Hong69c1b112018-02-27 17:06:00 -0800376 auto checkCompat = iterateValues(args, CHECK_COMPAT);
Yifan Hong07292852019-08-21 15:40:30 -0700377 if (checkCompat.empty()) {
378 return usage(argv[0]);
Yifan Hong69c1b112018-02-27 17:06:00 -0800379 }
380
Yifan Hong07292852019-08-21 15:40:30 -0700381 auto rootdirs = iterateValues(args, ROOTDIR);
382 if (!rootdirs.empty()) {
383 if (std::distance(rootdirs.begin(), rootdirs.end()) > 1) {
384 std::cerr << "Error: Can't have multiple --rootdir options" << std::endl;
385 return usage(argv[0]);
386 }
387 args.emplace(DIR_MAP, "/:" + *rootdirs.begin());
388 }
389
390 auto properties = getProperties(iterateValues(args, PROPERTY));
391 auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
392
393 std::shared_ptr<StaticRuntimeInfo> runtimeInfo;
394 auto kernelArgs = iterateValues(args, KERNEL);
395 if (!kernelArgs.empty()) {
396 runtimeInfo = getRuntimeInfo(kernelArgs);
397 if (runtimeInfo == nullptr) {
398 return usage(argv[0]);
399 }
400 }
401
402 std::string error;
403 if (dirmap.empty()) {
404 std::cerr << "Missing --rootdir or --dirmap option." << std::endl;
405 return usage(argv[0]);
406 }
407
408 int compat = checkAllFiles(dirmap, properties, runtimeInfo, &error);
409 std::cerr << "Debug: "
410 << (compat == COMPATIBLE
411 ? "files are compatible"
412 : compat == INCOMPATIBLE ? "files are incompatible"
413 : ("Encountered an error: " + error))
414 << std::endl;
415 return compat;
Yifan Hong69c1b112018-02-27 17:06:00 -0800416}