/* * Copyright (C) 2017 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "utils.h" namespace android { namespace vintf { namespace details { // fake sysprops using Properties = std::map; using Dirmap = std::map; enum Option : int { // Modes HELP, DUMP_FILE_LIST = 1, CHECK_COMPAT, CHECK_ONE, // Options ROOTDIR, PROPERTY, DIR_MAP, KERNEL, }; // command line arguments using Args = std::multimap; class HostFileSystem : public details::FileSystemImpl { public: HostFileSystem(const Dirmap& dirmap, status_t missingError) : mDirMap(dirmap), mMissingError(missingError) {} status_t fetch(const std::string& path, std::string* fetched, std::string* error) const override { auto resolved = resolve(path, error); if (resolved.empty()) { return mMissingError; } status_t status = details::FileSystemImpl::fetch(resolved, fetched, error); LOG(INFO) << "Fetch '" << resolved << "': " << toString(status); return status; } status_t listFiles(const std::string& path, std::vector* out, std::string* error) const override { auto resolved = resolve(path, error); if (resolved.empty()) { return mMissingError; } status_t status = details::FileSystemImpl::listFiles(resolved, out, error); LOG(INFO) << "List '" << resolved << "': " << toString(status); return status; } private: static std::string toString(status_t status) { return status == OK ? "SUCCESS" : strerror(-status); } std::string resolve(const std::string& path, std::string* error) const { for (auto [prefix, mappedPath] : mDirMap) { if (path == prefix) { return mappedPath; } if (android::base::StartsWith(path, prefix + "/")) { return mappedPath + "/" + path.substr(prefix.size() + 1); } } if (error) { *error = "Cannot resolve path " + path; } else { LOG(mMissingError == NAME_NOT_FOUND ? INFO : ERROR) << "Cannot resolve path " << path; } return ""; } Dirmap mDirMap; status_t mMissingError; }; class PresetPropertyFetcher : public PropertyFetcher { public: std::string getProperty(const std::string& key, const std::string& defaultValue) const override { auto it = mProps.find(key); if (it == mProps.end()) { LOG(INFO) << "Sysprop " << key << " is missing, default to '" << defaultValue << "'"; return defaultValue; } LOG(INFO) << "Sysprop " << key << "=" << it->second; return it->second; } uint64_t getUintProperty(const std::string& key, uint64_t defaultValue, uint64_t max) const override { uint64_t result; std::string value = getProperty(key, ""); if (!value.empty() && android::base::ParseUint(value, &result, max)) return result; return defaultValue; } bool getBoolProperty(const std::string& key, bool defaultValue) const override { std::string value = getProperty(key, ""); if (value == "1" || value == "true") { return true; } else if (value == "0" || value == "false") { return false; } return defaultValue; } void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); } private: std::map mProps; }; struct StaticRuntimeInfo : public RuntimeInfo { KernelVersion kernelVersion; std::string kernelConfigFile; status_t fetchAllInformation(FetchFlags flags) override { if (flags & RuntimeInfo::FetchFlag::CPU_VERSION) { mKernel.mVersion = kernelVersion; LOG(INFO) << "fetched kernel version " << kernelVersion; } if (flags & RuntimeInfo::FetchFlag::CONFIG_GZ) { std::string content; if (!android::base::ReadFileToString(kernelConfigFile, &content)) { LOG(ERROR) << "Cannot read " << kernelConfigFile; return UNKNOWN_ERROR; } KernelConfigParser parser; auto status = parser.processAndFinish(content); if (status != OK) { return status; } mKernel.mConfigs = std::move(parser.configs()); LOG(INFO) << "read kernel configs from " << kernelConfigFile; } if (flags & RuntimeInfo::FetchFlag::POLICYVERS) { mKernelSepolicyVersion = SIZE_MAX; } return OK; } }; struct StubRuntimeInfo : public RuntimeInfo { status_t fetchAllInformation(FetchFlags) override { return UNKNOWN_ERROR; } }; struct StaticRuntimeInfoFactory : public ObjectFactory { std::shared_ptr info; StaticRuntimeInfoFactory(std::shared_ptr i) : info(i) {} std::shared_ptr make_shared() const override { if (info) return info; return std::make_shared(); } }; // helper functions template std::unique_ptr readObject(FileSystem* fileSystem, const std::string& path, const XmlConverter& converter) { std::string xml; std::string error; status_t err = fileSystem->fetch(path, &xml, &error); if (err != OK) { LOG(ERROR) << "Cannot read '" << path << "' (" << strerror(-err) << "): " << error; return nullptr; } auto ret = std::make_unique(); if (!converter(ret.get(), xml, &error)) { LOG(ERROR) << "Cannot parse '" << path << "': " << error; return nullptr; } return ret; } int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) { auto fileSystem = std::make_unique(); auto manifest = readObject(fileSystem.get(), manifestPath, gHalManifestConverter); auto matrix = readObject(fileSystem.get(), matrixPath, gCompatibilityMatrixConverter); if (manifest == nullptr || matrix == nullptr) { return -1; } std::string error; if (!manifest->checkCompatibility(*matrix, &error)) { LOG(ERROR) << "Incompatible: " << error; std::cout << "false" << std::endl; return 1; } std::cout << "true" << std::endl; return 0; } Args parseArgs(int argc, char** argv) { int longOptFlag; int optionIndex; Args ret; std::vector longopts{ // Modes {"help", no_argument, &longOptFlag, HELP}, {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST}, {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT}, {"check-one", no_argument, &longOptFlag, CHECK_ONE}, // Options {"rootdir", required_argument, &longOptFlag, ROOTDIR}, {"property", required_argument, &longOptFlag, PROPERTY}, {"dirmap", required_argument, &longOptFlag, DIR_MAP}, {"kernel", required_argument, &longOptFlag, KERNEL}, {0, 0, 0, 0}}; std::map shortopts{ {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT}, }; for (;;) { int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex); if (c == -1) { break; } std::string argValue = optarg ? optarg : std::string{}; if (c == 0) { ret.emplace(static_cast