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