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, ¶ms.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, ¶ms.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