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