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