1 // 2 // Copyright (C) 2012 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 <inttypes.h> 18 #include <sysexits.h> 19 #include <unistd.h> 20 21 #include <memory> 22 #include <string> 23 #include <vector> 24 25 #include <base/bind.h> 26 #include <base/command_line.h> 27 #include <base/logging.h> 28 #include <base/macros.h> 29 #include <base/strings/string_split.h> 30 #include <base/threading/platform_thread.h> 31 #include <brillo/daemons/daemon.h> 32 #include <brillo/flag_helper.h> 33 34 #include "update_engine/client.h" 35 #include "update_engine/common/error_code.h" 36 #include "update_engine/common/error_code_utils.h" 37 #include "update_engine/omaha_utils.h" 38 #include "update_engine/status_update_handler.h" 39 #include "update_engine/update_status.h" 40 #include "update_engine/update_status_utils.h" 41 42 using chromeos_update_engine::EolStatus; 43 using chromeos_update_engine::ErrorCode; 44 using chromeos_update_engine::UpdateStatusToString; 45 using chromeos_update_engine::utils::ErrorCodeToString; 46 using std::string; 47 using std::unique_ptr; 48 using std::vector; 49 using update_engine::UpdateStatus; 50 51 namespace { 52 53 // Constant to signal that we need to continue running the daemon after 54 // initialization. 55 const int kContinueRunning = -1; 56 57 // The ShowStatus request will be retried `kShowStatusRetryCount` times at 58 // `kShowStatusRetryInterval` second intervals on failure. 59 const int kShowStatusRetryCount = 30; 60 const int kShowStatusRetryIntervalInSeconds = 2; 61 62 class UpdateEngineClient : public brillo::Daemon { 63 public: 64 UpdateEngineClient(int argc, char** argv) : argc_(argc), argv_(argv) {} 65 66 ~UpdateEngineClient() override = default; 67 68 protected: 69 int OnInit() override { 70 int ret = Daemon::OnInit(); 71 if (ret != EX_OK) 72 return ret; 73 74 client_ = update_engine::UpdateEngineClient::CreateInstance(); 75 76 if (!client_) { 77 LOG(ERROR) << "UpdateEngineService not available."; 78 return 1; 79 } 80 81 // We can't call QuitWithExitCode from OnInit(), so we delay the execution 82 // of the ProcessFlags method after the Daemon initialization is done. 83 base::MessageLoop::current()->task_runner()->PostTask( 84 FROM_HERE, 85 base::Bind(&UpdateEngineClient::ProcessFlagsAndExit, 86 base::Unretained(this))); 87 return EX_OK; 88 } 89 90 private: 91 // Show the status of the update engine in stdout. 92 bool ShowStatus(); 93 94 // Return whether we need to reboot. 0 if reboot is needed, 1 if an error 95 // occurred, 2 if no reboot is needed. 96 int GetNeedReboot(); 97 98 // Main method that parses and triggers all the actions based on the passed 99 // flags. Returns the exit code of the program of kContinueRunning if it 100 // should not exit. 101 int ProcessFlags(); 102 103 // Processes the flags and exits the program accordingly. 104 void ProcessFlagsAndExit(); 105 106 // Copy of argc and argv passed to main(). 107 int argc_; 108 char** argv_; 109 110 // Library-based client 111 unique_ptr<update_engine::UpdateEngineClient> client_; 112 113 // Pointers to handlers for cleanup 114 vector<unique_ptr<update_engine::StatusUpdateHandler>> handlers_; 115 116 DISALLOW_COPY_AND_ASSIGN(UpdateEngineClient); 117 }; 118 119 class ExitingStatusUpdateHandler : public update_engine::StatusUpdateHandler { 120 public: 121 ~ExitingStatusUpdateHandler() override = default; 122 123 void IPCError(const string& error) override; 124 }; 125 126 void ExitingStatusUpdateHandler::IPCError(const string& error) { 127 LOG(ERROR) << error; 128 exit(1); 129 } 130 131 class WatchingStatusUpdateHandler : public ExitingStatusUpdateHandler { 132 public: 133 ~WatchingStatusUpdateHandler() override = default; 134 135 void HandleStatusUpdate(int64_t last_checked_time, 136 double progress, 137 UpdateStatus current_operation, 138 const string& new_version, 139 int64_t new_size) override; 140 }; 141 142 void WatchingStatusUpdateHandler::HandleStatusUpdate( 143 int64_t last_checked_time, 144 double progress, 145 UpdateStatus current_operation, 146 const string& new_version, 147 int64_t new_size) { 148 LOG(INFO) << "Got status update:"; 149 LOG(INFO) << " last_checked_time: " << last_checked_time; 150 LOG(INFO) << " progress: " << progress; 151 LOG(INFO) << " current_operation: " 152 << UpdateStatusToString(current_operation); 153 LOG(INFO) << " new_version: " << new_version; 154 LOG(INFO) << " new_size: " << new_size; 155 } 156 157 bool UpdateEngineClient::ShowStatus() { 158 int64_t last_checked_time = 0; 159 double progress = 0.0; 160 UpdateStatus current_op; 161 string new_version; 162 int64_t new_size = 0; 163 164 int retry_count = kShowStatusRetryCount; 165 while (retry_count > 0) { 166 if (client_->GetStatus(&last_checked_time, 167 &progress, 168 ¤t_op, 169 &new_version, 170 &new_size)) { 171 break; 172 } 173 if (--retry_count == 0) { 174 return false; 175 } 176 LOG(WARNING) << "Will try " << retry_count << " more times!"; 177 base::PlatformThread::Sleep( 178 base::TimeDelta::FromSeconds(kShowStatusRetryIntervalInSeconds)); 179 } 180 181 printf("LAST_CHECKED_TIME=%" PRIi64 182 "\nPROGRESS=%f\nCURRENT_OP=%s\n" 183 "NEW_VERSION=%s\nNEW_SIZE=%" PRIi64 "\n", 184 last_checked_time, 185 progress, 186 UpdateStatusToString(current_op), 187 new_version.c_str(), 188 new_size); 189 190 return true; 191 } 192 193 int UpdateEngineClient::GetNeedReboot() { 194 int64_t last_checked_time = 0; 195 double progress = 0.0; 196 UpdateStatus current_op; 197 string new_version; 198 int64_t new_size = 0; 199 200 if (!client_->GetStatus(&last_checked_time, 201 &progress, 202 ¤t_op, 203 &new_version, 204 &new_size)) { 205 return 1; 206 } 207 208 if (current_op == UpdateStatus::UPDATED_NEED_REBOOT) { 209 return 0; 210 } 211 212 return 2; 213 } 214 215 class UpdateWaitHandler : public ExitingStatusUpdateHandler { 216 public: 217 explicit UpdateWaitHandler(bool exit_on_error, 218 update_engine::UpdateEngineClient* client) 219 : exit_on_error_(exit_on_error), client_(client) {} 220 221 ~UpdateWaitHandler() override = default; 222 223 void HandleStatusUpdate(int64_t last_checked_time, 224 double progress, 225 UpdateStatus current_operation, 226 const string& new_version, 227 int64_t new_size) override; 228 229 private: 230 bool exit_on_error_; 231 update_engine::UpdateEngineClient* client_; 232 }; 233 234 void UpdateWaitHandler::HandleStatusUpdate(int64_t /* last_checked_time */, 235 double /* progress */, 236 UpdateStatus current_operation, 237 const string& /* new_version */, 238 int64_t /* new_size */) { 239 if (exit_on_error_ && current_operation == UpdateStatus::IDLE) { 240 int last_attempt_error; 241 ErrorCode code = ErrorCode::kSuccess; 242 if (client_ && client_->GetLastAttemptError(&last_attempt_error)) 243 code = static_cast<ErrorCode>(last_attempt_error); 244 245 LOG(ERROR) << "Update failed, current operation is " 246 << UpdateStatusToString(current_operation) 247 << ", last error code is " << ErrorCodeToString(code) << "(" 248 << last_attempt_error << ")"; 249 exit(1); 250 } 251 if (current_operation == UpdateStatus::UPDATED_NEED_REBOOT) { 252 LOG(INFO) << "Update succeeded -- reboot needed."; 253 exit(0); 254 } 255 } 256 257 int UpdateEngineClient::ProcessFlags() { 258 DEFINE_string(app_version, "", "Force the current app version."); 259 DEFINE_string(channel, 260 "", 261 "Set the target channel. The device will be powerwashed if the " 262 "target channel is more stable than the current channel unless " 263 "--nopowerwash is specified."); 264 DEFINE_bool(check_for_update, false, "Initiate check for updates."); 265 DEFINE_string( 266 cohort_hint, "", "Set the current cohort hint to the passed value."); 267 DEFINE_bool(follow, 268 false, 269 "Wait for any update operations to complete." 270 "Exit status is 0 if the update succeeded, and 1 otherwise."); 271 DEFINE_bool(interactive, true, "Mark the update request as interactive."); 272 DEFINE_string(omaha_url, "", "The URL of the Omaha update server."); 273 DEFINE_string(p2p_update, 274 "", 275 "Enables (\"yes\") or disables (\"no\") the peer-to-peer update" 276 " sharing."); 277 DEFINE_bool(powerwash, 278 true, 279 "When performing rollback or channel change, " 280 "do a powerwash or allow it respectively."); 281 DEFINE_bool(reboot, false, "Initiate a reboot if needed."); 282 DEFINE_bool(is_reboot_needed, 283 false, 284 "Exit status 0 if reboot is needed, " 285 "2 if reboot is not needed or 1 if an error occurred."); 286 DEFINE_bool(block_until_reboot_is_needed, 287 false, 288 "Blocks until reboot is " 289 "needed. Returns non-zero exit status if an error occurred."); 290 DEFINE_bool(reset_status, false, "Sets the status in update_engine to idle."); 291 DEFINE_bool(rollback, 292 false, 293 "Perform a rollback to the previous partition. The device will " 294 "be powerwashed unless --nopowerwash is specified."); 295 DEFINE_bool(can_rollback, 296 false, 297 "Shows whether rollback partition " 298 "is available."); 299 DEFINE_bool(show_channel, false, "Show the current and target channels."); 300 DEFINE_bool(show_cohort_hint, false, "Show the current cohort hint."); 301 DEFINE_bool(show_p2p_update, 302 false, 303 "Show the current setting for peer-to-peer update sharing."); 304 DEFINE_bool(show_update_over_cellular, 305 false, 306 "Show the current setting for updates over cellular networks."); 307 DEFINE_bool(status, false, "Print the status to stdout."); 308 DEFINE_bool(update, 309 false, 310 "Forces an update and waits for it to complete. " 311 "Implies --follow."); 312 DEFINE_string(update_over_cellular, 313 "", 314 "Enables (\"yes\") or disables (\"no\") the updates over " 315 "cellular networks."); 316 DEFINE_bool(watch_for_updates, 317 false, 318 "Listen for status updates and print them to the screen."); 319 DEFINE_bool(prev_version, 320 false, 321 "Show the previous OS version used before the update reboot."); 322 DEFINE_bool(last_attempt_error, false, "Show the last attempt error."); 323 DEFINE_bool(eol_status, false, "Show the current end-of-life status."); 324 DEFINE_bool(install, false, "Requests an install."); 325 DEFINE_string(dlc_module_ids, "", "colon-separated list of DLC IDs."); 326 327 // Boilerplate init commands. 328 base::CommandLine::Init(argc_, argv_); 329 brillo::FlagHelper::Init(argc_, argv_, "A/B Update Engine Client"); 330 331 // Ensure there are no positional arguments. 332 const vector<string> positional_args = 333 base::CommandLine::ForCurrentProcess()->GetArgs(); 334 if (!positional_args.empty()) { 335 LOG(ERROR) << "Found a positional argument '" << positional_args.front() 336 << "'. If you want to pass a value to a flag, pass it as " 337 "--flag=value."; 338 return 1; 339 } 340 341 // Update the status if requested. 342 if (FLAGS_reset_status) { 343 LOG(INFO) << "Setting Update Engine status to idle ..."; 344 345 if (client_->ResetStatus()) { 346 LOG(INFO) << "ResetStatus succeeded; to undo partition table changes " 347 "run:\n" 348 "(D=$(rootdev -d) P=$(rootdev -s); cgpt p -i$(($(echo " 349 "${P#$D} | sed 's/^[^0-9]*//')-1)) $D;)"; 350 } else { 351 LOG(ERROR) << "ResetStatus failed"; 352 return 1; 353 } 354 } 355 356 // Changes the current update over cellular network setting. 357 if (!FLAGS_update_over_cellular.empty()) { 358 bool allowed = FLAGS_update_over_cellular == "yes"; 359 if (!allowed && FLAGS_update_over_cellular != "no") { 360 LOG(ERROR) << "Unknown option: \"" << FLAGS_update_over_cellular 361 << "\". Please specify \"yes\" or \"no\"."; 362 } else { 363 if (!client_->SetUpdateOverCellularPermission(allowed)) { 364 LOG(ERROR) << "Error setting the update over cellular setting."; 365 return 1; 366 } 367 } 368 } 369 370 // Show the current update over cellular network setting. 371 if (FLAGS_show_update_over_cellular) { 372 bool allowed; 373 374 if (!client_->GetUpdateOverCellularPermission(&allowed)) { 375 LOG(ERROR) << "Error getting the update over cellular setting."; 376 return 1; 377 } 378 379 LOG(INFO) << "Current update over cellular network setting: " 380 << (allowed ? "ENABLED" : "DISABLED"); 381 } 382 383 // Change/show the cohort hint. 384 bool set_cohort_hint = 385 base::CommandLine::ForCurrentProcess()->HasSwitch("cohort_hint"); 386 if (set_cohort_hint) { 387 LOG(INFO) << "Setting cohort hint to: \"" << FLAGS_cohort_hint << "\""; 388 if (!client_->SetCohortHint(FLAGS_cohort_hint)) { 389 LOG(ERROR) << "Error setting the cohort hint."; 390 return 1; 391 } 392 } 393 394 if (FLAGS_show_cohort_hint || set_cohort_hint) { 395 string cohort_hint; 396 if (!client_->GetCohortHint(&cohort_hint)) { 397 LOG(ERROR) << "Error getting the cohort hint."; 398 return 1; 399 } 400 401 LOG(INFO) << "Current cohort hint: \"" << cohort_hint << "\""; 402 } 403 404 if (!FLAGS_powerwash && !FLAGS_rollback && FLAGS_channel.empty()) { 405 LOG(ERROR) << "powerwash flag only makes sense rollback or channel change"; 406 return 1; 407 } 408 409 // Change the P2P enabled setting. 410 if (!FLAGS_p2p_update.empty()) { 411 bool enabled = FLAGS_p2p_update == "yes"; 412 if (!enabled && FLAGS_p2p_update != "no") { 413 LOG(ERROR) << "Unknown option: \"" << FLAGS_p2p_update 414 << "\". Please specify \"yes\" or \"no\"."; 415 } else { 416 if (!client_->SetP2PUpdatePermission(enabled)) { 417 LOG(ERROR) << "Error setting the peer-to-peer update setting."; 418 return 1; 419 } 420 } 421 } 422 423 // Show the rollback availability. 424 if (FLAGS_can_rollback) { 425 string rollback_partition; 426 427 if (!client_->GetRollbackPartition(&rollback_partition)) { 428 LOG(ERROR) << "Error while querying rollback partition availability."; 429 return 1; 430 } 431 432 bool can_rollback = true; 433 if (rollback_partition.empty()) { 434 rollback_partition = "UNAVAILABLE"; 435 can_rollback = false; 436 } else { 437 rollback_partition = "AVAILABLE: " + rollback_partition; 438 } 439 440 LOG(INFO) << "Rollback partition: " << rollback_partition; 441 if (!can_rollback) { 442 return 1; 443 } 444 } 445 446 // Show the current P2P enabled setting. 447 if (FLAGS_show_p2p_update) { 448 bool enabled; 449 450 if (!client_->GetP2PUpdatePermission(&enabled)) { 451 LOG(ERROR) << "Error getting the peer-to-peer update setting."; 452 return 1; 453 } 454 455 LOG(INFO) << "Current update using P2P setting: " 456 << (enabled ? "ENABLED" : "DISABLED"); 457 } 458 459 // First, update the target channel if requested. 460 if (!FLAGS_channel.empty()) { 461 if (!client_->SetTargetChannel(FLAGS_channel, FLAGS_powerwash)) { 462 LOG(ERROR) << "Error setting the channel."; 463 return 1; 464 } 465 466 LOG(INFO) << "Channel permanently set to: " << FLAGS_channel; 467 } 468 469 // Show the current and target channels if requested. 470 if (FLAGS_show_channel) { 471 string current_channel; 472 string target_channel; 473 474 if (!client_->GetChannel(¤t_channel)) { 475 LOG(ERROR) << "Error getting the current channel."; 476 return 1; 477 } 478 479 if (!client_->GetTargetChannel(&target_channel)) { 480 LOG(ERROR) << "Error getting the target channel."; 481 return 1; 482 } 483 484 LOG(INFO) << "Current Channel: " << current_channel; 485 486 if (!target_channel.empty()) 487 LOG(INFO) << "Target Channel (pending update): " << target_channel; 488 } 489 490 bool do_update_request = FLAGS_check_for_update || FLAGS_update || 491 !FLAGS_app_version.empty() || 492 !FLAGS_omaha_url.empty(); 493 if (FLAGS_update) 494 FLAGS_follow = true; 495 496 if (do_update_request && FLAGS_rollback) { 497 LOG(ERROR) << "Incompatible flags specified with rollback." 498 << "Rollback should not include update-related flags."; 499 return 1; 500 } 501 502 if (FLAGS_rollback) { 503 LOG(INFO) << "Requesting rollback."; 504 if (!client_->Rollback(FLAGS_powerwash)) { 505 LOG(ERROR) << "Rollback request failed."; 506 return 1; 507 } 508 } 509 510 if (FLAGS_install) { 511 // Parse DLC module IDs. 512 vector<string> dlc_module_ids; 513 if (!FLAGS_dlc_module_ids.empty()) { 514 dlc_module_ids = base::SplitString(FLAGS_dlc_module_ids, 515 ":", 516 base::TRIM_WHITESPACE, 517 base::SPLIT_WANT_ALL); 518 } 519 if (dlc_module_ids.empty()) { 520 LOG(ERROR) << "dlc_module_ids is empty:" << FLAGS_dlc_module_ids; 521 return 1; 522 } 523 if (!client_->AttemptInstall(FLAGS_omaha_url, dlc_module_ids)) { 524 LOG(ERROR) << "AttemptInstall failed."; 525 return 1; 526 } 527 return 0; 528 } else if (!FLAGS_dlc_module_ids.empty()) { 529 LOG(ERROR) << "dlc_module_ids is not empty while install is not set:" 530 << FLAGS_dlc_module_ids; 531 return 1; 532 } 533 534 // Initiate an update check, if necessary. 535 if (do_update_request) { 536 LOG_IF(WARNING, FLAGS_reboot) << "-reboot flag ignored."; 537 string app_version = FLAGS_app_version; 538 if (FLAGS_update && app_version.empty()) { 539 app_version = "ForcedUpdate"; 540 LOG(INFO) << "Forcing an update by setting app_version to ForcedUpdate."; 541 } 542 LOG(INFO) << "Initiating update check and install."; 543 if (!client_->AttemptUpdate( 544 app_version, FLAGS_omaha_url, FLAGS_interactive)) { 545 LOG(ERROR) << "Error checking for update."; 546 return 1; 547 } 548 } 549 550 // These final options are all mutually exclusive with one another. 551 if (FLAGS_follow + FLAGS_watch_for_updates + FLAGS_reboot + FLAGS_status + 552 FLAGS_is_reboot_needed + FLAGS_block_until_reboot_is_needed > 553 1) { 554 LOG(ERROR) << "Multiple exclusive options selected. " 555 << "Select only one of --follow, --watch_for_updates, --reboot, " 556 << "--is_reboot_needed, --block_until_reboot_is_needed, " 557 << "or --status."; 558 return 1; 559 } 560 561 if (FLAGS_status) { 562 LOG(INFO) << "Querying Update Engine status..."; 563 if (!ShowStatus()) { 564 LOG(ERROR) << "Failed to query status"; 565 return 1; 566 } 567 return 0; 568 } 569 570 if (FLAGS_follow) { 571 LOG(INFO) << "Waiting for update to complete."; 572 auto handler = new UpdateWaitHandler(true, client_.get()); 573 handlers_.emplace_back(handler); 574 client_->RegisterStatusUpdateHandler(handler); 575 return kContinueRunning; 576 } 577 578 if (FLAGS_watch_for_updates) { 579 LOG(INFO) << "Watching for status updates."; 580 auto handler = new WatchingStatusUpdateHandler(); 581 handlers_.emplace_back(handler); 582 client_->RegisterStatusUpdateHandler(handler); 583 return kContinueRunning; 584 } 585 586 if (FLAGS_reboot) { 587 LOG(INFO) << "Requesting a reboot..."; 588 client_->RebootIfNeeded(); 589 return 0; 590 } 591 592 if (FLAGS_prev_version) { 593 string prev_version; 594 595 if (!client_->GetPrevVersion(&prev_version)) { 596 LOG(ERROR) << "Error getting previous version."; 597 } else { 598 LOG(INFO) << "Previous version = " << prev_version; 599 } 600 } 601 602 if (FLAGS_is_reboot_needed) { 603 int ret = GetNeedReboot(); 604 605 if (ret == 1) { 606 LOG(ERROR) << "Could not query the current operation."; 607 } 608 609 return ret; 610 } 611 612 if (FLAGS_block_until_reboot_is_needed) { 613 auto handler = new UpdateWaitHandler(false, nullptr); 614 handlers_.emplace_back(handler); 615 client_->RegisterStatusUpdateHandler(handler); 616 return kContinueRunning; 617 } 618 619 if (FLAGS_last_attempt_error) { 620 int last_attempt_error; 621 if (!client_->GetLastAttemptError(&last_attempt_error)) { 622 LOG(ERROR) << "Error getting last attempt error."; 623 } else { 624 ErrorCode code = static_cast<ErrorCode>(last_attempt_error); 625 printf( 626 "ERROR_CODE=%i\n" 627 "ERROR_MESSAGE=%s\n", 628 last_attempt_error, 629 ErrorCodeToString(code).c_str()); 630 } 631 } 632 633 if (FLAGS_eol_status) { 634 int eol_status; 635 if (!client_->GetEolStatus(&eol_status)) { 636 LOG(ERROR) << "Error getting the end-of-life status."; 637 } else { 638 EolStatus eol_status_code = static_cast<EolStatus>(eol_status); 639 printf("EOL_STATUS=%s\n", EolStatusToString(eol_status_code)); 640 } 641 } 642 643 return 0; 644 } 645 646 void UpdateEngineClient::ProcessFlagsAndExit() { 647 int ret = ProcessFlags(); 648 if (ret != kContinueRunning) 649 QuitWithExitCode(ret); 650 } 651 652 } // namespace 653 654 int main(int argc, char** argv) { 655 UpdateEngineClient client(argc, argv); 656 return client.Run(); 657 } 658