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