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