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
ErrorMessage(const android::binder::Status & status,int error_code=IGsiService::INSTALL_ERROR_GENERIC)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:
ProgressBar(sp<IGsiService> gsid)82 explicit ProgressBar(sp<IGsiService> gsid) : gsid_(gsid) {}
83
~ProgressBar()84 ~ProgressBar() { Stop(); }
85
Display()86 void Display() {
87 Finish();
88 done_ = false;
89 last_update_ = {};
90 worker_ = std::make_unique<std::thread>([this]() { Worker(); });
91 }
92
Stop()93 void Stop() {
94 if (!worker_) {
95 return;
96 }
97 SignalDone();
98 worker_->join();
99 worker_ = nullptr;
100 }
101
Finish()102 void Finish() {
103 if (!worker_) {
104 return;
105 }
106 Stop();
107 FinishLastBar();
108 }
109
110 private:
Worker()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
UpdateProgress()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
FinishLastBar()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
Display(const GsiProgress & progress)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
SignalDone()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
Install(sp<IGsiService> gsid,int argc,char ** argv)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
CreatePartition(sp<IGsiService> gsid,int argc,char ** argv)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
Wipe(sp<IGsiService> gsid,int argc,char **)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
WipeData(sp<IGsiService> gsid,int argc,char **)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
Status(sp<IGsiService> gsid,int argc,char **)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
Cancel(sp<IGsiService> gsid,int,char **)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
Enable(sp<IGsiService> gsid,int argc,char ** argv)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
Disable(sp<IGsiService> gsid,int argc,char **)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
usage(int,char * argv[])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
main(int argc,char ** argv)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