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