blob: b059947fe1649133694d906a193f9854f74ee3a5 [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 Anderson4c756732019-07-12 14:18:37 -070038#include <android/gsi/IGsid.h>
David Andersonee84d742019-01-07 18:10:29 -080039#include <binder/IServiceManager.h>
David Andersonb33f2b12019-01-22 14:14:10 -080040#include <cutils/android_reboot.h>
David Andersonc053b3b2019-01-08 18:22:07 -080041#include <libgsi/libgsi.h>
David Anderson83fdeca2019-09-09 17:56:22 -070042#include <libgsi/libgsid.h>
David Andersonc053b3b2019-01-08 18:22:07 -080043
44using namespace android::gsi;
45using namespace std::chrono_literals;
David Andersonee84d742019-01-07 18:10:29 -080046
47using android::sp;
Howard Chen4663de62019-11-05 20:46:20 +080048using android::base::Split;
Yo Chiang53bed1c2020-01-01 16:25:19 +080049using android::base::StringPrintf;
David Andersonc053b3b2019-01-08 18:22:07 -080050using CommandCallback = std::function<int(sp<IGsiService>, int, char**)>;
David Andersonee84d742019-01-07 18:10:29 -080051
David Andersona141ba82019-01-14 19:09:27 -080052static int Disable(sp<IGsiService> gsid, int argc, char** argv);
53static int Enable(sp<IGsiService> gsid, int argc, char** argv);
David Andersonc053b3b2019-01-08 18:22:07 -080054static int Install(sp<IGsiService> gsid, int argc, char** argv);
55static int Wipe(sp<IGsiService> gsid, int argc, char** argv);
David Anderson8bdf6252019-06-11 16:43:24 -070056static int WipeData(sp<IGsiService> gsid, int argc, char** argv);
Howard Chenf08428e2019-01-23 14:54:51 +080057static int Status(sp<IGsiService> gsid, int argc, char** argv);
Howard Chenecbc0192019-02-25 18:51:26 +080058static int Cancel(sp<IGsiService> gsid, int argc, char** argv);
David Andersonee84d742019-01-07 18:10:29 -080059
60static const std::map<std::string, CommandCallback> kCommandMap = {
David Anderson5cc440c2019-04-16 14:34:27 -070061 // clang-format off
David Andersona141ba82019-01-14 19:09:27 -080062 {"disable", Disable},
63 {"enable", Enable},
David Andersonee84d742019-01-07 18:10:29 -080064 {"install", Install},
65 {"wipe", Wipe},
David Anderson8bdf6252019-06-11 16:43:24 -070066 {"wipe-data", WipeData},
Howard Chenf08428e2019-01-23 14:54:51 +080067 {"status", Status},
Howard Chenecbc0192019-02-25 18:51:26 +080068 {"cancel", Cancel},
David Anderson5cc440c2019-04-16 14:34:27 -070069 // clang-format on
David Andersonee84d742019-01-07 18:10:29 -080070};
71
David Anderson5cc440c2019-04-16 14:34:27 -070072static std::string ErrorMessage(const android::binder::Status& status,
73 int error_code = IGsiService::INSTALL_ERROR_GENERIC) {
David Anderson646b4b22019-02-27 18:26:54 -080074 if (!status.isOk()) {
75 return status.exceptionMessage().string();
76 }
77 return "error code " + std::to_string(error_code);
78}
79
David Anderson6a5b8a72019-01-16 16:24:48 -080080class ProgressBar {
81 public:
82 explicit ProgressBar(sp<IGsiService> gsid) : gsid_(gsid) {}
83
84 ~ProgressBar() { Stop(); }
85
86 void Display() {
David Andersonb0f3c822019-01-31 19:19:02 -080087 Finish();
David Anderson6a5b8a72019-01-16 16:24:48 -080088 done_ = false;
89 last_update_ = {};
90 worker_ = std::make_unique<std::thread>([this]() { Worker(); });
91 }
92
93 void Stop() {
94 if (!worker_) {
95 return;
96 }
97 SignalDone();
98 worker_->join();
99 worker_ = nullptr;
100 }
101
102 void Finish() {
David Andersonb0f3c822019-01-31 19:19:02 -0800103 if (!worker_) {
104 return;
105 }
David Anderson6a5b8a72019-01-16 16:24:48 -0800106 Stop();
107 FinishLastBar();
108 }
109
110 private:
111 void Worker() {
112 std::unique_lock<std::mutex> lock(mutex_);
113 while (!done_) {
114 if (!UpdateProgress()) {
115 return;
116 }
117 cv_.wait_for(lock, 500ms, [this] { return done_; });
118 }
119 }
120
121 bool UpdateProgress() {
122 GsiProgress latest;
123 auto status = gsid_->getInstallProgress(&latest);
124 if (!status.isOk()) {
125 std::cout << std::endl;
126 return false;
127 }
128 if (latest.status == IGsiService::STATUS_NO_OPERATION) {
129 return true;
130 }
131 if (last_update_.step != latest.step) {
132 FinishLastBar();
133 }
134 Display(latest);
135 return true;
136 }
137
138 void FinishLastBar() {
David Andersonb0f3c822019-01-31 19:19:02 -0800139 // If no bar was in progress, don't do anything.
140 if (last_update_.total_bytes == 0) {
141 return;
142 }
David Anderson6a5b8a72019-01-16 16:24:48 -0800143 // Ensure we finish the display at 100%.
144 last_update_.bytes_processed = last_update_.total_bytes;
145 Display(last_update_);
146 std::cout << std::endl;
147 }
148
149 void Display(const GsiProgress& progress) {
150 if (progress.total_bytes == 0) {
151 return;
152 }
153
154 static constexpr int kColumns = 80;
155 static constexpr char kRedColor[] = "\x1b[31m";
156 static constexpr char kGreenColor[] = "\x1b[32m";
157 static constexpr char kResetColor[] = "\x1b[0m";
158
159 int percentage = (progress.bytes_processed * 100) / progress.total_bytes;
160 int64_t bytes_per_col = progress.total_bytes / kColumns;
161 uint32_t fill_count = progress.bytes_processed / bytes_per_col;
162 uint32_t dash_count = kColumns - fill_count;
163 std::string fills = std::string(fill_count, '=');
164 std::string dashes = std::string(dash_count, '-');
165
166 // Give the end of the bar some flare.
167 if (!fills.empty() && !dashes.empty()) {
168 fills[fills.size() - 1] = '>';
169 }
170
171 fprintf(stdout, "\r%-15s%6d%% ", progress.step.c_str(), percentage);
172 fprintf(stdout, "%s[%s%s%s", kGreenColor, fills.c_str(), kRedColor, dashes.c_str());
173 fprintf(stdout, "%s]%s", kGreenColor, kResetColor);
174 fflush(stdout);
175
176 last_update_ = progress;
177 }
178
179 void SignalDone() {
180 std::lock_guard<std::mutex> guard(mutex_);
181 done_ = true;
182 cv_.notify_all();
183 }
184
185 private:
186 sp<IGsiService> gsid_;
187 std::unique_ptr<std::thread> worker_;
188 std::condition_variable cv_;
189 std::mutex mutex_;
190 GsiProgress last_update_;
191 bool done_ = false;
192};
193
David Andersonc053b3b2019-01-08 18:22:07 -0800194static int Install(sp<IGsiService> gsid, int argc, char** argv) {
Howard Chen18109b12019-08-13 17:00:44 +0800195 constexpr const char* kDefaultPartition = "system";
David Andersonee84d742019-01-07 18:10:29 -0800196 struct option options[] = {
David Andersoneb30ac22019-03-12 15:24:53 -0700197 {"install-dir", required_argument, nullptr, 'i'},
David Andersonee84d742019-01-07 18:10:29 -0800198 {"gsi-size", required_argument, nullptr, 's'},
David Andersonb33f2b12019-01-22 14:14:10 -0800199 {"no-reboot", no_argument, nullptr, 'n'},
David Andersonee84d742019-01-07 18:10:29 -0800200 {"userdata-size", required_argument, nullptr, 'u'},
Howard Chen18109b12019-08-13 17:00:44 +0800201 {"partition-name", required_argument, nullptr, 'p'},
David Andersona141ba82019-01-14 19:09:27 -0800202 {"wipe", no_argument, nullptr, 'w'},
David Andersonee84d742019-01-07 18:10:29 -0800203 {nullptr, 0, nullptr, 0},
204 };
205
Paul Trautrim3c23df22019-12-11 16:18:22 +0900206 int64_t gsiSize = 0;
207 int64_t userdataSize = 0;
Howard Chen18109b12019-08-13 17:00:44 +0800208 bool wipeUserdata = false;
David Andersonb33f2b12019-01-22 14:14:10 -0800209 bool reboot = true;
Howard Chen18109b12019-08-13 17:00:44 +0800210 std::string installDir = "";
211 std::string partition = kDefaultPartition;
David Andersonb5f44b32019-02-20 18:04:37 -0800212 if (getuid() != 0) {
213 std::cerr << "must be root to install a GSI" << std::endl;
214 return EX_NOPERM;
215 }
216
David Andersonee84d742019-01-07 18:10:29 -0800217 int rv, index;
218 while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
219 switch (rv) {
Howard Chen18109b12019-08-13 17:00:44 +0800220 case 'p':
221 partition = optarg;
222 break;
David Andersonee84d742019-01-07 18:10:29 -0800223 case 's':
Howard Chen18109b12019-08-13 17:00:44 +0800224 if (!android::base::ParseInt(optarg, &gsiSize) || gsiSize <= 0) {
David Anderson550253b2019-02-08 17:55:27 -0800225 std::cerr << "Could not parse image size: " << optarg << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800226 return EX_USAGE;
227 }
228 break;
229 case 'u':
Howard Chen18109b12019-08-13 17:00:44 +0800230 if (!android::base::ParseInt(optarg, &userdataSize) || userdataSize < 0) {
David Anderson550253b2019-02-08 17:55:27 -0800231 std::cerr << "Could not parse image size: " << optarg << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800232 return EX_USAGE;
233 }
234 break;
David Andersoneb30ac22019-03-12 15:24:53 -0700235 case 'i':
Howard Chen18109b12019-08-13 17:00:44 +0800236 installDir = optarg;
David Andersoneb30ac22019-03-12 15:24:53 -0700237 break;
David Andersona141ba82019-01-14 19:09:27 -0800238 case 'w':
Howard Chen18109b12019-08-13 17:00:44 +0800239 wipeUserdata = true;
David Andersona141ba82019-01-14 19:09:27 -0800240 break;
David Andersonb33f2b12019-01-22 14:14:10 -0800241 case 'n':
242 reboot = false;
243 break;
David Andersonee84d742019-01-07 18:10:29 -0800244 }
245 }
246
Howard Chen18109b12019-08-13 17:00:44 +0800247 if (gsiSize <= 0) {
David Anderson550253b2019-02-08 17:55:27 -0800248 std::cerr << "Must specify --gsi-size." << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800249 return EX_USAGE;
250 }
251
David Andersona78f8112019-01-17 11:50:50 -0800252 bool running_gsi = false;
253 gsid->isGsiRunning(&running_gsi);
254 if (running_gsi) {
David Anderson550253b2019-02-08 17:55:27 -0800255 std::cerr << "Cannot install a GSI within a live GSI." << std::endl;
256 std::cerr << "Use gsi_tool disable or wipe and reboot first." << std::endl;
David Andersona78f8112019-01-17 11:50:50 -0800257 return EX_SOFTWARE;
258 }
259
David Andersonee84d742019-01-07 18:10:29 -0800260 android::base::unique_fd input(dup(1));
261 if (input < 0) {
David Anderson550253b2019-02-08 17:55:27 -0800262 std::cerr << "Error duplicating descriptor: " << strerror(errno) << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800263 return EX_SOFTWARE;
264 }
David Anderson6a5b8a72019-01-16 16:24:48 -0800265 // Note: the progress bar needs to be re-started in between each call.
266 ProgressBar progress(gsid);
David Anderson6a5b8a72019-01-16 16:24:48 -0800267 progress.Display();
David Anderson868dea52019-01-17 13:34:38 -0800268 int error;
Howard Chen4663de62019-11-05 20:46:20 +0800269 auto status = gsid->openInstall(installDir, &error);
270 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
Yo Chiang99b12d02019-12-02 15:47:57 +0800271 std::cerr << "Could not open DSU installation: " << ErrorMessage(status, error) << "\n";
Howard Chen4663de62019-11-05 20:46:20 +0800272 return EX_SOFTWARE;
273 }
Howard Chen18109b12019-08-13 17:00:44 +0800274 if (partition == kDefaultPartition) {
Howard Chen4663de62019-11-05 20:46:20 +0800275 auto status = gsid->createPartition("userdata", userdataSize, false, &error);
Howard Chen18109b12019-08-13 17:00:44 +0800276 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
277 std::cerr << "Could not start live image install: " << ErrorMessage(status, error)
278 << "\n";
279 return EX_SOFTWARE;
280 }
281 }
Howard Chen18109b12019-08-13 17:00:44 +0800282
Howard Chen4663de62019-11-05 20:46:20 +0800283 status = gsid->createPartition(partition, gsiSize, true, &error);
David Anderson868dea52019-01-17 13:34:38 -0800284 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
David Anderson646b4b22019-02-27 18:26:54 -0800285 std::cerr << "Could not start live image install: " << ErrorMessage(status, error) << "\n";
David Andersonee84d742019-01-07 18:10:29 -0800286 return EX_SOFTWARE;
287 }
David Anderson1d94d262019-01-11 20:39:51 -0800288 android::os::ParcelFileDescriptor stream(std::move(input));
289
David Anderson868dea52019-01-17 13:34:38 -0800290 bool ok = false;
David Anderson6a5b8a72019-01-16 16:24:48 -0800291 progress.Display();
Howard Chen4663de62019-11-05 20:46:20 +0800292 status = gsid->commitGsiChunkFromStream(stream, gsiSize, &ok);
David Anderson6a5b8a72019-01-16 16:24:48 -0800293 if (!ok) {
David Anderson646b4b22019-02-27 18:26:54 -0800294 std::cerr << "Could not commit live image data: " << ErrorMessage(status) << "\n";
David Andersonee84d742019-01-07 18:10:29 -0800295 return EX_SOFTWARE;
296 }
297
Howard Chen4663de62019-11-05 20:46:20 +0800298 status = gsid->closeInstall(&error);
299 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
300 std::cerr << "Could not close DSU installation: " << ErrorMessage(status, error) << "\n";
301 return EX_SOFTWARE;
302 }
David Anderson6a5b8a72019-01-16 16:24:48 -0800303 progress.Finish();
Howard Chenee5c2b12019-11-08 11:57:47 +0800304 std::string dsuSlot;
305 status = gsid->getActiveDsuSlot(&dsuSlot);
306 if (!status.isOk()) {
307 std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
308 return EX_SOFTWARE;
309 }
310 status = gsid->enableGsi(true, dsuSlot, &error);
David Anderson868dea52019-01-17 13:34:38 -0800311 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
David Anderson646b4b22019-02-27 18:26:54 -0800312 std::cerr << "Could not make live image bootable: " << ErrorMessage(status, error) << "\n";
David Andersonee84d742019-01-07 18:10:29 -0800313 return EX_SOFTWARE;
314 }
David Andersonb33f2b12019-01-22 14:14:10 -0800315
316 if (reboot) {
317 if (!android::base::SetProperty(ANDROID_RB_PROPERTY, "reboot,adb")) {
David Anderson550253b2019-02-08 17:55:27 -0800318 std::cerr << "Failed to reboot automatically" << std::endl;
David Andersonb33f2b12019-01-22 14:14:10 -0800319 return EX_SOFTWARE;
320 }
321 } else {
322 std::cout << "Please reboot to use the GSI." << std::endl;
323 }
David Andersonee84d742019-01-07 18:10:29 -0800324 return 0;
325}
326
David Andersonc053b3b2019-01-08 18:22:07 -0800327static int Wipe(sp<IGsiService> gsid, int argc, char** /* argv */) {
David Andersonee84d742019-01-07 18:10:29 -0800328 if (argc > 1) {
David Anderson550253b2019-02-08 17:55:27 -0800329 std::cerr << "Unrecognized arguments to wipe." << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800330 return EX_USAGE;
331 }
David Andersonc053b3b2019-01-08 18:22:07 -0800332 bool ok;
Howard Chen25b18cc2019-08-02 11:21:58 +0800333 auto status = gsid->removeGsi(&ok);
David Andersonc053b3b2019-01-08 18:22:07 -0800334 if (!status.isOk() || !ok) {
David Anderson646b4b22019-02-27 18:26:54 -0800335 std::cerr << "Could not remove GSI install: " << ErrorMessage(status) << "\n";
David Andersonee84d742019-01-07 18:10:29 -0800336 return EX_SOFTWARE;
337 }
David Anderson5f805912019-03-07 12:41:15 -0800338
339 bool running = false;
340 if (gsid->isGsiRunning(&running).isOk() && running) {
341 std::cout << "Live image install will be removed next reboot." << std::endl;
342 } else {
343 std::cout << "Live image install successfully removed." << std::endl;
344 }
David Andersonee84d742019-01-07 18:10:29 -0800345 return 0;
346}
347
David Anderson8bdf6252019-06-11 16:43:24 -0700348static int WipeData(sp<IGsiService> gsid, int argc, char** /* argv */) {
349 if (argc > 1) {
350 std::cerr << "Unrecognized arguments to wipe-data.\n";
351 return EX_USAGE;
352 }
353
354 bool running;
355 auto status = gsid->isGsiRunning(&running);
356 if (!status.isOk()) {
357 std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
358 return EX_SOFTWARE;
359 }
360 if (running) {
361 std::cerr << "Cannot wipe GSI userdata while running a GSI.\n";
362 return EX_USAGE;
363 }
364
365 bool installed;
366 status = gsid->isGsiInstalled(&installed);
367 if (!status.isOk()) {
368 std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
369 return EX_SOFTWARE;
370 }
371 if (!installed) {
372 std::cerr << "No GSI is installed.\n";
373 return EX_USAGE;
374 }
375
376 int error;
Howard Chen423129b2020-03-03 13:28:37 +0800377 status = gsid->zeroPartition("userdata" + std::string(kDsuPostfix), &error);
David Anderson8bdf6252019-06-11 16:43:24 -0700378 if (!status.isOk() || error) {
379 std::cerr << "Could not wipe GSI userdata: " << ErrorMessage(status, error) << "\n";
380 return EX_SOFTWARE;
381 }
382 return 0;
383}
384
Howard Chenf08428e2019-01-23 14:54:51 +0800385static int Status(sp<IGsiService> gsid, int argc, char** /* argv */) {
386 if (argc > 1) {
David Anderson550253b2019-02-08 17:55:27 -0800387 std::cerr << "Unrecognized arguments to status." << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800388 return EX_USAGE;
389 }
390 bool running;
391 auto status = gsid->isGsiRunning(&running);
392 if (!status.isOk()) {
David Anderson646b4b22019-02-27 18:26:54 -0800393 std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800394 return EX_SOFTWARE;
395 } else if (running) {
396 std::cout << "running" << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800397 }
398 bool installed;
399 status = gsid->isGsiInstalled(&installed);
400 if (!status.isOk()) {
David Anderson646b4b22019-02-27 18:26:54 -0800401 std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800402 return EX_SOFTWARE;
403 } else if (installed) {
404 std::cout << "installed" << std::endl;
Howard Chenf08428e2019-01-23 14:54:51 +0800405 }
Howard Chen670b3062019-02-26 18:14:47 +0800406 bool enabled;
407 status = gsid->isGsiEnabled(&enabled);
408 if (!status.isOk()) {
409 std::cerr << status.exceptionMessage().string() << std::endl;
410 return EX_SOFTWARE;
411 } else if (running || installed) {
412 std::cout << (enabled ? "enabled" : "disabled") << std::endl;
413 } else {
414 std::cout << "normal" << std::endl;
415 }
Howard Chen96c31fd2019-08-23 17:38:51 +0800416 if (getuid() != 0) {
417 return 0;
418 }
Howard Chen96c31fd2019-08-23 17:38:51 +0800419
Howard Chenee5c2b12019-11-08 11:57:47 +0800420 std::vector<std::string> dsu_slots;
421 status = gsid->getInstalledDsuSlots(&dsu_slots);
422 if (!status.isOk()) {
423 std::cerr << status.exceptionMessage().string() << std::endl;
424 return EX_SOFTWARE;
425 }
426 int n = 0;
427 for (auto&& dsu_slot : dsu_slots) {
428 std::cout << "[" << n++ << "] " << dsu_slot << std::endl;
429 sp<IImageService> image_service = nullptr;
430 status = gsid->openImageService("dsu/" + dsu_slot + "/", &image_service);
431 if (!status.isOk()) {
432 std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
433 return EX_SOFTWARE;
434 }
435 std::vector<std::string> images;
436 status = image_service->getAllBackingImages(&images);
437 if (!status.isOk()) {
438 std::cerr << "error: " << status.exceptionMessage().string() << std::endl;
439 return EX_SOFTWARE;
440 }
441 for (auto&& image : images) {
442 std::cout << "installed: " << image << std::endl;
Yo Chiang53bed1c2020-01-01 16:25:19 +0800443 AvbPublicKey public_key;
444 int err = 0;
445 status = image_service->getAvbPublicKey(image, &public_key, &err);
446 std::cout << "AVB public key (sha1): ";
447 if (!public_key.bytes.empty()) {
448 for (auto b : public_key.sha1) {
449 std::cout << StringPrintf("%02x", b & 255);
450 }
451 std::cout << std::endl;
452 } else {
453 std::cout << "[NONE]" << std::endl;
454 }
Howard Chenee5c2b12019-11-08 11:57:47 +0800455 }
Howard Chen96c31fd2019-08-23 17:38:51 +0800456 }
Howard Chenf08428e2019-01-23 14:54:51 +0800457 return 0;
458}
459
Howard Chenecbc0192019-02-25 18:51:26 +0800460static int Cancel(sp<IGsiService> gsid, int /* argc */, char** /* argv */) {
461 bool cancelled = false;
462 auto status = gsid->cancelGsiInstall(&cancelled);
463 if (!status.isOk()) {
464 std::cerr << status.exceptionMessage().string() << std::endl;
465 return EX_SOFTWARE;
466 }
467 if (!cancelled) {
468 std::cout << "Fail to cancel the installation." << std::endl;
469 return EX_SOFTWARE;
470 }
471 return 0;
472}
473
David Anderson564a04c2019-02-27 13:33:44 -0800474static int Enable(sp<IGsiService> gsid, int argc, char** argv) {
475 bool one_shot = false;
Howard Chenee5c2b12019-11-08 11:57:47 +0800476 std::string dsuSlot = {};
David Anderson564a04c2019-02-27 13:33:44 -0800477 struct option options[] = {
478 {"single-boot", no_argument, nullptr, 's'},
Howard Chenee5c2b12019-11-08 11:57:47 +0800479 {"dsuslot", required_argument, nullptr, 'd'},
David Anderson564a04c2019-02-27 13:33:44 -0800480 {nullptr, 0, nullptr, 0},
481 };
482 int rv, index;
483 while ((rv = getopt_long_only(argc, argv, "", options, &index)) != -1) {
484 switch (rv) {
485 case 's':
486 one_shot = true;
487 break;
Howard Chenee5c2b12019-11-08 11:57:47 +0800488 case 'd':
489 dsuSlot = optarg;
490 break;
David Anderson564a04c2019-02-27 13:33:44 -0800491 default:
492 std::cerr << "Unrecognized argument to enable\n";
493 return EX_USAGE;
494 }
David Andersona141ba82019-01-14 19:09:27 -0800495 }
496
497 bool installed = false;
498 gsid->isGsiInstalled(&installed);
499 if (!installed) {
David Anderson550253b2019-02-08 17:55:27 -0800500 std::cerr << "Could not find GSI install to re-enable" << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800501 return EX_SOFTWARE;
502 }
503
504 bool installing = false;
505 gsid->isGsiInstallInProgress(&installing);
506 if (installing) {
David Anderson550253b2019-02-08 17:55:27 -0800507 std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800508 return EX_SOFTWARE;
509 }
Howard Chenee5c2b12019-11-08 11:57:47 +0800510 if (dsuSlot.empty()) {
511 auto status = gsid->getActiveDsuSlot(&dsuSlot);
512 if (!status.isOk()) {
513 std::cerr << "Could not get the active DSU slot: " << ErrorMessage(status) << "\n";
514 return EX_SOFTWARE;
515 }
516 }
David Anderson868dea52019-01-17 13:34:38 -0800517 int error;
Howard Chenee5c2b12019-11-08 11:57:47 +0800518 auto status = gsid->enableGsi(one_shot, dsuSlot, &error);
David Anderson868dea52019-01-17 13:34:38 -0800519 if (!status.isOk() || error != IGsiService::INSTALL_OK) {
David Anderson646b4b22019-02-27 18:26:54 -0800520 std::cerr << "Error re-enabling GSI: " << ErrorMessage(status, error) << "\n";
David Andersona141ba82019-01-14 19:09:27 -0800521 return EX_SOFTWARE;
522 }
523 std::cout << "Live image install successfully enabled." << std::endl;
524 return 0;
525}
526
527static int Disable(sp<IGsiService> gsid, int argc, char** /* argv */) {
528 if (argc > 1) {
David Anderson550253b2019-02-08 17:55:27 -0800529 std::cerr << "Unrecognized arguments to disable." << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800530 return EX_USAGE;
531 }
532
533 bool installing = false;
534 gsid->isGsiInstallInProgress(&installing);
535 if (installing) {
David Anderson550253b2019-02-08 17:55:27 -0800536 std::cerr << "Cannot enable or disable while an installation is in progress." << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800537 return EX_SOFTWARE;
538 }
539
540 bool ok = false;
Howard Chen25b18cc2019-08-02 11:21:58 +0800541 gsid->disableGsi(&ok);
David Andersona141ba82019-01-14 19:09:27 -0800542 if (!ok) {
David Anderson550253b2019-02-08 17:55:27 -0800543 std::cerr << "Error disabling GSI" << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800544 return EX_SOFTWARE;
545 }
546 std::cout << "Live image install successfully disabled." << std::endl;
547 return 0;
548}
549
550static int usage(int /* argc */, char* argv[]) {
551 fprintf(stderr,
552 "%s - command-line tool for installing GSI images.\n"
553 "\n"
554 "Usage:\n"
Howard Chenf08428e2019-01-23 14:54:51 +0800555 " %s <disable|install|wipe|status> [options]\n"
David Andersona141ba82019-01-14 19:09:27 -0800556 "\n"
557 " disable Disable the currently installed GSI.\n"
Howard Chenee5c2b12019-11-08 11:57:47 +0800558 " enable [-s, --single-boot]\n"
559 " [-d, --dsuslot slotname]\n"
David Anderson564a04c2019-02-27 13:33:44 -0800560 " Enable a previously disabled GSI.\n"
David Andersona141ba82019-01-14 19:09:27 -0800561 " install Install a new GSI. Specify the image size with\n"
562 " --gsi-size and the desired userdata size with\n"
563 " --userdata-size (the latter defaults to 8GiB)\n"
564 " --wipe (remove old gsi userdata first)\n"
Howard Chenf08428e2019-01-23 14:54:51 +0800565 " wipe Completely remove a GSI and its associated data\n"
David Anderson8bdf6252019-06-11 16:43:24 -0700566 " wipe-data Ensure the GSI's userdata will be formatted\n"
Howard Chenecbc0192019-02-25 18:51:26 +0800567 " cancel Cancel the installation\n"
568 " status Show status\n",
David Andersona141ba82019-01-14 19:09:27 -0800569 argv[0], argv[0]);
570 return EX_USAGE;
571}
572
David Andersonee84d742019-01-07 18:10:29 -0800573int main(int argc, char** argv) {
David Anderson83fdeca2019-09-09 17:56:22 -0700574 android::base::InitLogging(argv, android::base::StdioLogger, android::base::DefaultAborter);
David Andersonee84d742019-01-07 18:10:29 -0800575
David Anderson83fdeca2019-09-09 17:56:22 -0700576 android::sp<IGsiService> service = GetGsiService();
577 if (!service) {
David Anderson4c756732019-07-12 14:18:37 -0700578 return EX_SOFTWARE;
579 }
580
David Andersonee84d742019-01-07 18:10:29 -0800581 if (1 >= argc) {
David Anderson550253b2019-02-08 17:55:27 -0800582 std::cerr << "Expected command." << std::endl;
David Andersonee84d742019-01-07 18:10:29 -0800583 return EX_USAGE;
584 }
585
586 std::string command = argv[1];
David Anderson34502be2019-02-01 18:06:20 -0800587
David Andersonee84d742019-01-07 18:10:29 -0800588 auto iter = kCommandMap.find(command);
589 if (iter == kCommandMap.end()) {
David Anderson550253b2019-02-08 17:55:27 -0800590 std::cerr << "Unrecognized command: " << command << std::endl;
David Andersona141ba82019-01-14 19:09:27 -0800591 return usage(argc, argv);
David Andersonee84d742019-01-07 18:10:29 -0800592 }
David Andersonc053b3b2019-01-08 18:22:07 -0800593
David Anderson4c756732019-07-12 14:18:37 -0700594 int rc = iter->second(service, argc - 1, argv + 1);
David Andersonc053b3b2019-01-08 18:22:07 -0800595 return rc;
David Andersonee84d742019-01-07 18:10:29 -0800596}