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