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