blob: 4c246cca1800bb9c5a5f001b90c00200956405ba [file] [log] [blame]
David Andersonee84d742019-01-07 18:10:29 -08001//
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>
Howard Chen3f6d5a62019-08-22 15:26:33 +080020#include <unistd.h>
David Andersonee84d742019-01-07 18:10:29 -080021
David Andersonc053b3b2019-01-08 18:22:07 -080022#include <chrono>
David Anderson6a5b8a72019-01-16 16:24:48 -080023#include <condition_variable>
David Andersonee84d742019-01-07 18:10:29 -080024#include <functional>
25#include <iostream>
26#include <map>
David Anderson6a5b8a72019-01-16 16:24:48 -080027#include <mutex>
David Andersonee84d742019-01-07 18:10:29 -080028#include <string>
David Anderson6a5b8a72019-01-16 16:24:48 -080029#include <thread>
David Andersonee84d742019-01-07 18:10:29 -080030
David Anderson83fdeca2019-09-09 17:56:22 -070031#include <android-base/logging.h>
David Andersonee84d742019-01-07 18:10:29 -080032#include <android-base/parseint.h>
David Andersonc053b3b2019-01-08 18:22:07 -080033#include <android-base/properties.h>
Yo Chiang53bed1c2020-01-01 16:25:19 +080034#include <android-base/stringprintf.h>
Howard Chen4663de62019-11-05 20:46:20 +080035#include <android-base/strings.h>
David Andersonee84d742019-01-07 18:10:29 -080036#include <android-base/unique_fd.h>
David Andersonc053b3b2019-01-08 18:22:07 -080037#include <android/gsi/IGsiService.h>
David Andersonee84d742019-01-07 18:10:29 -080038#include <binder/IServiceManager.h>
David Andersonb33f2b12019-01-22 14:14:10 -080039#include <cutils/android_reboot.h>
David Andersonc053b3b2019-01-08 18:22:07 -080040#include <libgsi/libgsi.h>
David Anderson83fdeca2019-09-09 17:56:22 -070041#include <libgsi/libgsid.h>
David Andersonc053b3b2019-01-08 18:22:07 -080042
43using namespace android::gsi;
44using namespace std::chrono_literals;
David Andersonee84d742019-01-07 18:10:29 -080045
46using android::sp;
Howard Chen4663de62019-11-05 20:46:20 +080047using android::base::Split;
Yo Chiang53bed1c2020-01-01 16:25:19 +080048using android::base::StringPrintf;
David Andersonc053b3b2019-01-08 18:22:07 -080049using CommandCallback = std::function<int(sp<IGsiService>, int, char**)>;
David Andersonee84d742019-01-07 18:10:29 -080050
David Andersona141ba82019-01-14 19:09:27 -080051static int Disable(sp<IGsiService> gsid, int argc, char** argv);
52static int Enable(sp<IGsiService> gsid, int argc, char** argv);
David Andersonc053b3b2019-01-08 18:22:07 -080053static int Install(sp<IGsiService> gsid, int argc, char** argv);
54static int Wipe(sp<IGsiService> gsid, int argc, char** argv);
David Anderson8bdf6252019-06-11 16:43:24 -070055static int WipeData(sp<IGsiService> gsid, int argc, char** argv);
Howard Chenf08428e2019-01-23 14:54:51 +080056static int Status(sp<IGsiService> gsid, int argc, char** argv);
Howard Chenecbc0192019-02-25 18:51:26 +080057static int Cancel(sp<IGsiService> gsid, int argc, char** argv);
David Andersonee84d742019-01-07 18:10:29 -080058
59static const std::map<std::string, CommandCallback> kCommandMap = {
David Anderson5cc440c2019-04-16 14:34:27 -070060 // clang-format off
David Andersona141ba82019-01-14 19:09:27 -080061 {"disable", Disable},
62 {"enable", Enable},
David Andersonee84d742019-01-07 18:10:29 -080063 {"install", Install},
64 {"wipe", Wipe},
David Anderson8bdf6252019-06-11 16:43:24 -070065 {"wipe-data", WipeData},
Howard Chenf08428e2019-01-23 14:54:51 +080066 {"status", Status},
Howard Chenecbc0192019-02-25 18:51:26 +080067 {"cancel", Cancel},
David Anderson5cc440c2019-04-16 14:34:27 -070068 // clang-format on
David Andersonee84d742019-01-07 18:10:29 -080069};
70
David Anderson5cc440c2019-04-16 14:34:27 -070071static std::string ErrorMessage(const android::binder::Status& status,
72 int error_code = IGsiService::INSTALL_ERROR_GENERIC) {
David Anderson646b4b22019-02-27 18:26:54 -080073 if (!status.isOk()) {
74 return status.exceptionMessage().string();
75 }
76 return "error code " + std::to_string(error_code);
77}
78
David Anderson6a5b8a72019-01-16 16:24:48 -080079class ProgressBar {
80 public:
81 explicit ProgressBar(sp<IGsiService> gsid) : gsid_(gsid) {}
82
83 ~ProgressBar() { Stop(); }
84
85 void Display() {
David Andersonb0f3c822019-01-31 19:19:02 -080086 Finish();
David Anderson6a5b8a72019-01-16 16:24:48 -080087 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() {
David Andersonb0f3c822019-01-31 19:19:02 -0800102 if (!worker_) {
103 return;
104 }
David Anderson6a5b8a72019-01-16 16:24:48 -0800105 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() {
David Andersonb0f3c822019-01-31 19:19:02 -0800138 // If no bar was in progress, don't do anything.
139 if (last_update_.total_bytes == 0) {
140 return;
141 }
David Anderson6a5b8a72019-01-16 16:24:48 -0800142 // 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
David Andersonc053b3b2019-01-08 18:22:07 -0800193static int Install(sp<IGsiService> gsid, int argc, char** argv) {
Howard Chen18109b12019-08-13 17:00:44 +0800194 constexpr const char* kDefaultPartition = "system";
David Andersonee84d742019-01-07 18:10:29 -0800195 struct option options[] = {
David Andersoneb30ac22019-03-12 15:24:53 -0700196 {"install-dir", required_argument, nullptr, 'i'},
David Andersonee84d742019-01-07 18:10:29 -0800197 {"gsi-size", required_argument, nullptr, 's'},
David Andersonb33f2b12019-01-22 14:14:10 -0800198 {"no-reboot", no_argument, nullptr, 'n'},
David Andersonee84d742019-01-07 18:10:29 -0800199 {"userdata-size", required_argument, nullptr, 'u'},
Howard Chen18109b12019-08-13 17:00:44 +0800200 {"partition-name", required_argument, nullptr, 'p'},
David Andersona141ba82019-01-14 19:09:27 -0800201 {"wipe", no_argument, nullptr, 'w'},
David Andersonee84d742019-01-07 18:10:29 -0800202 {nullptr, 0, nullptr, 0},
203 };
204
Paul Trautrim3c23df22019-12-11 16:18:22 +0900205 int64_t gsiSize = 0;
206 int64_t userdataSize = 0;
Howard Chen18109b12019-08-13 17:00:44 +0800207 bool wipeUserdata = false;
David Andersonb33f2b12019-01-22 14:14:10 -0800208 bool reboot = true;
Howard Chen18109b12019-08-13 17:00:44 +0800209 std::string installDir = "";
210 std::string partition = kDefaultPartition;
David Andersonb5f44b32019-02-20 18:04:37 -0800211 if (getuid() != 0) {
212 std::cerr << "must be root to install a GSI" << std::endl;
213 return EX_NOPERM;
214 }
215
David Andersonee84d742019-01-07 18:10:29 -0800216 int rv, index;
217 while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
218 switch (rv) {
Howard Chen18109b12019-08-13 17:00:44 +0800219 case 'p':
220 partition = optarg;
221 break;
David Andersonee84d742019-01-07 18:10:29 -0800222 case 's':
Howard Chen18109b12019-08-13 17:00:44 +0800223 if (!android::base::ParseInt(optarg, &gsiSize) || gsiSize <= 0) {
David Anderson550253b2019-02-08 17:55:27 -0800224 std::cerr << "Could not parse image size: " << optarg << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800225 return EX_USAGE;
226 }
227 break;
228 case 'u':
Howard Chen18109b12019-08-13 17:00:44 +0800229 if (!android::base::ParseInt(optarg, &userdataSize) || userdataSize < 0) {
David Anderson550253b2019-02-08 17:55:27 -0800230 std::cerr << "Could not parse image size: " << optarg << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800231 return EX_USAGE;
232 }
233 break;
David Andersoneb30ac22019-03-12 15:24:53 -0700234 case 'i':
Howard Chen18109b12019-08-13 17:00:44 +0800235 installDir = optarg;
David Andersoneb30ac22019-03-12 15:24:53 -0700236 break;
David Andersona141ba82019-01-14 19:09:27 -0800237 case 'w':
Howard Chen18109b12019-08-13 17:00:44 +0800238 wipeUserdata = true;
David Andersona141ba82019-01-14 19:09:27 -0800239 break;
David Andersonb33f2b12019-01-22 14:14:10 -0800240 case 'n':
241 reboot = false;
242 break;
David Andersonee84d742019-01-07 18:10:29 -0800243 }
244 }
245
Howard Chen18109b12019-08-13 17:00:44 +0800246 if (gsiSize <= 0) {
David Anderson550253b2019-02-08 17:55:27 -0800247 std::cerr << "Must specify --gsi-size." << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800248 return EX_USAGE;
249 }
250
David Andersona78f8112019-01-17 11:50:50 -0800251 bool running_gsi = false;
252 gsid->isGsiRunning(&running_gsi);
253 if (running_gsi) {
David Anderson550253b2019-02-08 17:55:27 -0800254 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;
David Andersona78f8112019-01-17 11:50:50 -0800256 return EX_SOFTWARE;
257 }
258
David Andersonee84d742019-01-07 18:10:29 -0800259 android::base::unique_fd input(dup(1));
260 if (input < 0) {
David Anderson550253b2019-02-08 17:55:27 -0800261 std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800262 return EX_SOFTWARE;
263 }
David Anderson6a5b8a72019-01-16 16:24:48 -0800264 // Note: the progress bar needs to be re-started in between each call.
265 ProgressBar progress(gsid);
David Anderson6a5b8a72019-01-16 16:24:48 -0800266 progress.Display();
David Anderson868dea52019-01-17 13:34:38 -0800267 int error;
Howard Chen4663de62019-11-05 20:46:20 +0800268 auto status = gsid->openInstall(installDir, &error);
269 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
Yo Chiang99b12d02019-12-02 15:47:57 +0800270 std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error) << "\n";
Howard Chen4663de62019-11-05 20:46:20 +0800271 return EX_SOFTWARE;
272 }
Howard Chen18109b12019-08-13 17:00:44 +0800273 if (partition == kDefaultPartition) {
Howard Chen4663de62019-11-05 20:46:20 +0800274 auto status = gsid->createPartition("userdata", userdataSize, false, &error);
Howard Chen18109b12019-08-13 17:00:44 +0800275 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 }
Howard Chen18109b12019-08-13 17:00:44 +0800281
Howard Chen4663de62019-11-05 20:46:20 +0800282 status = gsid->createPartition(partition, gsiSize, true, &error);
David Anderson868dea52019-01-17 13:34:38 -0800283 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
David Anderson646b4b22019-02-27 18:26:54 -0800284 std::cerr << "Could not start live image install: " << ErrorMessage(status, error) << "\n";
David Andersonee84d742019-01-07 18:10:29 -0800285 return EX_SOFTWARE;
286 }
David Anderson1d94d262019-01-11 20:39:51 -0800287 android::os::ParcelFileDescriptor stream(std::move(input));
288
David Anderson868dea52019-01-17 13:34:38 -0800289 bool ok = false;
David Anderson6a5b8a72019-01-16 16:24:48 -0800290 progress.Display();
Howard Chen4663de62019-11-05 20:46:20 +0800291 status = gsid->commitGsiChunkFromStream(stream, gsiSize, &ok);
David Anderson6a5b8a72019-01-16 16:24:48 -0800292 if (!ok) {
David Anderson646b4b22019-02-27 18:26:54 -0800293 std::cerr << "Could not commit live image data: " << ErrorMessage(status) << "\n";
David Andersonee84d742019-01-07 18:10:29 -0800294 return EX_SOFTWARE;
295 }
296
Howard Chen4663de62019-11-05 20:46:20 +0800297 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 }
David Anderson6a5b8a72019-01-16 16:24:48 -0800302 progress.Finish();
Howard Chenee5c2b12019-11-08 11:57:47 +0800303 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);
David Anderson868dea52019-01-17 13:34:38 -0800310 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
David Anderson646b4b22019-02-27 18:26:54 -0800311 std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n";
David Andersonee84d742019-01-07 18:10:29 -0800312 return EX_SOFTWARE;
313 }
David Andersonb33f2b12019-01-22 14:14:10 -0800314
315 if (reboot) {
316 if (!android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,adb")) {
David Anderson550253b2019-02-08 17:55:27 -0800317 std::cerr << "Failed to reboot automatically" << std::endl;
David Andersonb33f2b12019-01-22 14:14:10 -0800318 return EX_SOFTWARE;
319 }
320 } else {
321 std::cout << "Please reboot to use the GSI." << std::endl;
322 }
David Andersonee84d742019-01-07 18:10:29 -0800323 return 0;
324}
325
David Andersonc053b3b2019-01-08 18:22:07 -0800326static int Wipe(sp<IGsiService> gsid, int argc, char** /* argv */) {
David Andersonee84d742019-01-07 18:10:29 -0800327 if (argc > 1) {
David Anderson550253b2019-02-08 17:55:27 -0800328 std::cerr << "Unrecognized arguments to wipe." << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800329 return EX_USAGE;
330 }
David Andersonc053b3b2019-01-08 18:22:07 -0800331 bool ok;
Howard Chen25b18cc2019-08-02 11:21:58 +0800332 auto status = gsid->removeGsi(&ok);
David Andersonc053b3b2019-01-08 18:22:07 -0800333 if (!status.isOk() || !ok) {
David Anderson646b4b22019-02-27 18:26:54 -0800334 std::cerr << "Could not remove GSI install: " << ErrorMessage(status) << "\n";
David Andersonee84d742019-01-07 18:10:29 -0800335 return EX_SOFTWARE;
336 }
David Anderson5f805912019-03-07 12:41:15 -0800337
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 }
David Andersonee84d742019-01-07 18:10:29 -0800344 return 0;
345}
346
David Anderson8bdf6252019-06-11 16:43:24 -0700347static 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;
Howard Chen423129b2020-03-03 13:28:37 +0800376 status = gsid->zeroPartition("userdata" + std::string(kDsuPostfix), &error);
David Anderson8bdf6252019-06-11 16:43:24 -0700377 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
Howard Chenf08428e2019-01-23 14:54:51 +0800384static int Status(sp<IGsiService> gsid, int argc, char** /* argv */) {
385 if (argc > 1) {
David Anderson550253b2019-02-08 17:55:27 -0800386 std::cerr << "Unrecognized arguments to status." << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800387 return EX_USAGE;
388 }
389 bool running;
390 auto status = gsid->isGsiRunning(&running);
391 if (!status.isOk()) {
David Anderson646b4b22019-02-27 18:26:54 -0800392 std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800393 return EX_SOFTWARE;
394 } else if (running) {
395 std::cout << "running" << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800396 }
397 bool installed;
398 status = gsid->isGsiInstalled(&installed);
399 if (!status.isOk()) {
David Anderson646b4b22019-02-27 18:26:54 -0800400 std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800401 return EX_SOFTWARE;
402 } else if (installed) {
403 std::cout << "installed" << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800404 }
Howard Chen670b3062019-02-26 18:14:47 +0800405 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 }
Howard Chen96c31fd2019-08-23 17:38:51 +0800415 if (getuid() != 0) {
416 return 0;
417 }
Howard Chen96c31fd2019-08-23 17:38:51 +0800418
Howard Chenee5c2b12019-11-08 11:57:47 +0800419 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;
Yo Chiang53bed1c2020-01-01 16:25:19 +0800442 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 }
Howard Chenee5c2b12019-11-08 11:57:47 +0800454 }
Howard Chen96c31fd2019-08-23 17:38:51 +0800455 }
Howard Chenf08428e2019-01-23 14:54:51 +0800456 return 0;
457}
458
Howard Chenecbc0192019-02-25 18:51:26 +0800459static 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
David Anderson564a04c2019-02-27 13:33:44 -0800473static int Enable(sp<IGsiService> gsid, int argc, char** argv) {
474 bool one_shot = false;
Howard Chenee5c2b12019-11-08 11:57:47 +0800475 std::string dsuSlot = {};
David Anderson564a04c2019-02-27 13:33:44 -0800476 struct option options[] = {
477 {"single-boot", no_argument, nullptr, 's'},
Howard Chenee5c2b12019-11-08 11:57:47 +0800478 {"dsuslot", required_argument, nullptr, 'd'},
David Anderson564a04c2019-02-27 13:33:44 -0800479 {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;
Howard Chenee5c2b12019-11-08 11:57:47 +0800487 case 'd':
488 dsuSlot = optarg;
489 break;
David Anderson564a04c2019-02-27 13:33:44 -0800490 default:
491 std::cerr << "Unrecognized argument to enable\n";
492 return EX_USAGE;
493 }
David Andersona141ba82019-01-14 19:09:27 -0800494 }
495
496 bool installed = false;
497 gsid->isGsiInstalled(&installed);
498 if (!installed) {
David Anderson550253b2019-02-08 17:55:27 -0800499 std::cerr << "Could not find GSI install to re-enable" << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800500 return EX_SOFTWARE;
501 }
502
503 bool installing = false;
504 gsid->isGsiInstallInProgress(&installing);
505 if (installing) {
David Anderson550253b2019-02-08 17:55:27 -0800506 std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800507 return EX_SOFTWARE;
508 }
Howard Chenee5c2b12019-11-08 11:57:47 +0800509 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 }
David Anderson868dea52019-01-17 13:34:38 -0800516 int error;
Howard Chenee5c2b12019-11-08 11:57:47 +0800517 auto status = gsid->enableGsi(one_shot, dsuSlot, &error);
David Anderson868dea52019-01-17 13:34:38 -0800518 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
David Anderson646b4b22019-02-27 18:26:54 -0800519 std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n";
David Andersona141ba82019-01-14 19:09:27 -0800520 return EX_SOFTWARE;
521 }
522 std::cout << "Live image install successfully enabled." << std::endl;
523 return 0;
524}
525
526static int Disable(sp<IGsiService> gsid, int argc, char** /* argv */) {
527 if (argc > 1) {
David Anderson550253b2019-02-08 17:55:27 -0800528 std::cerr << "Unrecognized arguments to disable." << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800529 return EX_USAGE;
530 }
531
532 bool installing = false;
533 gsid->isGsiInstallInProgress(&installing);
534 if (installing) {
David Anderson550253b2019-02-08 17:55:27 -0800535 std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800536 return EX_SOFTWARE;
537 }
538
539 bool ok = false;
Howard Chen25b18cc2019-08-02 11:21:58 +0800540 gsid->disableGsi(&ok);
David Andersona141ba82019-01-14 19:09:27 -0800541 if (!ok) {
David Anderson550253b2019-02-08 17:55:27 -0800542 std::cerr << "Error disabling GSI" << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800543 return EX_SOFTWARE;
544 }
545 std::cout << "Live image install successfully disabled." << std::endl;
546 return 0;
547}
548
549static int usage(int /* argc */, char* argv[]) {
550 fprintf(stderr,
551 "%s - command-line tool for installing GSI images.\n"
552 "\n"
553 "Usage:\n"
Howard Chenf08428e2019-01-23 14:54:51 +0800554 " %s <disable|install|wipe|status> [options]\n"
David Andersona141ba82019-01-14 19:09:27 -0800555 "\n"
556 " disable Disable the currently installed GSI.\n"
Howard Chenee5c2b12019-11-08 11:57:47 +0800557 " enable [-s, --single-boot]\n"
558 " [-d, --dsuslot slotname]\n"
David Anderson564a04c2019-02-27 13:33:44 -0800559 " Enable a previously disabled GSI.\n"
David Andersona141ba82019-01-14 19:09:27 -0800560 " 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"
Howard Chenf08428e2019-01-23 14:54:51 +0800564 " wipe Completely remove a GSI and its associated data\n"
David Anderson8bdf6252019-06-11 16:43:24 -0700565 " wipe-data Ensure the GSI's userdata will be formatted\n"
Howard Chenecbc0192019-02-25 18:51:26 +0800566 " cancel Cancel the installation\n"
567 " status Show status\n",
David Andersona141ba82019-01-14 19:09:27 -0800568 argv[0], argv[0]);
569 return EX_USAGE;
570}
571
David Andersonee84d742019-01-07 18:10:29 -0800572int main(int argc, char** argv) {
David Anderson83fdeca2019-09-09 17:56:22 -0700573 android::base::InitLogging(argv, android::base::StdioLogger, android::base::DefaultAborter);
David Andersonee84d742019-01-07 18:10:29 -0800574
David Anderson83fdeca2019-09-09 17:56:22 -0700575 android::sp<IGsiService> service = GetGsiService();
576 if (!service) {
David Anderson4c756732019-07-12 14:18:37 -0700577 return EX_SOFTWARE;
578 }
579
David Andersonee84d742019-01-07 18:10:29 -0800580 if (1 >= argc) {
David Anderson550253b2019-02-08 17:55:27 -0800581 std::cerr << "Expected command." << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800582 return EX_USAGE;
583 }
584
585 std::string command = argv[1];
David Anderson34502be2019-02-01 18:06:20 -0800586
David Andersonee84d742019-01-07 18:10:29 -0800587 auto iter = kCommandMap.find(command);
588 if (iter == kCommandMap.end()) {
David Anderson550253b2019-02-08 17:55:27 -0800589 std::cerr << "Unrecognized command: " << command << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800590 return usage(argc, argv);
David Andersonee84d742019-01-07 18:10:29 -0800591 }
David Andersonc053b3b2019-01-08 18:22:07 -0800592
David Anderson4c756732019-07-12 14:18:37 -0700593 int rc = iter->second(service, argc - 1, argv + 1);
David Andersonc053b3b2019-01-08 18:22:07 -0800594 return rc;
David Andersonee84d742019-01-07 18:10:29 -0800595}