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