1 // 2 // Copyright (C) 2019 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 <stdio.h> 19 #include <sysexits.h> 20 #include <unistd.h> 21 22 #include <chrono> 23 #include <condition_variable> 24 #include <functional> 25 #include <iostream> 26 #include <map> 27 #include <mutex> 28 #include <string> 29 #include <thread> 30 31 #include <android-base/logging.h> 32 #include <android-base/parseint.h> 33 #include <android-base/properties.h> 34 #include <android-base/stringprintf.h> 35 #include <android-base/strings.h> 36 #include <android-base/unique_fd.h> 37 #include <android/gsi/IGsiService.h> 38 #include <binder/IServiceManager.h> 39 #include <cutils/android_reboot.h> 40 #include <libgsi/libgsi.h> 41 #include <libgsi/libgsid.h> 42 43 using namespace android::gsi; 44 using namespace std::chrono_literals; 45 46 using android::sp; 47 using android::base::Split; 48 using android::base::StringPrintf; 49 using CommandCallback = std::function<int(sp<IGsiService>, int, char**)>; 50 51 static int Disable(sp<IGsiService> gsid, int argc, char** argv); 52 static int Enable(sp<IGsiService> gsid, int argc, char** argv); 53 static int Install(sp<IGsiService> gsid, int argc, char** argv); 54 static int Wipe(sp<IGsiService> gsid, int argc, char** argv); 55 static int WipeData(sp<IGsiService> gsid, int argc, char** argv); 56 static int Status(sp<IGsiService> gsid, int argc, char** argv); 57 static int Cancel(sp<IGsiService> gsid, int argc, char** argv); 58 59 static const std::map<std::string, CommandCallback> kCommandMap = { 60 // clang-format off 61 {"disable", Disable}, 62 {"enable", Enable}, 63 {"install", Install}, 64 {"wipe", Wipe}, 65 {"wipe-data", WipeData}, 66 {"status", Status}, 67 {"cancel", Cancel}, 68 // clang-format on 69 }; 70 71 static std::string ErrorMessage(const android::binder::Status& status, 72 int error_code = IGsiService::INSTALL_ERROR_GENERIC) { 73 if (!status.isOk()) { 74 return status.exceptionMessage().string(); 75 } 76 return "error code " + std::to_string(error_code); 77 } 78 79 class ProgressBar { 80 public: 81 explicit ProgressBar(sp<IGsiService> gsid) : gsid_(gsid) {} 82 83 ~ProgressBar() { Stop(); } 84 85 void Display() { 86 Finish(); 87 done_ = false; 88 last_update_ = {}; 89 worker_ = std::make_unique<std::thread>([this]() { Worker(); }); 90 } 91 92 void Stop() { 93 if (!worker_) { 94 return; 95 } 96 SignalDone(); 97 worker_->join(); 98 worker_ = nullptr; 99 } 100 101 void Finish() { 102 if (!worker_) { 103 return; 104 } 105 Stop(); 106 FinishLastBar(); 107 } 108 109 private: 110 void Worker() { 111 std::unique_lock<std::mutex> lock(mutex_); 112 while (!done_) { 113 if (!UpdateProgress()) { 114 return; 115 } 116 cv_.wait_for(lock, 500ms, [this] { return done_; }); 117 } 118 } 119 120 bool UpdateProgress() { 121 GsiProgress latest; 122 auto status = gsid_->getInstallProgress(&latest); 123 if (!status.isOk()) { 124 std::cout << std::endl; 125 return false; 126 } 127 if (latest.status == IGsiService::STATUS_NO_OPERATION) { 128 return true; 129 } 130 if (last_update_.step != latest.step) { 131 FinishLastBar(); 132 } 133 Display(latest); 134 return true; 135 } 136 137 void FinishLastBar() { 138 // If no bar was in progress, don't do anything. 139 if (last_update_.total_bytes == 0) { 140 return; 141 } 142 // Ensure we finish the display at 100%. 143 last_update_.bytes_processed = last_update_.total_bytes; 144 Display(last_update_); 145 std::cout << std::endl; 146 } 147 148 void Display(const GsiProgress& progress) { 149 if (progress.total_bytes == 0) { 150 return; 151 } 152 153 static constexpr int kColumns = 80; 154 static constexpr char kRedColor[] = "\x1b[31m"; 155 static constexpr char kGreenColor[] = "\x1b[32m"; 156 static constexpr char kResetColor[] = "\x1b[0m"; 157 158 int percentage = (progress.bytes_processed * 100) / progress.total_bytes; 159 int64_t bytes_per_col = progress.total_bytes / kColumns; 160 uint32_t fill_count = progress.bytes_processed / bytes_per_col; 161 uint32_t dash_count = kColumns - fill_count; 162 std::string fills = std::string(fill_count, '='); 163 std::string dashes = std::string(dash_count, '-'); 164 165 // Give the end of the bar some flare. 166 if (!fills.empty() && !dashes.empty()) { 167 fills[fills.size() - 1] = '>'; 168 } 169 170 fprintf(stdout, "\r%-15s%6d%% ", progress.step.c_str(), percentage); 171 fprintf(stdout, "%s[%s%s%s", kGreenColor, fills.c_str(), kRedColor, dashes.c_str()); 172 fprintf(stdout, "%s]%s", kGreenColor, kResetColor); 173 fflush(stdout); 174 175 last_update_ = progress; 176 } 177 178 void SignalDone() { 179 std::lock_guard<std::mutex> guard(mutex_); 180 done_ = true; 181 cv_.notify_all(); 182 } 183 184 private: 185 sp<IGsiService> gsid_; 186 std::unique_ptr<std::thread> worker_; 187 std::condition_variable cv_; 188 std::mutex mutex_; 189 GsiProgress last_update_; 190 bool done_ = false; 191 }; 192 193 static int Install(sp<IGsiService> gsid, int argc, char** argv) { 194 constexpr const char* kDefaultPartition = "system"; 195 struct option options[] = { 196 {"install-dir", required_argument, nullptr, 'i'}, 197 {"gsi-size", required_argument, nullptr, 's'}, 198 {"no-reboot", no_argument, nullptr, 'n'}, 199 {"userdata-size", required_argument, nullptr, 'u'}, 200 {"partition-name", required_argument, nullptr, 'p'}, 201 {"wipe", no_argument, nullptr, 'w'}, 202 {nullptr, 0, nullptr, 0}, 203 }; 204 205 int64_t gsiSize = 0; 206 int64_t userdataSize = 0; 207 bool wipeUserdata = false; 208 bool reboot = true; 209 std::string installDir = ""; 210 std::string partition = kDefaultPartition; 211 if (getuid() != 0) { 212 std::cerr << "must be root to install a GSI" << std::endl; 213 return EX_NOPERM; 214 } 215 216 int rv, index; 217 while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) { 218 switch (rv) { 219 case 'p': 220 partition = optarg; 221 break; 222 case 's': 223 if (!android::base::ParseInt(optarg, &gsiSize) || gsiSize <= 0) { 224 std::cerr << "Could not parse image size: " << optarg << std::endl; 225 return EX_USAGE; 226 } 227 break; 228 case 'u': 229 if (!android::base::ParseInt(optarg, &userdataSize) || userdataSize < 0) { 230 std::cerr << "Could not parse image size: " << optarg << std::endl; 231 return EX_USAGE; 232 } 233 break; 234 case 'i': 235 installDir = optarg; 236 break; 237 case 'w': 238 wipeUserdata = true; 239 break; 240 case 'n': 241 reboot = false; 242 break; 243 } 244 } 245 246 if (gsiSize <= 0) { 247 std::cerr << "Must specify --gsi-size." << std::endl; 248 return EX_USAGE; 249 } 250 251 bool running_gsi = false; 252 gsid->isGsiRunning(&running_gsi); 253 if (running_gsi) { 254 std::cerr << "Cannot install a GSI within a live GSI." << std::endl; 255 std::cerr << "Use gsi_tool disable or wipe and reboot first." << std::endl; 256 return EX_SOFTWARE; 257 } 258 259 android::base::unique_fd input(dup(1)); 260 if (input < 0) { 261 std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl; 262 return EX_SOFTWARE; 263 } 264 // Note: the progress bar needs to be re-started in between each call. 265 ProgressBar progress(gsid); 266 progress.Display(); 267 int error; 268 auto status = gsid->openInstall(installDir, &error); 269 if (!status.isOk() || error != IGsiService::INSTALL_OK) { 270 std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error) << "\n"; 271 return EX_SOFTWARE; 272 } 273 if (partition == kDefaultPartition) { 274 auto status = gsid->createPartition("userdata", userdataSize, false, &error); 275 if (!status.isOk() || error != IGsiService::INSTALL_OK) { 276 std::cerr << "Could not start live image install: " << ErrorMessage(status, error) 277 << "\n"; 278 return EX_SOFTWARE; 279 } 280 } 281 282 status = gsid->createPartition(partition, gsiSize, true, &error); 283 if (!status.isOk() || error != IGsiService::INSTALL_OK) { 284 std::cerr << "Could not start live image install: " << ErrorMessage(status, error) << "\n"; 285 return EX_SOFTWARE; 286 } 287 android::os::ParcelFileDescriptor stream(std::move(input)); 288 289 bool ok = false; 290 progress.Display(); 291 status = gsid->commitGsiChunkFromStream(stream, gsiSize, &ok); 292 if (!ok) { 293 std::cerr << "Could not commit live image data: " << ErrorMessage(status) << "\n"; 294 return EX_SOFTWARE; 295 } 296 297 status = gsid->closeInstall(&error); 298 if (!status.isOk() || error != IGsiService::INSTALL_OK) { 299 std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error) << "\n"; 300 return EX_SOFTWARE; 301 } 302 progress.Finish(); 303 std::string dsuSlot; 304 status = gsid->getActiveDsuSlot(&dsuSlot); 305 if (!status.isOk()) { 306 std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n"; 307 return EX_SOFTWARE; 308 } 309 status = gsid->enableGsi(true, dsuSlot, &error); 310 if (!status.isOk() || error != IGsiService::INSTALL_OK) { 311 std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n"; 312 return EX_SOFTWARE; 313 } 314 315 if (reboot) { 316 if (!android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,adb")) { 317 std::cerr << "Failed to reboot automatically" << std::endl; 318 return EX_SOFTWARE; 319 } 320 } else { 321 std::cout << "Please reboot to use the GSI." << std::endl; 322 } 323 return 0; 324 } 325 326 static int Wipe(sp<IGsiService> gsid, int argc, char** /* argv */) { 327 if (argc > 1) { 328 std::cerr << "Unrecognized arguments to wipe." << std::endl; 329 return EX_USAGE; 330 } 331 bool ok; 332 auto status = gsid->removeGsi(&ok); 333 if (!status.isOk() || !ok) { 334 std::cerr << "Could not remove GSI install: " << ErrorMessage(status) << "\n"; 335 return EX_SOFTWARE; 336 } 337 338 bool running = false; 339 if (gsid->isGsiRunning(&running).isOk() && running) { 340 std::cout << "Live image install will be removed next reboot." << std::endl; 341 } else { 342 std::cout << "Live image install successfully removed." << std::endl; 343 } 344 return 0; 345 } 346 347 static int WipeData(sp<IGsiService> gsid, int argc, char** /* argv */) { 348 if (argc > 1) { 349 std::cerr << "Unrecognized arguments to wipe-data.\n"; 350 return EX_USAGE; 351 } 352 353 bool running; 354 auto status = gsid->isGsiRunning(&running); 355 if (!status.isOk()) { 356 std::cerr << "error: " << status.exceptionMessage().string() << std::endl; 357 return EX_SOFTWARE; 358 } 359 if (running) { 360 std::cerr << "Cannot wipe GSI userdata while running a GSI.\n"; 361 return EX_USAGE; 362 } 363 364 bool installed; 365 status = gsid->isGsiInstalled(&installed); 366 if (!status.isOk()) { 367 std::cerr << "error: " << status.exceptionMessage().string() << std::endl; 368 return EX_SOFTWARE; 369 } 370 if (!installed) { 371 std::cerr << "No GSI is installed.\n"; 372 return EX_USAGE; 373 } 374 375 int error; 376 status = gsid->zeroPartition("userdata" + std::string(kDsuPostfix), &error); 377 if (!status.isOk() || error) { 378 std::cerr << "Could not wipe GSI userdata: " << ErrorMessage(status, error) << "\n"; 379 return EX_SOFTWARE; 380 } 381 return 0; 382 } 383 384 static int Status(sp<IGsiService> gsid, int argc, char** /* argv */) { 385 if (argc > 1) { 386 std::cerr << "Unrecognized arguments to status." << std::endl; 387 return EX_USAGE; 388 } 389 bool running; 390 auto status = gsid->isGsiRunning(&running); 391 if (!status.isOk()) { 392 std::cerr << "error: " << status.exceptionMessage().string() << std::endl; 393 return EX_SOFTWARE; 394 } else if (running) { 395 std::cout << "running" << std::endl; 396 } 397 bool installed; 398 status = gsid->isGsiInstalled(&installed); 399 if (!status.isOk()) { 400 std::cerr << "error: " << status.exceptionMessage().string() << std::endl; 401 return EX_SOFTWARE; 402 } else if (installed) { 403 std::cout << "installed" << std::endl; 404 } 405 bool enabled; 406 status = gsid->isGsiEnabled(&enabled); 407 if (!status.isOk()) { 408 std::cerr << status.exceptionMessage().string() << std::endl; 409 return EX_SOFTWARE; 410 } else if (running || installed) { 411 std::cout << (enabled ? "enabled" : "disabled") << std::endl; 412 } else { 413 std::cout << "normal" << std::endl; 414 } 415 if (getuid() != 0) { 416 return 0; 417 } 418 419 std::vector<std::string> dsu_slots; 420 status = gsid->getInstalledDsuSlots(&dsu_slots); 421 if (!status.isOk()) { 422 std::cerr << status.exceptionMessage().string() << std::endl; 423 return EX_SOFTWARE; 424 } 425 int n = 0; 426 for (auto&& dsu_slot : dsu_slots) { 427 std::cout << "[" << n++ << "] " << dsu_slot << std::endl; 428 sp<IImageService> image_service = nullptr; 429 status = gsid->openImageService("dsu/" + dsu_slot + "/", &image_service); 430 if (!status.isOk()) { 431 std::cerr << "error: " << status.exceptionMessage().string() << std::endl; 432 return EX_SOFTWARE; 433 } 434 std::vector<std::string> images; 435 status = image_service->getAllBackingImages(&images); 436 if (!status.isOk()) { 437 std::cerr << "error: " << status.exceptionMessage().string() << std::endl; 438 return EX_SOFTWARE; 439 } 440 for (auto&& image : images) { 441 std::cout << "installed: " << image << std::endl; 442 AvbPublicKey public_key; 443 int err = 0; 444 status = image_service->getAvbPublicKey(image, &public_key, &err); 445 std::cout << "AVB public key (sha1): "; 446 if (!public_key.bytes.empty()) { 447 for (auto b : public_key.sha1) { 448 std::cout << StringPrintf("%02x", b & 255); 449 } 450 std::cout << std::endl; 451 } else { 452 std::cout << "[NONE]" << std::endl; 453 } 454 } 455 } 456 return 0; 457 } 458 459 static int Cancel(sp<IGsiService> gsid, int /* argc */, char** /* argv */) { 460 bool cancelled = false; 461 auto status = gsid->cancelGsiInstall(&cancelled); 462 if (!status.isOk()) { 463 std::cerr << status.exceptionMessage().string() << std::endl; 464 return EX_SOFTWARE; 465 } 466 if (!cancelled) { 467 std::cout << "Fail to cancel the installation." << std::endl; 468 return EX_SOFTWARE; 469 } 470 return 0; 471 } 472 473 static int Enable(sp<IGsiService> gsid, int argc, char** argv) { 474 bool one_shot = false; 475 std::string dsuSlot = {}; 476 struct option options[] = { 477 {"single-boot", no_argument, nullptr, 's'}, 478 {"dsuslot", required_argument, nullptr, 'd'}, 479 {nullptr, 0, nullptr, 0}, 480 }; 481 int rv, index; 482 while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) { 483 switch (rv) { 484 case 's': 485 one_shot = true; 486 break; 487 case 'd': 488 dsuSlot = optarg; 489 break; 490 default: 491 std::cerr << "Unrecognized argument to enable\n"; 492 return EX_USAGE; 493 } 494 } 495 496 bool installed = false; 497 gsid->isGsiInstalled(&installed); 498 if (!installed) { 499 std::cerr << "Could not find GSI install to re-enable" << std::endl; 500 return EX_SOFTWARE; 501 } 502 503 bool installing = false; 504 gsid->isGsiInstallInProgress(&installing); 505 if (installing) { 506 std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl; 507 return EX_SOFTWARE; 508 } 509 if (dsuSlot.empty()) { 510 auto status = gsid->getActiveDsuSlot(&dsuSlot); 511 if (!status.isOk()) { 512 std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n"; 513 return EX_SOFTWARE; 514 } 515 } 516 int error; 517 auto status = gsid->enableGsi(one_shot, dsuSlot, &error); 518 if (!status.isOk() || error != IGsiService::INSTALL_OK) { 519 std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n"; 520 return EX_SOFTWARE; 521 } 522 std::cout << "Live image install successfully enabled." << std::endl; 523 return 0; 524 } 525 526 static int Disable(sp<IGsiService> gsid, int argc, char** /* argv */) { 527 if (argc > 1) { 528 std::cerr << "Unrecognized arguments to disable." << std::endl; 529 return EX_USAGE; 530 } 531 532 bool installing = false; 533 gsid->isGsiInstallInProgress(&installing); 534 if (installing) { 535 std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl; 536 return EX_SOFTWARE; 537 } 538 539 bool ok = false; 540 gsid->disableGsi(&ok); 541 if (!ok) { 542 std::cerr << "Error disabling GSI" << std::endl; 543 return EX_SOFTWARE; 544 } 545 std::cout << "Live image install successfully disabled." << std::endl; 546 return 0; 547 } 548 549 static int usage(int /* argc */, char* argv[]) { 550 fprintf(stderr, 551 "%s - command-line tool for installing GSI images.\n" 552 "\n" 553 "Usage:\n" 554 " %s <disable|install|wipe|status> [options]\n" 555 "\n" 556 " disable Disable the currently installed GSI.\n" 557 " enable [-s, --single-boot]\n" 558 " [-d, --dsuslot slotname]\n" 559 " Enable a previously disabled GSI.\n" 560 " install Install a new GSI. Specify the image size with\n" 561 " --gsi-size and the desired userdata size with\n" 562 " --userdata-size (the latter defaults to 8GiB)\n" 563 " --wipe (remove old gsi userdata first)\n" 564 " wipe Completely remove a GSI and its associated data\n" 565 " wipe-data Ensure the GSI's userdata will be formatted\n" 566 " cancel Cancel the installation\n" 567 " status Show status\n", 568 argv[0], argv[0]); 569 return EX_USAGE; 570 } 571 572 int main(int argc, char** argv) { 573 android::base::InitLogging(argv, android::base::StdioLogger, android::base::DefaultAborter); 574 575 android::sp<IGsiService> service = GetGsiService(); 576 if (!service) { 577 return EX_SOFTWARE; 578 } 579 580 if (1 >= argc) { 581 std::cerr << "Expected command." << std::endl; 582 return EX_USAGE; 583 } 584 585 std::string command = argv[1]; 586 587 auto iter = kCommandMap.find(command); 588 if (iter == kCommandMap.end()) { 589 std::cerr << "Unrecognized command: " << command << std::endl; 590 return usage(argc, argv); 591 } 592 593 int rc = iter->second(service, argc - 1, argv + 1); 594 return rc; 595 } 596