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 <unistd.h> 19 20 #include <iostream> 21 #include <map> 22 23 #include <android-base/parseint.h> 24 #include <utils/Errors.h> 25 #include <vintf/VintfObject.h> 26 #include <vintf/parse_xml.h> 27 #include "utils.h" 28 29 namespace android { 30 namespace vintf { 31 namespace details { 32 33 // fake sysprops 34 using Properties = std::map<std::string, std::string>; 35 36 enum Option : int { 37 DUMP_FILE_LIST = 1, 38 ROOTDIR, 39 HELP, 40 PROPERTY, 41 CHECK_COMPAT, 42 }; 43 // command line arguments 44 using Args = std::multimap<Option, std::string>; 45 46 class HostFileFetcher : public FileFetcher { 47 public: 48 void setRootDir(const std::string& rootdir) { 49 mRootDir = rootdir; 50 if (!mRootDir.empty() && mRootDir.back() != '/') { 51 mRootDir.push_back('/'); 52 } 53 } 54 virtual status_t fetch(const std::string& path, std::string& fetched, std::string* error) { 55 return HostFileFetcher::fetchInternal(path, fetched, error); 56 } 57 virtual status_t fetch(const std::string& path, std::string& fetched) { 58 return HostFileFetcher::fetchInternal(path, fetched, nullptr); 59 } 60 virtual status_t listFiles(const std::string& path, std::vector<std::string>* out, 61 std::string* error) { 62 status_t status = FileFetcher::listFiles(mRootDir + path, out, error); 63 std::cerr << "Debug: List '" << mRootDir << path << "': " << toString(status) << std::endl; 64 return status; 65 } 66 67 private: 68 status_t fetchInternal(const std::string& path, std::string& fetched, std::string* error) { 69 status_t status = FileFetcher::fetchInternal(mRootDir + path, fetched, error); 70 std::cerr << "Debug: Fetch '" << mRootDir << path << "': " << toString(status) << std::endl; 71 return status; 72 } 73 static std::string toString(status_t status) { 74 return status == OK ? "SUCCESS" : strerror(-status); 75 } 76 std::string mRootDir; 77 }; 78 79 class PresetPropertyFetcher : public PropertyFetcher { 80 public: 81 std::string getProperty(const std::string& key, 82 const std::string& defaultValue) const override { 83 auto it = mProps.find(key); 84 if (it == mProps.end()) { 85 std::cerr << "Debug: Sysprop " << key << " is missing, default to '" << defaultValue 86 << "'" << std::endl; 87 return defaultValue; 88 } 89 std::cerr << "Debug: Sysprop " << key << "=" << it->second << std::endl; 90 return it->second; 91 } 92 uint64_t getUintProperty(const std::string& key, uint64_t defaultValue, 93 uint64_t max) const override { 94 uint64_t result; 95 std::string value = getProperty(key, ""); 96 if (!value.empty() && android::base::ParseUint(value, &result, max)) return result; 97 return defaultValue; 98 } 99 bool getBoolProperty(const std::string& key, bool defaultValue) const override { 100 std::string value = getProperty(key, ""); 101 if (value == "1" || value == "true") { 102 return true; 103 } else if (value == "0" || value == "false") { 104 return false; 105 } 106 return defaultValue; 107 } 108 void setProperties(const Properties& props) { mProps.insert(props.begin(), props.end()); } 109 110 private: 111 std::map<std::string, std::string> mProps; 112 }; 113 114 // globals 115 static HostFileFetcher hostFileFetcher; 116 FileFetcher* gFetcher = &hostFileFetcher; 117 118 static PartitionMounter partitionMounter; 119 PartitionMounter* gPartitionMounter = &partitionMounter; 120 121 static ObjectFactory<RuntimeInfo> runtimeInfoFactory; 122 ObjectFactory<RuntimeInfo>* gRuntimeInfoFactory = &runtimeInfoFactory; 123 124 static PresetPropertyFetcher hostPropertyFetcher; 125 const PropertyFetcher& getPropertyFetcher() { 126 return hostPropertyFetcher; 127 } 128 129 // helper functions 130 template <typename T> 131 std::unique_ptr<T> readObject(const std::string& path, const XmlConverter<T>& converter) { 132 std::string xml; 133 std::string error; 134 status_t err = details::gFetcher->fetch(path, xml, &error); 135 if (err != OK) { 136 std::cerr << "Error: Cannot read '" << path << "' (" << strerror(-err) << "): " << error 137 << std::endl; 138 return nullptr; 139 } 140 auto ret = std::make_unique<T>(); 141 if (!converter(ret.get(), xml, &error)) { 142 std::cerr << "Error: Cannot parse '" << path << "': " << error << std::endl; 143 return nullptr; 144 } 145 return ret; 146 } 147 148 int checkCompatibilityForFiles(const std::string& manifestPath, const std::string& matrixPath) { 149 auto manifest = readObject(manifestPath, gHalManifestConverter); 150 auto matrix = readObject(matrixPath, gCompatibilityMatrixConverter); 151 if (manifest == nullptr || matrix == nullptr) { 152 return -1; 153 } 154 155 std::string error; 156 if (!manifest->checkCompatibility(*matrix, &error)) { 157 std::cerr << "Error: Incompatible: " << error << std::endl; 158 std::cout << "false" << std::endl; 159 return 1; 160 } 161 162 std::cout << "true" << std::endl; 163 return 0; 164 } 165 166 Args parseArgs(int argc, char** argv) { 167 int longOptFlag; 168 int optionIndex; 169 Args ret; 170 std::vector<struct option> longopts{ 171 {"dump-file-list", no_argument, &longOptFlag, DUMP_FILE_LIST}, 172 {"rootdir", required_argument, &longOptFlag, ROOTDIR}, 173 {"help", no_argument, &longOptFlag, HELP}, 174 {"property", required_argument, &longOptFlag, PROPERTY}, 175 {"check-compat", no_argument, &longOptFlag, CHECK_COMPAT}, 176 {0, 0, 0, 0}}; 177 std::map<int, Option> shortopts{ 178 {'h', HELP}, {'D', PROPERTY}, {'c', CHECK_COMPAT}, 179 }; 180 for (;;) { 181 int c = getopt_long(argc, argv, "hcD:", longopts.data(), &optionIndex); 182 if (c == -1) { 183 break; 184 } 185 std::string argValue = optarg ? optarg : std::string{}; 186 if (c == 0) { 187 ret.emplace(static_cast<Option>(longOptFlag), std::move(argValue)); 188 } else { 189 ret.emplace(shortopts[c], std::move(argValue)); 190 } 191 } 192 if (optind < argc) { 193 // see non option 194 std::cerr << "unrecognized option `" << argv[optind] << "'" << std::endl; 195 return {{HELP, ""}}; 196 } 197 return ret; 198 } 199 200 template <typename T> 201 Properties getProperties(const T& args) { 202 Properties ret; 203 for (const auto& arg : args) { 204 auto pos = arg.find('='); 205 auto key = arg.substr(0, pos); 206 auto value = pos == std::string::npos ? std::string{} : arg.substr(pos + 1); 207 ret[key] = value; 208 } 209 return ret; 210 } 211 212 int usage(const char* me) { 213 std::cerr 214 << me << ": check VINTF metadata." << std::endl 215 << " Options:" << std::endl 216 << " --dump-file-list: Dump a list of directories / files on device" << std::endl 217 << " that is required to be used by --check-compat." << std::endl 218 << " -c, --check-compat: check compatibility for files under the root" << std::endl 219 << " directory specified by --root-dir." << std::endl 220 << " --rootdir=<dir>: specify root directory for all metadata." << std::endl 221 << " -D, --property <key>=<value>: specify sysprops." << std::endl 222 << " --help: show this message." << std::endl 223 << std::endl 224 << " Example:" << std::endl 225 << " # Get the list of required files." << std::endl 226 << " " << me << " --dump-file-list > /tmp/files.txt" << std::endl 227 << " # Pull from ADB, or use your own command to extract files from images" 228 << std::endl 229 << " ROOTDIR=/tmp/device/" << std::endl 230 << " cat /tmp/files.txt | xargs -I{} bash -c \"mkdir -p $ROOTDIR`dirname {}` && adb " 231 "pull {} $ROOTDIR{}\"" 232 << std::endl 233 << " # Check compatibility." << std::endl 234 << " " << me << " --check-compat --rootdir=$ROOTDIR \\" << std::endl 235 << " --property ro.product.first_api_level=`adb shell getprop " 236 "ro.product.first_api_level` \\" 237 << std::endl 238 << " --property ro.boot.product.hardware.sku=`adb shell getprop " 239 "ro.boot.product.hardware.sku`" 240 << std::endl; 241 return 1; 242 } 243 244 int checkAllFiles(const std::string& rootdir, const Properties& props, std::string* error) { 245 hostFileFetcher.setRootDir(rootdir); 246 hostPropertyFetcher.setProperties(props); 247 248 return VintfObject::CheckCompatibility({} /* packageInfo */, error, DISABLE_RUNTIME_INFO); 249 } 250 251 } // namespace details 252 } // namespace vintf 253 } // namespace android 254 255 int main(int argc, char** argv) { 256 using namespace android::vintf; 257 using namespace android::vintf::details; 258 // legacy usage: check_vintf <manifest.xml> <matrix.xml> 259 if (argc == 3) { 260 int ret = checkCompatibilityForFiles(argv[1], argv[2]); 261 if (ret >= 0) return ret; 262 } 263 264 Args args = parseArgs(argc, argv); 265 266 if (!iterateValues(args, HELP).empty()) { 267 return usage(argv[0]); 268 } 269 270 if (!iterateValues(args, DUMP_FILE_LIST).empty()) { 271 for (const auto& file : dumpFileList()) { 272 std::cout << file << std::endl; 273 } 274 return 0; 275 } 276 277 auto rootdirs = iterateValues(args, ROOTDIR); 278 auto properties = getProperties(iterateValues(args, PROPERTY)); 279 280 auto checkCompat = iterateValues(args, CHECK_COMPAT); 281 if (!checkCompat.empty()) { 282 if (rootdirs.empty()) { 283 std::cerr << "Missing --rootdir option." << std::endl; 284 return usage(argv[0]); 285 } 286 int ret = COMPATIBLE; 287 for (const auto& rootdir : rootdirs) { 288 std::cerr << "Debug: checking files under " << rootdir << "..." << std::endl; 289 std::string error; 290 int compat = checkAllFiles(rootdir, properties, &error); 291 std::cerr << "Debug: files under " << rootdir 292 << (compat == COMPATIBLE 293 ? " is compatible" 294 : compat == INCOMPATIBLE ? " are incompatible" 295 : (" has encountered an error: " + error)) 296 << std::endl; 297 } 298 if (ret == COMPATIBLE) { 299 std::cout << "true" << std::endl; 300 } 301 return ret; 302 } 303 304 return usage(argv[0]); 305 } 306