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 {"wipe", no_argument, nullptr, 'w'},
241 {nullptr, 0, nullptr, 0},
242 };
243
244 int64_t gsiSize = 0;
245 int64_t userdataSize = 0;
246 bool wipeUserdata = false;
247 bool reboot = true;
248 std::string installDir = "";
249 std::string partition = kDefaultPartition;
250 if (!IsRoot()) {
251 std::cerr << "must be root to install a GSI" << std::endl;
252 return EX_NOPERM;
253 }
254
255 int rv, index;
256 while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
257 switch (rv) {
258 case 'p':
259 partition = optarg;
260 break;
261 case 's':
262 if (!android::base::ParseInt(optarg, &gsiSize) || gsiSize <= 0) {
263 std::cerr << "Could not parse image size: " << optarg << std::endl;
264 return EX_USAGE;
265 }
266 break;
267 case 'u':
268 if (!android::base::ParseInt(optarg, &userdataSize) || userdataSize < 0) {
269 std::cerr << "Could not parse image size: " << optarg << std::endl;
270 return EX_USAGE;
271 }
272 break;
273 case 'i':
274 installDir = optarg;
275 break;
276 case 'w':
277 wipeUserdata = true;
278 break;
279 case 'n':
280 reboot = false;
281 break;
282 }
283 }
284
285 if (gsiSize <= 0) {
286 std::cerr << "Must specify --gsi-size." << std::endl;
287 return EX_USAGE;
288 }
289
290 bool running_gsi = false;
291 gsid->isGsiRunning(&running_gsi);
292 if (running_gsi) {
293 std::cerr << "Cannot install a GSI within a live GSI." << std::endl;
294 std::cerr << "Use gsi_tool disable or wipe and reboot first." << std::endl;
295 return EX_SOFTWARE;
296 }
297
298 android::base::unique_fd input(dup(STDIN_FILENO));
299 if (input < 0) {
300 std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
301 return EX_SOFTWARE;
302 }
303 // Note: the progress bar needs to be re-started in between each call.
304 ProgressBar progress(gsid);
305 progress.Display();
306 int error;
307 auto status = gsid->openInstall(installDir, &error);
308 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
309 std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error) << "\n";
310 return EX_SOFTWARE;
311 }
312 if (partition == kDefaultPartition) {
313 auto status = gsid->createPartition("userdata", userdataSize, false, &error);
314 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
315 std::cerr << "Could not start live image install: " << ErrorMessage(status, error)
316 << "\n";
317 return EX_SOFTWARE;
318 }
319 status = gsid->closePartition(&error);
320 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
321 std::cerr << "Could not closePartition(userdata): " << ErrorMessage(status, error)
322 << std::endl;
323 return EX_SOFTWARE;
324 }
325 }
326
327 status = gsid->createPartition(partition, gsiSize, true, &error);
328 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
329 std::cerr << "Could not start live image install: " << ErrorMessage(status, error) << "\n";
330 return EX_SOFTWARE;
331 }
332 android::os::ParcelFileDescriptor stream(std::move(input));
333
334 bool ok = false;
335 progress.Display();
336 status = gsid->commitGsiChunkFromStream(stream, gsiSize, &ok);
337 if (!ok) {
338 std::cerr << "Could not commit live image data: " << ErrorMessage(status) << "\n";
339 return EX_SOFTWARE;
340 }
341
342 status = gsid->closePartition(&error);
343 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
344 std::cerr << "Could not closePartition(" << partition
345 << "): " << ErrorMessage(status, error) << std::endl;
346 return EX_SOFTWARE;
347 }
348
349 status = gsid->closeInstall(&error);
350 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
351 std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error) << "\n";
352 return EX_SOFTWARE;
353 }
354 progress.Finish();
355 std::string dsuSlot;
356 status = gsid->getActiveDsuSlot(&dsuSlot);
357 if (!status.isOk()) {
358 std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
359 return EX_SOFTWARE;
360 }
361 status = gsid->enableGsi(true, dsuSlot, &error);
362 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
363 std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n";
364 return EX_SOFTWARE;
365 }
366
367 if (reboot) {
368 if (!android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,adb")) {
369 std::cerr << "Failed to reboot automatically" << std::endl;
370 return EX_SOFTWARE;
371 }
372 } else {
373 std::cout << "Please reboot to use the GSI." << std::endl;
374 }
375 return 0;
376 }
377
378 // Experimental API
CreatePartition(sp<IGsiService> gsid,int argc,char ** argv)379 static int CreatePartition(sp<IGsiService> gsid, int argc, char** argv) {
380 std::string installDir;
381 std::string partitionName;
382 bool readOnly = true;
383 int64_t partitionSize = 0;
384
385 struct option options[] = {
386 {"install-dir", required_argument, nullptr, 'i'},
387 {"partition-name", required_argument, nullptr, 'p'},
388 {"readwrite", no_argument, nullptr, 'r'},
389 {"size", required_argument, nullptr, 's'},
390 {nullptr, 0, nullptr, 0},
391 };
392
393 int rv = 0;
394 while ((rv = getopt_long_only(argc, argv, "", options, nullptr)) != -1) {
395 switch (rv) {
396 case 'i':
397 installDir = optarg;
398 break;
399 case 'p':
400 partitionName = optarg;
401 break;
402 case 'r':
403 readOnly = false;
404 break;
405 case 's':
406 if (!android::base::ParseInt(optarg, &partitionSize)) {
407 std::cerr << "Could not parse partition size: " << optarg << std::endl;
408 return EX_USAGE;
409 }
410 break;
411 default:
412 return EX_USAGE;
413 }
414 }
415
416 if (!IsRoot()) {
417 std::cerr << "must be root to install a DSU" << std::endl;
418 return EX_NOPERM;
419 }
420
421 bool gsiRunning = false;
422 auto status = gsid->isGsiRunning(&gsiRunning);
423 if (!status.isOk()) {
424 std::cerr << "Could not get DSU running status: " << ErrorMessage(status) << std::endl;
425 return EX_SOFTWARE;
426 }
427 if (gsiRunning) {
428 std::cerr << "Could not install DSU within an active DSU." << std::endl;
429 return EX_SOFTWARE;
430 }
431
432 if (partitionSize <= 0) {
433 std::cerr << "Partition size must be greater than zero: " << partitionSize << std::endl;
434 return EX_USAGE;
435 }
436
437 // Note: the progress bar needs to be re-started in between each call.
438 ProgressBar progress(gsid);
439 progress.Display();
440
441 int error;
442 status = gsid->openInstall(installDir, &error);
443 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
444 std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error)
445 << std::endl;
446 return EX_SOFTWARE;
447 }
448
449 status = gsid->createPartition(partitionName, partitionSize, readOnly, &error);
450 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
451 std::cerr << "Could not create DSU partition: " << ErrorMessage(status, error) << std::endl;
452 return EX_SOFTWARE;
453 }
454
455 if (readOnly) {
456 android::base::unique_fd input(dup(STDIN_FILENO));
457 if (input < 0) {
458 std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
459 return EX_SOFTWARE;
460 }
461 android::os::ParcelFileDescriptor stream(std::move(input));
462
463 bool ok = false;
464 status = gsid->commitGsiChunkFromStream(stream, partitionSize, &ok);
465 if (!ok) {
466 std::cerr << "Could not commit data from stdin: " << ErrorMessage(status) << std::endl;
467 return EX_SOFTWARE;
468 }
469 }
470
471 status = gsid->closePartition(&error);
472 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
473 std::cerr << "Could not close DSU partition:" << ErrorMessage(status, error) << std::endl;
474 return EX_SOFTWARE;
475 }
476
477 status = gsid->closeInstall(&error);
478 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
479 std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error)
480 << std::endl;
481 return EX_SOFTWARE;
482 }
483
484 progress.Finish();
485
486 std::string dsuSlot;
487 status = gsid->getActiveDsuSlot(&dsuSlot);
488 if (!status.isOk()) {
489 std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << std::endl;
490 return EX_SOFTWARE;
491 }
492
493 // Immediately enable DSU after a partition is installed to ensure the installation status file
494 // is created.
495 status = gsid->enableGsi(/* one_shot = */ true, dsuSlot, &error);
496 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
497 std::cerr << "Could not make DSU bootable: " << ErrorMessage(status, error) << std::endl;
498 return EX_SOFTWARE;
499 }
500
501 std::cout << "Enabled DSU slot: " << dsuSlot << std::endl;
502 std::cout << "Please reboot to use the DSU." << std::endl;
503 return 0;
504 }
505
Wipe(sp<IGsiService> gsid,int argc,char **)506 static int Wipe(sp<IGsiService> gsid, int argc, char** /* argv */) {
507 if (argc > 1) {
508 std::cerr << "Unrecognized arguments to wipe." << std::endl;
509 return EX_USAGE;
510 }
511 bool ok;
512 auto status = gsid->removeGsi(&ok);
513 if (!status.isOk() || !ok) {
514 std::cerr << "Could not remove GSI install: " << ErrorMessage(status) << "\n";
515 return EX_SOFTWARE;
516 }
517
518 bool running = false;
519 if (gsid->isGsiRunning(&running).isOk() && running) {
520 std::cout << "Live image install will be removed next reboot." << std::endl;
521 } else {
522 std::cout << "Live image install successfully removed." << std::endl;
523 }
524 return 0;
525 }
526
WipeData(sp<IGsiService> gsid,int argc,char **)527 static int WipeData(sp<IGsiService> gsid, int argc, char** /* argv */) {
528 if (argc > 1) {
529 std::cerr << "Unrecognized arguments to wipe-data.\n";
530 return EX_USAGE;
531 }
532
533 bool running;
534 auto status = gsid->isGsiRunning(&running);
535 if (!status.isOk()) {
536 std::cerr << "error: " << status.exceptionMessage().c_str() << std::endl;
537 return EX_SOFTWARE;
538 }
539 if (running) {
540 std::cerr << "Cannot wipe GSI userdata while running a GSI.\n";
541 return EX_USAGE;
542 }
543
544 bool installed;
545 status = gsid->isGsiInstalled(&installed);
546 if (!status.isOk()) {
547 std::cerr << "error: " << status.exceptionMessage().c_str() << std::endl;
548 return EX_SOFTWARE;
549 }
550 if (!installed) {
551 std::cerr << "No GSI is installed.\n";
552 return EX_USAGE;
553 }
554
555 int error;
556 status = gsid->zeroPartition("userdata" + std::string(kDsuPostfix), &error);
557 if (!status.isOk() || error) {
558 std::cerr << "Could not wipe GSI userdata: " << ErrorMessage(status, error) << "\n";
559 return EX_SOFTWARE;
560 }
561 return 0;
562 }
563
Status(sp<IGsiService> gsid,int argc,char **)564 static int Status(sp<IGsiService> gsid, int argc, char** /* argv */) {
565 if (argc > 1) {
566 std::cerr << "Unrecognized arguments to status." << std::endl;
567 return EX_USAGE;
568 }
569 bool running;
570 auto status = gsid->isGsiRunning(&running);
571 if (!status.isOk()) {
572 std::cerr << "error: " << status.exceptionMessage().c_str() << std::endl;
573 return EX_SOFTWARE;
574 } else if (running) {
575 std::cout << "running" << std::endl;
576 }
577 bool installed;
578 status = gsid->isGsiInstalled(&installed);
579 if (!status.isOk()) {
580 std::cerr << "error: " << status.exceptionMessage().c_str() << std::endl;
581 return EX_SOFTWARE;
582 } else if (installed) {
583 std::cout << "installed" << std::endl;
584 }
585 bool enabled;
586 status = gsid->isGsiEnabled(&enabled);
587 if (!status.isOk()) {
588 std::cerr << status.exceptionMessage().c_str() << std::endl;
589 return EX_SOFTWARE;
590 } else if (running || installed) {
591 std::cout << (enabled ? "enabled" : "disabled") << std::endl;
592 } else {
593 std::cout << "normal" << std::endl;
594 }
595 if (!IsRoot()) {
596 return 0;
597 }
598
599 std::vector<std::string> dsu_slots;
600 status = gsid->getInstalledDsuSlots(&dsu_slots);
601 if (!status.isOk()) {
602 std::cerr << status.exceptionMessage().c_str() << std::endl;
603 return EX_SOFTWARE;
604 }
605 int n = 0;
606 for (auto&& dsu_slot : dsu_slots) {
607 std::cout << "[" << n++ << "] " << dsu_slot << std::endl;
608 sp<IImageService> image_service = nullptr;
609 status = gsid->openImageService("dsu/" + dsu_slot + "/", &image_service);
610 if (!status.isOk()) {
611 if (running) {
612 // openImageService through binder (gsid) could fail if running,
613 // because we can't stat the "outside" userdata.
614 continue;
615 }
616 std::cerr << "error: " << status.exceptionMessage().c_str() << std::endl;
617 return EX_SOFTWARE;
618 }
619 std::vector<std::string> images;
620 status = image_service->getAllBackingImages(&images);
621 if (!status.isOk()) {
622 std::cerr << "error: " << status.exceptionMessage().c_str() << std::endl;
623 return EX_SOFTWARE;
624 }
625 for (auto&& image : images) {
626 std::cout << "installed: " << image << std::endl;
627 AvbPublicKey public_key;
628 int err = 0;
629 status = image_service->getAvbPublicKey(image, &public_key, &err);
630 std::cout << "AVB public key (sha1): ";
631 if (!public_key.bytes.empty()) {
632 for (auto b : public_key.sha1) {
633 std::cout << StringPrintf("%02x", b & 255);
634 }
635 std::cout << std::endl;
636 } else {
637 std::cout << "[NONE]" << std::endl;
638 }
639 }
640 }
641 return 0;
642 }
643
Cancel(sp<IGsiService> gsid,int,char **)644 static int Cancel(sp<IGsiService> gsid, int /* argc */, char** /* argv */) {
645 bool cancelled = false;
646 auto status = gsid->cancelGsiInstall(&cancelled);
647 if (!status.isOk()) {
648 std::cerr << status.exceptionMessage().c_str() << std::endl;
649 return EX_SOFTWARE;
650 }
651 if (!cancelled) {
652 std::cout << "Fail to cancel the installation." << std::endl;
653 return EX_SOFTWARE;
654 }
655 return 0;
656 }
657
Enable(sp<IGsiService> gsid,int argc,char ** argv)658 static int Enable(sp<IGsiService> gsid, int argc, char** argv) {
659 bool one_shot = false;
660 std::string dsuSlot = {};
661 struct option options[] = {
662 {"single-boot", no_argument, nullptr, 's'},
663 {"dsuslot", required_argument, nullptr, 'd'},
664 {nullptr, 0, nullptr, 0},
665 };
666 int rv, index;
667 while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
668 switch (rv) {
669 case 's':
670 one_shot = true;
671 break;
672 case 'd':
673 dsuSlot = optarg;
674 break;
675 default:
676 std::cerr << "Unrecognized argument to enable\n";
677 return EX_USAGE;
678 }
679 }
680
681 bool installed = false;
682 gsid->isGsiInstalled(&installed);
683 if (!installed) {
684 std::cerr << "Could not find GSI install to re-enable" << std::endl;
685 return EX_SOFTWARE;
686 }
687
688 bool installing = false;
689 gsid->isGsiInstallInProgress(&installing);
690 if (installing) {
691 std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
692 return EX_SOFTWARE;
693 }
694 if (dsuSlot.empty()) {
695 auto status = gsid->getActiveDsuSlot(&dsuSlot);
696 if (!status.isOk()) {
697 std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
698 return EX_SOFTWARE;
699 }
700 }
701 int error;
702 auto status = gsid->enableGsi(one_shot, dsuSlot, &error);
703 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
704 std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n";
705 return EX_SOFTWARE;
706 }
707 std::cout << "Live image install successfully enabled." << std::endl;
708 return 0;
709 }
710
Disable(sp<IGsiService> gsid,int argc,char **)711 static int Disable(sp<IGsiService> gsid, int argc, char** /* argv */) {
712 if (argc > 1) {
713 std::cerr << "Unrecognized arguments to disable." << std::endl;
714 return EX_USAGE;
715 }
716 bool installing = false;
717 gsid->isGsiInstallInProgress(&installing);
718 if (installing) {
719 std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
720 return EX_SOFTWARE;
721 }
722
723 bool ok = false;
724 gsid->disableGsi(&ok);
725 if (!ok) {
726 std::cerr << "Error disabling GSI" << std::endl;
727 return EX_SOFTWARE;
728 }
729 std::cout << "Live image install successfully disabled." << std::endl;
730 return 0;
731 }
732
usage(int,char * argv[])733 static int usage(int /* argc */, char* argv[]) {
734 fprintf(stderr,
735 "%s - command-line tool for installing GSI images.\n"
736 "\n"
737 "Usage:\n"
738 " %s <disable|install|wipe|status> [options]\n"
739 "\n"
740 " disable Disable the currently installed GSI.\n"
741 " enable [-s, --single-boot]\n"
742 " [-d, --dsuslot slotname]\n"
743 " Enable a previously disabled GSI.\n"
744 " install Install a new GSI. Specify the image size with\n"
745 " --gsi-size and the desired userdata size with\n"
746 " --userdata-size (the latter defaults to 8GiB)\n"
747 " --wipe (remove old gsi userdata first)\n"
748 " wipe Completely remove a GSI and its associated data\n"
749 " wipe-data Ensure the GSI's userdata will be formatted\n"
750 " cancel Cancel the installation\n"
751 " status Show status\n",
752 argv[0], argv[0]);
753 return EX_USAGE;
754 }
755
main(int argc,char ** argv)756 int main(int argc, char** argv) {
757 android::base::InitLogging(argv, android::base::StderrLogger, android::base::DefaultAborter);
758
759 // Start a threadpool to service waitForService() callbacks.
760 android::ProcessState::self()->startThreadPool();
761 android::sp<IGsiService> service = GetGsiService();
762 if (!service) {
763 return EX_SOFTWARE;
764 }
765
766 if (1 >= argc) {
767 std::cerr << "Expected command." << std::endl;
768 return EX_USAGE;
769 }
770
771 std::string command = argv[1];
772
773 int rc;
774 const auto& vec = kEnforceNonLockedDsu;
775 if (std::find(vec.begin(), vec.end(), command) != vec.end() &&
776 (rc = EnforceNonLockedDsu(service)) != 0) {
777 return rc;
778 }
779
780 auto iter = kCommandMap.find(command);
781 if (iter == kCommandMap.end()) {
782 std::cerr << "Unrecognized command: " << command << std::endl;
783 return usage(argc, argv);
784 }
785
786 rc = iter->second(service, argc - 1, argv + 1);
787 return rc;
788 }
789