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 <sysexits.h>
19 #include <unistd.h>
20
21 #include <algorithm>
22 #include <functional>
23 #include <iostream>
24 #include <map>
25 #include <optional>
26
27 #include <aidl/metadata.h>
28 #include <android-base/file.h>
29 #include <android-base/logging.h>
30 #include <android-base/parseint.h>
31 #include <android-base/result.h>
32 #include <android-base/strings.h>
33 #include <hidl/metadata.h>
34 #include <kver/kernel_release.h>
35 #include <utils/Errors.h>
36 #include <vintf/Dirmap.h>
37 #include <vintf/HostFileSystem.h>
38 #include <vintf/KernelConfigParser.h>
39 #include <vintf/VintfObject.h>
40 #include <vintf/fcm_exclude.h>
41 #include <vintf/parse_string.h>
42 #include <vintf/parse_xml.h>
43 #include "constants-private.h"
44 #include "utils.h"
45
46 using android::kver::KernelRelease;
47
48 namespace android {
49 namespace vintf {
50 namespace details {
51
52 // fake sysprops
53 using Properties = std::map<std::string, std::string>;
54
55 enum Option : int {
56 // Modes
57 HELP,
58 DUMP_FILE_LIST = 1,
59 CHECK_COMPAT,
60 CHECK_ONE,
61
62 // Options
63 ROOTDIR,
64 PROPERTY,
65 DIR_MAP,
66 KERNEL,
67 };
68 // command line arguments
69 using Args = std::multimap<Option, std::string>;
70
71 class PresetPropertyFetcher : public PropertyFetcher {
72 public:
getProperty(const std::string & key,const std::string & defaultValue) const73 std::string getProperty(const std::string& key,
74 const std::string& defaultValue) const override {
75 auto it = mProps.find(key);
76 if (it == mProps.end()) {
77 LOG(INFO) << "Sysprop " << key << " is missing, default to '" << defaultValue << "'";
78 return defaultValue;
79 }
80 LOG(INFO) << "Sysprop " << key << "=" << it->second;
81 return it->second;
82 }
getUintProperty(const std::string & key,uint64_t defaultValue,uint64_t max) const83 uint64_t getUintProperty(const std::string& key, uint64_t defaultValue,
84 uint64_t max) const override {
85 uint64_t result;
86 std::string value = getProperty(key, "");
87 if (!value.empty() && android::base::ParseUint(value, &result, max)) return result;
88 return defaultValue;
89 }
getBoolProperty(const std::string & key,bool defaultValue) const90 bool getBoolProperty(const std::string& key, bool defaultValue) const override {
91 std::string value = getProperty(key, "");
92 if (value == "1" || value == "true") {
93 return true;
94 } else if (value == "0" || value == "false") {
95 return false;
96 }
97 return defaultValue;
98 }
setProperties(const Properties & props)99 void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); }
100
101 private:
102 std::map<std::string, std::string> mProps;
103 };
104
105 struct StaticRuntimeInfo : public RuntimeInfo {
106 KernelVersion kernelVersion;
107 Level kernelLevel = Level::UNSPECIFIED;
108 std::string kernelConfigFile;
109
fetchAllInformationandroid::vintf::details::StaticRuntimeInfo110 status_t fetchAllInformation(FetchFlags flags) override {
111 if (flags & RuntimeInfo::FetchFlag::CPU_VERSION) {
112 mKernel.mVersion = kernelVersion;
113 LOG(INFO) << "fetched kernel version " << kernelVersion;
114 }
115 if (flags & RuntimeInfo::FetchFlag::KERNEL_FCM) {
116 mKernel.mLevel = kernelLevel;
117 LOG(INFO) << "fetched kernel level from RuntimeInfo '" << kernelLevel << "'";
118 }
119 if (flags & RuntimeInfo::FetchFlag::CONFIG_GZ) {
120 std::string content;
121 if (!android::base::ReadFileToString(kernelConfigFile, &content)) {
122 LOG(ERROR) << "ERROR: Cannot read " << kernelConfigFile;
123 return UNKNOWN_ERROR;
124 }
125 KernelConfigParser parser;
126 auto status = parser.processAndFinish(content);
127 if (status != OK) {
128 return status;
129 }
130 mKernel.mConfigs = std::move(parser.configs());
131 LOG(INFO) << "read kernel configs from " << kernelConfigFile;
132 }
133 if (flags & RuntimeInfo::FetchFlag::POLICYVERS) {
134 mKernelSepolicyVersion = SIZE_MAX;
135 }
136 return OK;
137 }
138
setIsMainlineandroid::vintf::details::StaticRuntimeInfo139 void setIsMainline(bool value) { mIsMainline = value; }
140 };
141
142 struct StubRuntimeInfo : public RuntimeInfo {
fetchAllInformationandroid::vintf::details::StubRuntimeInfo143 status_t fetchAllInformation(FetchFlags) override { return UNKNOWN_ERROR; }
144 };
145
146 struct StaticRuntimeInfoFactory : public ObjectFactory<RuntimeInfo> {
147 std::shared_ptr<RuntimeInfo> info;
StaticRuntimeInfoFactoryandroid::vintf::details::StaticRuntimeInfoFactory148 StaticRuntimeInfoFactory(std::shared_ptr<RuntimeInfo> i) : info(i) {}
make_sharedandroid::vintf::details::StaticRuntimeInfoFactory149 std::shared_ptr<RuntimeInfo> make_shared() const override {
150 if (info) return info;
151 return std::make_shared<StubRuntimeInfo>();
152 }
153 };
154
155 // helper functions
156 template <typename T>
readObject(FileSystem * fileSystem,const std::string & path)157 std::unique_ptr<T> readObject(FileSystem* fileSystem, const std::string& path) {
158 std::string xml;
159 std::string error;
160 status_t err = fileSystem->fetch(path, &xml, &error);
161 if (err != OK) {
162 LOG(ERROR) << "ERROR: Cannot read '" << path << "' (" << strerror(-err) << "): " << error;
163 return nullptr;
164 }
165 auto ret = std::make_unique<T>();
166 ret->setFileName(path);
167 if (!fromXml(ret.get(), xml, &error)) {
168 LOG(ERROR) << "ERROR: Cannot parse '" << path << "': " << error;
169 return nullptr;
170 }
171 return ret;
172 }
173
checkCompatibilityForFiles(const std::string & manifestPath,const std::string & matrixPath)174 int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) {
175 auto fileSystem = std::make_unique<FileSystemImpl>();
176 auto manifest = readObject<HalManifest>(fileSystem.get(), manifestPath);
177 auto matrix = readObject<CompatibilityMatrix>(fileSystem.get(), matrixPath);
178 if (manifest == nullptr || matrix == nullptr) {
179 return -1;
180 }
181
182 std::string error;
183 if (!manifest->checkCompatibility(*matrix, &error)) {
184 LOG(ERROR) << "ERROR: Incompatible: " << error;
185 std::cout << "false" << std::endl;
186 return 1;
187 }
188
189 std::cout << "true" << std::endl;
190 return 0;
191 }
192
parseArgs(int argc,char ** argv)193 Args parseArgs(int argc, char** argv) {
194 int longOptFlag;
195 int optionIndex;
196 Args ret;
197 std::vector<struct option> longopts{
198 // Modes
199 {"help", no_argument, &longOptFlag, HELP},
200 {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST},
201 {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT},
202 {"check-one", no_argument, &longOptFlag, CHECK_ONE},
203 // Options
204 {"rootdir", required_argument, &longOptFlag, ROOTDIR},
205 {"property", required_argument, &longOptFlag, PROPERTY},
206 {"dirmap", required_argument, &longOptFlag, DIR_MAP},
207 {"kernel", required_argument, &longOptFlag, KERNEL},
208 {0, 0, 0, 0}};
209 std::map<int, Option> shortopts{
210 {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT},
211 };
212 for (;;) {
213 int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex);
214 if (c == -1) {
215 break;
216 }
217 std::string argValue = optarg ? optarg : std::string{};
218 if (c == 0) {
219 ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue));
220 } else {
221 ret.emplace(shortopts[c], std::move(argValue));
222 }
223 }
224 if (optind < argc) {
225 // see non option
226 LOG(ERROR) << "ERROR: unrecognized option `" << argv[optind] << "'";
227 return {{HELP, ""}};
228 }
229 return ret;
230 }
231
232 template <typename T>
getProperties(const T & args)233 Properties getProperties(const T& args) {
234 return splitArgs(args, '=');
235 }
236
237 // Parse a kernel version or a GKI kernel release.
parseKernelVersionOrRelease(const std::string & s,StaticRuntimeInfo * ret)238 bool parseKernelVersionOrRelease(const std::string& s, StaticRuntimeInfo* ret) {
239 // 5.4.42
240 if (parse(s, &ret->kernelVersion)) {
241 ret->kernelLevel = Level::UNSPECIFIED;
242 ret->setIsMainline(false);
243 LOG(INFO) << "\"" << s << "\" is not a mainline kernel.";
244 return true;
245 }
246 LOG(INFO) << "Cannot parse \"" << s << "\" as kernel version, parsing as GKI kernel release.";
247
248 // 5.4.42-android12-0-something
249 auto kernelRelease = KernelRelease::Parse(s, true /* allow suffix */);
250 if (kernelRelease.has_value()) {
251 ret->kernelVersion = KernelVersion{kernelRelease->version(), kernelRelease->patch_level(),
252 kernelRelease->sub_level()};
253 ret->kernelLevel = RuntimeInfo::gkiAndroidReleaseToLevel(kernelRelease->android_release());
254 ret->setIsMainline(false);
255 LOG(INFO) << "\"" << s << "\" is not a mainline kernel.";
256 return true;
257 }
258 LOG(INFO) << "Cannot parse \"" << s << "\" as GKI kernel release, parsing as kernel release";
259
260 // 5.4.42-something
261 auto pos = s.find_first_not_of("0123456789.");
262 // substr handles pos == npos case
263 if (parse(s.substr(0, pos), &ret->kernelVersion)) {
264 ret->kernelLevel = Level::UNSPECIFIED;
265
266 bool isMainline = RuntimeInfo::kernelReleaseIsMainline(s);
267 ret->setIsMainline(isMainline);
268 LOG(INFO) << "\"" << s << "\" is" << (isMainline ? "" : " not") << " a mainline kernel.";
269
270 return true;
271 }
272
273 LOG(INFO) << "Cannot parse \"" << s << "\" as kernel release";
274 return false;
275 }
276
277 // Parse the first half of --kernel. |s| can either be a kernel version, a GKI kernel release,
278 // or a file that contains either of them.
parseKernelArgFirstHalf(const std::string & s,StaticRuntimeInfo * ret)279 bool parseKernelArgFirstHalf(const std::string& s, StaticRuntimeInfo* ret) {
280 if (parseKernelVersionOrRelease(s, ret)) {
281 LOG(INFO) << "Successfully parsed \"" << s << "\"";
282 return true;
283 }
284 std::string content;
285 if (!android::base::ReadFileToString(s, &content)) {
286 PLOG(INFO) << "Cannot read file " << s;
287 return false;
288 }
289 if (parseKernelVersionOrRelease(content, ret)) {
290 LOG(INFO) << "Successfully parsed content of " << s << ": " << content;
291 return true;
292 }
293 LOG(ERROR) << "ERROR: Cannot parse content of " << s << ": " << content;
294 return false;
295 }
296
297 template <typename T>
getRuntimeInfo(const T & args)298 std::shared_ptr<StaticRuntimeInfo> getRuntimeInfo(const T& args) {
299 auto ret = std::make_shared<StaticRuntimeInfo>();
300 if (std::distance(args.begin(), args.end()) > 1) {
301 LOG(ERROR) << "ERROR: Can't have multiple --kernel options";
302 return nullptr;
303 }
304 const auto& arg = *args.begin();
305 auto colonPos = arg.rfind(":");
306 if (colonPos == std::string::npos) {
307 LOG(ERROR) << "ERROR: Invalid --kernel";
308 return nullptr;
309 }
310
311 if (!parseKernelArgFirstHalf(arg.substr(0, colonPos), ret.get())) {
312 return nullptr;
313 }
314
315 ret->kernelConfigFile = arg.substr(colonPos + 1);
316 return ret;
317 }
318
usage(const char * me)319 int usage(const char* me) {
320 LOG(ERROR)
321 << me << ": check VINTF metadata." << std::endl
322 << " Modes:" << std::endl
323 << " --dump-file-list: Dump a list of directories / files on device" << std::endl
324 << " that is required to be used by --check-compat." << std::endl
325 << " -c, --check-compat: check compatibility for files under the root" << std::endl
326 << " directory specified by --root-dir." << std::endl
327 << " --check-one: check consistency of VINTF metadata for a single partition."
328 << std::endl
329 << std::endl
330 << " Options:" << std::endl
331 << " --rootdir=<dir>: specify root directory for all metadata. Same as " << std::endl
332 << " --dirmap /:<dir>" << std::endl
333 << " -D, --property <key>=<value>: specify sysprops." << std::endl
334 << " --dirmap </system:/dir/to/system> [--dirmap </vendor:/dir/to/vendor>[...]]"
335 << std::endl
336 << " Map partitions to directories. Cannot be specified with --rootdir."
337 << " --kernel <version:path/to/config>" << std::endl
338 << " Use the given kernel version and config to check. If" << std::endl
339 << " unspecified, kernel requirements are skipped." << std::endl
340 << " The first half, version, can be just x.y.z, or a file " << std::endl
341 << " containing the full kernel release string x.y.z-something." << std::endl
342 << " --help: show this message." << std::endl
343 << std::endl
344 << " Example:" << std::endl
345 << " # Get the list of required files." << std::endl
346 << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl
347 << " # Pull from ADB, or use your own command to extract files from images"
348 << std::endl
349 << " ROOTDIR=/tmp/device/" << std::endl
350 << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb "
351 "pull {} $ROOTDIR{}\""
352 << std::endl
353 << " # Check compatibility." << std::endl
354 << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl
355 << " --property ro.product.first_api_level=`adb shell getprop "
356 "ro.product.first_api_level` \\"
357 << std::endl
358 << " --property ro.boot.product.hardware.sku=`adb shell getprop "
359 "ro.boot.product.hardware.sku`";
360 return EX_USAGE;
361 }
362
363 // If |result| is already an error, don't do anything. Otherwise, set it to
364 // an error with |errorCode|. Return reference to Error object for appending
365 // additional error messages.
SetErrorCode(std::optional<android::base::Error<>> * retError,int errorCode=0)366 android::base::Error<>& SetErrorCode(std::optional<android::base::Error<>>* retError,
367 int errorCode = 0) {
368 if (!retError->has_value()) {
369 retError->emplace(errorCode);
370 } else {
371 // Use existing error code.
372 // There should already been an error message appended. Add a new line char for
373 // additional messages.
374 (**retError) << "\n";
375 }
376 return **retError;
377 }
378
379 // If |other| is an error, add it to |retError|.
380 template <typename T>
AddResult(std::optional<android::base::Error<>> * retError,const android::base::Result<T> & other,const char * additionalMessage="")381 void AddResult(std::optional<android::base::Error<>>* retError,
382 const android::base::Result<T>& other, const char* additionalMessage = "") {
383 if (other.ok()) return;
384 SetErrorCode(retError, other.error().code()) << other.error() << additionalMessage;
385 }
386
387 static constexpr const char* gCheckMissingHalsSuggestion{
388 "\n- If this is a new package, add it to the latest framework compatibility matrix."
389 "\n- If no interface should be added to the framework compatibility matrix (e.g. "
390 "types-only package), add it to the exempt list in libvintf_fcm_exclude."};
391
checkAllFiles(const Dirmap & dirmap,const Properties & props,std::shared_ptr<StaticRuntimeInfo> runtimeInfo)392 android::base::Result<void> checkAllFiles(const Dirmap& dirmap, const Properties& props,
393 std::shared_ptr<StaticRuntimeInfo> runtimeInfo) {
394 auto hostFileSystem = std::make_unique<HostFileSystem>(dirmap, UNKNOWN_ERROR);
395 auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
396 hostPropertyFetcher->setProperties(props);
397
398 CheckFlags::Type flags = CheckFlags::DEFAULT;
399 if (!runtimeInfo) flags = flags.disableRuntimeInfo();
400
401 auto vintfObject =
402 VintfObject::Builder()
403 .setFileSystem(std::move(hostFileSystem))
404 .setPropertyFetcher(std::move(hostPropertyFetcher))
405 .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(runtimeInfo))
406 .build();
407
408 std::optional<android::base::Error<>> retError = std::nullopt;
409
410 std::string compatibleError;
411 int compatibleResult = vintfObject->checkCompatibility(&compatibleError, flags);
412 if (compatibleResult == INCOMPATIBLE) {
413 SetErrorCode(&retError) << compatibleError;
414 } else if (compatibleResult != COMPATIBLE) {
415 SetErrorCode(&retError, -compatibleResult) << compatibleError;
416 }
417
418 auto hidlMetadata = HidlInterfaceMetadata::all();
419
420 std::string deprecateError;
421 int deprecateResult = vintfObject->checkDeprecation(hidlMetadata, &deprecateError);
422 if (deprecateResult == DEPRECATED) {
423 SetErrorCode(&retError) << deprecateError;
424 } else if (deprecateResult != NO_DEPRECATED_HALS) {
425 SetErrorCode(&retError, -deprecateResult) << deprecateError;
426 }
427
428 auto hasFcmExt = vintfObject->hasFrameworkCompatibilityMatrixExtensions();
429 AddResult(&retError, hasFcmExt);
430
431 auto deviceManifest = vintfObject->getDeviceHalManifest();
432 Level targetFcm = Level::UNSPECIFIED;
433 if (deviceManifest == nullptr) {
434 SetErrorCode(&retError, -NAME_NOT_FOUND) << "No device HAL manifest";
435 } else {
436 targetFcm = deviceManifest->level();
437 }
438
439 if (hasFcmExt.value_or(false) || (targetFcm != Level::UNSPECIFIED && targetFcm >= Level::R)) {
440 AddResult(&retError, vintfObject->checkUnusedHals(hidlMetadata));
441 } else {
442 LOG(INFO) << "Skip checking unused HALs.";
443 }
444
445 if (retError.has_value()) {
446 return *retError;
447 } else {
448 return {};
449 }
450 }
451
452 // Checks consistency of VINTF metadata for a single partition.
453 // For now it supports either /system or /vendor.
checkOne(const Dirmap & dirmap,const Properties & props)454 int checkOne(const Dirmap& dirmap, const Properties& props) {
455 if (dirmap.count("/system") + dirmap.count("/vendor") != 1) {
456 LOG(ERROR) << "ERROR: --check-one requires either --dirmap /system or --dirmap /vendor";
457 return EX_SOFTWARE;
458 }
459
460 auto hostFileSystem = std::make_unique<HostFileSystem>(dirmap, NAME_NOT_FOUND);
461 auto hostPropertyFetcher = std::make_unique<PresetPropertyFetcher>();
462 hostPropertyFetcher->setProperties(props);
463
464 auto vintfObject =
465 VintfObject::Builder()
466 .setFileSystem(std::move(hostFileSystem))
467 .setPropertyFetcher(std::move(hostPropertyFetcher))
468 .setRuntimeInfoFactory(std::make_unique<StaticRuntimeInfoFactory>(nullptr))
469 .build();
470
471 if (dirmap.count("/system")) {
472 LOG(INFO) << "Checking system manifest.";
473 auto manifest = vintfObject->getFrameworkHalManifest();
474 if (!manifest) {
475 LOG(ERROR) << "ERROR: Cannot fetch system manifest.";
476 return EX_SOFTWARE;
477 }
478 LOG(INFO) << "Checking system matrix.";
479 auto matrix = vintfObject->getFrameworkCompatibilityMatrix();
480 if (!matrix) {
481 LOG(ERROR) << "ERROR: Cannot fetch system matrix.";
482 return EX_SOFTWARE;
483 }
484 auto res = vintfObject->checkMissingHalsInMatrices(
485 HidlInterfaceMetadata::all(), AidlInterfaceMetadata::all(),
486 ShouldCheckMissingHidlHalsInFcm, ShouldCheckMissingAidlHalsInFcm);
487 if (!res.ok()) {
488 LOG(ERROR) << "ERROR: " << res.error() << gCheckMissingHalsSuggestion;
489 return EX_SOFTWARE;
490 }
491
492 res = vintfObject->checkMatrixHalsHasDefinition(HidlInterfaceMetadata::all(),
493 AidlInterfaceMetadata::all());
494 if (!res.ok()) {
495 LOG(ERROR) << "ERROR: " << res.error();
496 return EX_SOFTWARE;
497 }
498 return EX_OK;
499 }
500
501 if (dirmap.count("/vendor")) {
502 LOG(INFO) << "Checking vendor manifest.";
503 auto manifest = vintfObject->getDeviceHalManifest();
504 if (!manifest) {
505 LOG(ERROR) << "ERROR: Cannot fetch vendor manifest.";
506 return EX_SOFTWARE;
507 }
508 LOG(INFO) << "Checking vendor matrix.";
509 auto matrix = vintfObject->getDeviceCompatibilityMatrix();
510 if (!matrix) {
511 LOG(ERROR) << "ERROR: Cannot fetch vendor matrix.";
512 return EX_SOFTWARE;
513 }
514 return EX_OK;
515 }
516
517 __builtin_unreachable();
518 }
519
Logger(android::base::LogId,android::base::LogSeverity severity,const char *,const char *,unsigned int,const char * message)520 void Logger(android::base::LogId, android::base::LogSeverity severity, const char* /*tag*/,
521 const char* /*file*/, unsigned int /*line*/, const char* message) {
522 if (severity >= android::base::ERROR) {
523 fflush(stdout);
524 fprintf(stderr, "\033[31m%s\033[0m\n", message);
525 } else if (severity >= android::base::WARNING) {
526 fflush(stdout);
527 fprintf(stderr, "\033[33m[WARN] %s\033[0m\n", message);
528 } else {
529 fflush(stderr);
530 fprintf(stdout, "[INFO] %s\n", message);
531 }
532 }
533
534 } // namespace details
535 } // namespace vintf
536 } // namespace android
537
main(int argc,char ** argv)538 int main(int argc, char** argv) {
539 android::base::SetLogger(android::vintf::details::Logger);
540
541 using namespace android::vintf;
542 using namespace android::vintf::details;
543 // legacy usage: check_vintf <manifest.xml> <matrix.xml>
544 if (argc == 3 && *argv[1] != '-' && *argv[2] != '-') {
545 int ret = checkCompatibilityForFiles(argv[1], argv[2]);
546 if (ret >= 0) return ret;
547 }
548
549 Args args = parseArgs(argc, argv);
550
551 if (!iterateValues(args, HELP).empty()) {
552 return usage(argv[0]);
553 }
554
555 auto dirmap = getDirmap(iterateValues(args, DIR_MAP));
556 auto properties = getProperties(iterateValues(args, PROPERTY));
557 if (!iterateValues(args, DUMP_FILE_LIST).empty()) {
558 auto it = properties.find("ro.boot.product.hardware.sku");
559 const std::string sku = it == properties.end() ? "" : it->second;
560 for (const auto& file : dumpFileList(sku)) {
561 std::cout << file << std::endl;
562 }
563 return 0;
564 }
565
566 if (!iterateValues(args, CHECK_ONE).empty()) {
567 return checkOne(dirmap, properties);
568 }
569
570 auto checkCompat = iterateValues(args, CHECK_COMPAT);
571 if (checkCompat.empty()) {
572 return usage(argv[0]);
573 }
574
575 auto rootdirs = iterateValues(args, ROOTDIR);
576 if (!rootdirs.empty()) {
577 if (std::distance(rootdirs.begin(), rootdirs.end()) > 1) {
578 LOG(ERROR) << "ERROR: Can't have multiple --rootdir options";
579 return usage(argv[0]);
580 }
581 args.emplace(DIR_MAP, "/:" + *rootdirs.begin());
582 }
583
584 std::shared_ptr<StaticRuntimeInfo> runtimeInfo;
585 auto kernelArgs = iterateValues(args, KERNEL);
586 if (!kernelArgs.empty()) {
587 runtimeInfo = getRuntimeInfo(kernelArgs);
588 if (runtimeInfo == nullptr) {
589 return usage(argv[0]);
590 }
591 }
592
593 if (dirmap.empty()) {
594 LOG(ERROR) << "ERROR: Missing --rootdir or --dirmap option.";
595 return usage(argv[0]);
596 }
597
598 auto compat = checkAllFiles(dirmap, properties, runtimeInfo);
599
600 if (compat.ok()) {
601 std::cout << "COMPATIBLE" << std::endl;
602 return EX_OK;
603 }
604 if (compat.error().code() == 0) {
605 LOG(ERROR) << "ERROR: files are incompatible: " << compat.error();
606 std::cout << "INCOMPATIBLE" << std::endl;
607 return EX_DATAERR;
608 }
609 LOG(ERROR) << "ERROR: " << strerror(compat.error().code()) << ": " << compat.error();
610 return EX_SOFTWARE;
611 }
612