update_attempter.cc revision 362757765e7697dd93e8de05fa0a59f29242eb89
1// Copyright (c) 2010 The Chromium OS Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "update_engine/update_attempter.h" 6 7// From 'man clock_gettime': feature test macro: _POSIX_C_SOURCE >= 199309L 8#ifndef _POSIX_C_SOURCE 9#define _POSIX_C_SOURCE 199309L 10#endif // _POSIX_C_SOURCE 11#include <time.h> 12 13#include <tr1/memory> 14#include <string> 15#include <vector> 16 17#include <glib.h> 18#include <metrics/metrics_library.h> 19 20#include "update_engine/dbus_service.h" 21#include "update_engine/download_action.h" 22#include "update_engine/filesystem_copier_action.h" 23#include "update_engine/libcurl_http_fetcher.h" 24#include "update_engine/omaha_request_action.h" 25#include "update_engine/omaha_request_params.h" 26#include "update_engine/omaha_response_handler_action.h" 27#include "update_engine/postinstall_runner_action.h" 28#include "update_engine/prefs_interface.h" 29#include "update_engine/set_bootable_flag_action.h" 30#include "update_engine/update_check_scheduler.h" 31 32using base::TimeDelta; 33using base::TimeTicks; 34using std::tr1::shared_ptr; 35using std::string; 36using std::vector; 37 38namespace chromeos_update_engine { 39 40const int UpdateAttempter::kMaxDeltaUpdateFailures = 3; 41 42const char* kUpdateCompletedMarker = 43 "/var/run/update_engine_autoupdate_completed"; 44 45const char* UpdateStatusToString(UpdateStatus status) { 46 switch (status) { 47 case UPDATE_STATUS_IDLE: 48 return "UPDATE_STATUS_IDLE"; 49 case UPDATE_STATUS_CHECKING_FOR_UPDATE: 50 return "UPDATE_STATUS_CHECKING_FOR_UPDATE"; 51 case UPDATE_STATUS_UPDATE_AVAILABLE: 52 return "UPDATE_STATUS_UPDATE_AVAILABLE"; 53 case UPDATE_STATUS_DOWNLOADING: 54 return "UPDATE_STATUS_DOWNLOADING"; 55 case UPDATE_STATUS_VERIFYING: 56 return "UPDATE_STATUS_VERIFYING"; 57 case UPDATE_STATUS_FINALIZING: 58 return "UPDATE_STATUS_FINALIZING"; 59 case UPDATE_STATUS_UPDATED_NEED_REBOOT: 60 return "UPDATE_STATUS_UPDATED_NEED_REBOOT"; 61 case UPDATE_STATUS_REPORTING_ERROR_EVENT: 62 return "UPDATE_STATUS_REPORTING_ERROR_EVENT"; 63 default: 64 return "unknown status"; 65 } 66} 67 68// Turns a generic kActionCodeError to a generic error code specific 69// to |action| (e.g., kActionCodeFilesystemCopierError). If |code| is 70// not kActionCodeError, or the action is not matched, returns |code| 71// unchanged. 72ActionExitCode GetErrorCodeForAction(AbstractAction* action, 73 ActionExitCode code) { 74 if (code != kActionCodeError) 75 return code; 76 77 const string type = action->Type(); 78 if (type == OmahaRequestAction::StaticType()) 79 return kActionCodeOmahaRequestError; 80 if (type == OmahaResponseHandlerAction::StaticType()) 81 return kActionCodeOmahaResponseHandlerError; 82 if (type == FilesystemCopierAction::StaticType()) 83 return kActionCodeFilesystemCopierError; 84 if (type == PostinstallRunnerAction::StaticType()) 85 return kActionCodePostinstallRunnerError; 86 if (type == SetBootableFlagAction::StaticType()) 87 return kActionCodeSetBootableFlagError; 88 89 return code; 90} 91 92UpdateAttempter::UpdateAttempter(PrefsInterface* prefs, 93 MetricsLibraryInterface* metrics_lib) 94 : processor_(new ActionProcessor()), 95 dbus_service_(NULL), 96 prefs_(prefs), 97 metrics_lib_(metrics_lib), 98 update_check_scheduler_(NULL), 99 http_response_code_(0), 100 priority_(utils::kProcessPriorityNormal), 101 manage_priority_source_(NULL), 102 download_active_(false), 103 status_(UPDATE_STATUS_IDLE), 104 download_progress_(0.0), 105 last_checked_time_(0), 106 new_version_("0.0.0.0"), 107 new_size_(0), 108 is_full_update_(false) { 109 if (utils::FileExists(kUpdateCompletedMarker)) 110 status_ = UPDATE_STATUS_UPDATED_NEED_REBOOT; 111} 112 113UpdateAttempter::~UpdateAttempter() { 114 CleanupPriorityManagement(); 115} 116 117void UpdateAttempter::Update(const std::string& app_version, 118 const std::string& omaha_url) { 119 if (status_ == UPDATE_STATUS_UPDATED_NEED_REBOOT) { 120 LOG(INFO) << "Not updating b/c we already updated and we're waiting for " 121 << "reboot"; 122 return; 123 } 124 if (status_ != UPDATE_STATUS_IDLE) { 125 // Update in progress. Do nothing 126 return; 127 } 128 http_response_code_ = 0; 129 if (!omaha_request_params_.Init(app_version, omaha_url)) { 130 LOG(ERROR) << "Unable to initialize Omaha request device params."; 131 return; 132 } 133 DisableDeltaUpdateIfNeeded(); 134 CHECK(!processor_->IsRunning()); 135 processor_->set_delegate(this); 136 137 // Actions: 138 shared_ptr<OmahaRequestAction> update_check_action( 139 new OmahaRequestAction(prefs_, 140 omaha_request_params_, 141 NULL, 142 new LibcurlHttpFetcher)); 143 shared_ptr<OmahaResponseHandlerAction> response_handler_action( 144 new OmahaResponseHandlerAction); 145 shared_ptr<FilesystemCopierAction> filesystem_copier_action( 146 new FilesystemCopierAction(false)); 147 shared_ptr<FilesystemCopierAction> kernel_filesystem_copier_action( 148 new FilesystemCopierAction(true)); 149 shared_ptr<OmahaRequestAction> download_started_action( 150 new OmahaRequestAction(prefs_, 151 omaha_request_params_, 152 new OmahaEvent( 153 OmahaEvent::kTypeUpdateDownloadStarted), 154 new LibcurlHttpFetcher)); 155 shared_ptr<DownloadAction> download_action( 156 new DownloadAction(new LibcurlHttpFetcher)); 157 shared_ptr<OmahaRequestAction> download_finished_action( 158 new OmahaRequestAction(prefs_, 159 omaha_request_params_, 160 new OmahaEvent( 161 OmahaEvent::kTypeUpdateDownloadFinished), 162 new LibcurlHttpFetcher)); 163 shared_ptr<PostinstallRunnerAction> postinstall_runner_action_precommit( 164 new PostinstallRunnerAction(true)); 165 shared_ptr<SetBootableFlagAction> set_bootable_flag_action( 166 new SetBootableFlagAction); 167 shared_ptr<PostinstallRunnerAction> postinstall_runner_action_postcommit( 168 new PostinstallRunnerAction(false)); 169 shared_ptr<OmahaRequestAction> update_complete_action( 170 new OmahaRequestAction(prefs_, 171 omaha_request_params_, 172 new OmahaEvent(OmahaEvent::kTypeUpdateComplete), 173 new LibcurlHttpFetcher)); 174 175 download_action->set_delegate(this); 176 response_handler_action_ = response_handler_action; 177 178 actions_.push_back(shared_ptr<AbstractAction>(update_check_action)); 179 actions_.push_back(shared_ptr<AbstractAction>(response_handler_action)); 180 actions_.push_back(shared_ptr<AbstractAction>(filesystem_copier_action)); 181 actions_.push_back(shared_ptr<AbstractAction>( 182 kernel_filesystem_copier_action)); 183 actions_.push_back(shared_ptr<AbstractAction>(download_started_action)); 184 actions_.push_back(shared_ptr<AbstractAction>(download_action)); 185 actions_.push_back(shared_ptr<AbstractAction>(download_finished_action)); 186 actions_.push_back(shared_ptr<AbstractAction>( 187 postinstall_runner_action_precommit)); 188 actions_.push_back(shared_ptr<AbstractAction>(set_bootable_flag_action)); 189 actions_.push_back(shared_ptr<AbstractAction>( 190 postinstall_runner_action_postcommit)); 191 actions_.push_back(shared_ptr<AbstractAction>(update_complete_action)); 192 193 // Enqueue the actions 194 for (vector<shared_ptr<AbstractAction> >::iterator it = actions_.begin(); 195 it != actions_.end(); ++it) { 196 processor_->EnqueueAction(it->get()); 197 } 198 199 // Bond them together. We have to use the leaf-types when calling 200 // BondActions(). 201 BondActions(update_check_action.get(), 202 response_handler_action.get()); 203 BondActions(response_handler_action.get(), 204 filesystem_copier_action.get()); 205 BondActions(filesystem_copier_action.get(), 206 kernel_filesystem_copier_action.get()); 207 BondActions(kernel_filesystem_copier_action.get(), 208 download_action.get()); 209 BondActions(download_action.get(), 210 postinstall_runner_action_precommit.get()); 211 BondActions(postinstall_runner_action_precommit.get(), 212 set_bootable_flag_action.get()); 213 BondActions(set_bootable_flag_action.get(), 214 postinstall_runner_action_postcommit.get()); 215 216 SetStatusAndNotify(UPDATE_STATUS_CHECKING_FOR_UPDATE); 217 processor_->StartProcessing(); 218} 219 220void UpdateAttempter::CheckForUpdate(const std::string& app_version, 221 const std::string& omaha_url) { 222 if (status_ != UPDATE_STATUS_IDLE) { 223 LOG(INFO) << "Check for update requested, but status is " 224 << UpdateStatusToString(status_) << ", so not checking."; 225 return; 226 } 227 Update(app_version, omaha_url); 228} 229 230bool UpdateAttempter::RebootIfNeeded() { 231 if (status_ != UPDATE_STATUS_UPDATED_NEED_REBOOT) { 232 LOG(INFO) << "Reboot requested, but status is " 233 << UpdateStatusToString(status_) << ", so not rebooting."; 234 return false; 235 } 236 TEST_AND_RETURN_FALSE(utils::Reboot()); 237 return true; 238} 239 240// Delegate methods: 241void UpdateAttempter::ProcessingDone(const ActionProcessor* processor, 242 ActionExitCode code) { 243 CHECK(response_handler_action_); 244 LOG(INFO) << "Processing Done."; 245 actions_.clear(); 246 247 // Reset process priority back to normal. 248 CleanupPriorityManagement(); 249 250 if (status_ == UPDATE_STATUS_REPORTING_ERROR_EVENT) { 251 LOG(INFO) << "Error event sent."; 252 SetStatusAndNotify(UPDATE_STATUS_IDLE); 253 return; 254 } 255 256 if (code == kActionCodeSuccess) { 257 SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT); 258 utils::WriteFile(kUpdateCompletedMarker, "", 0); 259 prefs_->SetInt64(kPrefsDeltaUpdateFailures, 0); 260 261 // Report the time it took to update the system. 262 int64_t update_time = time(NULL) - last_checked_time_; 263 metrics_lib_->SendToUMA("Installer.UpdateTime", 264 static_cast<int>(update_time), // sample 265 1, // min = 1 second 266 20 * 60, // max = 20 minutes 267 50); // buckets 268 return; 269 } 270 271 if (ScheduleErrorEventAction()) { 272 return; 273 } 274 LOG(INFO) << "No update."; 275 SetStatusAndNotify(UPDATE_STATUS_IDLE); 276} 277 278void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) { 279 // Reset process priority back to normal. 280 CleanupPriorityManagement(); 281 download_progress_ = 0.0; 282 SetStatusAndNotify(UPDATE_STATUS_IDLE); 283 actions_.clear(); 284 error_event_.reset(NULL); 285} 286 287// Called whenever an action has finished processing, either successfully 288// or otherwise. 289void UpdateAttempter::ActionCompleted(ActionProcessor* processor, 290 AbstractAction* action, 291 ActionExitCode code) { 292 // Reset download progress regardless of whether or not the download 293 // action succeeded. Also, get the response code from HTTP request 294 // actions (update download as well as the initial update check 295 // actions). 296 const string type = action->Type(); 297 if (type == DownloadAction::StaticType()) { 298 download_progress_ = 0.0; 299 DownloadAction* download_action = dynamic_cast<DownloadAction*>(action); 300 http_response_code_ = download_action->GetHTTPResponseCode(); 301 } else if (type == OmahaRequestAction::StaticType()) { 302 OmahaRequestAction* omaha_request_action = 303 dynamic_cast<OmahaRequestAction*>(action); 304 // If the request is not an event, then it's the update-check. 305 if (!omaha_request_action->IsEvent()) { 306 http_response_code_ = omaha_request_action->GetHTTPResponseCode(); 307 // Forward the server-dictated poll interval to the update check 308 // scheduler, if any. 309 if (update_check_scheduler_) { 310 update_check_scheduler_->set_poll_interval( 311 omaha_request_action->GetOutputObject().poll_interval); 312 } 313 } 314 } 315 if (code != kActionCodeSuccess) { 316 // If this was a delta update attempt and the current state is at or past 317 // the download phase, count the failure in case a switch to full update 318 // becomes necessary. Ignore network transfer timeouts and failures. 319 if (status_ >= UPDATE_STATUS_DOWNLOADING && 320 !is_full_update_ && 321 code != kActionCodeDownloadTransferError) { 322 MarkDeltaUpdateFailure(); 323 } 324 // On failure, schedule an error event to be sent to Omaha. 325 CreatePendingErrorEvent(action, code); 326 return; 327 } 328 // Find out which action completed. 329 if (type == OmahaResponseHandlerAction::StaticType()) { 330 // Note that the status will be updated to DOWNLOADING when some 331 // bytes get actually downloaded from the server and the 332 // BytesReceived callback is invoked. This avoids notifying the 333 // user that a download has started in cases when the server and 334 // the client are unable to initiate the download. 335 OmahaResponseHandlerAction* omaha_response_handler_action = 336 dynamic_cast<OmahaResponseHandlerAction*>(action); 337 CHECK(omaha_response_handler_action); 338 const InstallPlan& plan = omaha_response_handler_action->install_plan(); 339 last_checked_time_ = time(NULL); 340 // TODO(adlr): put version in InstallPlan 341 new_version_ = "0.0.0.0"; 342 new_size_ = plan.size; 343 is_full_update_ = plan.is_full_update; 344 SetupPriorityManagement(); 345 } else if (type == DownloadAction::StaticType()) { 346 SetStatusAndNotify(UPDATE_STATUS_FINALIZING); 347 } 348} 349 350// Stop updating. An attempt will be made to record status to the disk 351// so that updates can be resumed later. 352void UpdateAttempter::Terminate() { 353 // TODO(adlr): implement this method. 354 NOTIMPLEMENTED(); 355} 356 357// Try to resume from a previously Terminate()d update. 358void UpdateAttempter::ResumeUpdating() { 359 // TODO(adlr): implement this method. 360 NOTIMPLEMENTED(); 361} 362 363void UpdateAttempter::SetDownloadStatus(bool active) { 364 download_active_ = active; 365 LOG(INFO) << "Download status: " << (active ? "active" : "inactive"); 366} 367 368void UpdateAttempter::BytesReceived(uint64_t bytes_received, uint64_t total) { 369 if (!download_active_) { 370 LOG(ERROR) << "BytesReceived called while not downloading."; 371 return; 372 } 373 double progress = static_cast<double>(bytes_received) / 374 static_cast<double>(total); 375 // Self throttle based on progress. Also send notifications if 376 // progress is too slow. 377 const double kDeltaPercent = 0.01; // 1% 378 if (status_ != UPDATE_STATUS_DOWNLOADING || 379 bytes_received == total || 380 progress - download_progress_ >= kDeltaPercent || 381 TimeTicks::Now() - last_notify_time_ >= TimeDelta::FromSeconds(10)) { 382 download_progress_ = progress; 383 SetStatusAndNotify(UPDATE_STATUS_DOWNLOADING); 384 } 385} 386 387bool UpdateAttempter::GetStatus(int64_t* last_checked_time, 388 double* progress, 389 std::string* current_operation, 390 std::string* new_version, 391 int64_t* new_size) { 392 *last_checked_time = last_checked_time_; 393 *progress = download_progress_; 394 *current_operation = UpdateStatusToString(status_); 395 *new_version = new_version_; 396 *new_size = new_size_; 397 return true; 398} 399 400void UpdateAttempter::SetStatusAndNotify(UpdateStatus status) { 401 status_ = status; 402 if (update_check_scheduler_) { 403 update_check_scheduler_->SetUpdateStatus(status_); 404 } 405 if (!dbus_service_) 406 return; 407 last_notify_time_ = TimeTicks::Now(); 408 update_engine_service_emit_status_update( 409 dbus_service_, 410 last_checked_time_, 411 download_progress_, 412 UpdateStatusToString(status_), 413 new_version_.c_str(), 414 new_size_); 415} 416 417void UpdateAttempter::CreatePendingErrorEvent(AbstractAction* action, 418 ActionExitCode code) { 419 if (error_event_.get()) { 420 // This shouldn't really happen. 421 LOG(WARNING) << "There's already an existing pending error event."; 422 return; 423 } 424 425 // For now assume that Omaha response action failure means that 426 // there's no update so don't send an event. Also, double check that 427 // the failure has not occurred while sending an error event -- in 428 // which case don't schedule another. This shouldn't really happen 429 // but just in case... 430 if (action->Type() == OmahaResponseHandlerAction::StaticType() || 431 status_ == UPDATE_STATUS_REPORTING_ERROR_EVENT) { 432 return; 433 } 434 435 code = GetErrorCodeForAction(action, code); 436 error_event_.reset(new OmahaEvent(OmahaEvent::kTypeUpdateComplete, 437 OmahaEvent::kResultError, 438 code)); 439} 440 441bool UpdateAttempter::ScheduleErrorEventAction() { 442 if (error_event_.get() == NULL) 443 return false; 444 445 LOG(INFO) << "Update failed -- reporting the error event."; 446 shared_ptr<OmahaRequestAction> error_event_action( 447 new OmahaRequestAction(prefs_, 448 omaha_request_params_, 449 error_event_.release(), // Pass ownership. 450 new LibcurlHttpFetcher)); 451 actions_.push_back(shared_ptr<AbstractAction>(error_event_action)); 452 processor_->EnqueueAction(error_event_action.get()); 453 SetStatusAndNotify(UPDATE_STATUS_REPORTING_ERROR_EVENT); 454 processor_->StartProcessing(); 455 return true; 456} 457 458void UpdateAttempter::SetPriority(utils::ProcessPriority priority) { 459 if (priority_ == priority) { 460 return; 461 } 462 if (utils::SetProcessPriority(priority)) { 463 priority_ = priority; 464 LOG(INFO) << "Process priority = " << priority_; 465 } 466} 467 468void UpdateAttempter::SetupPriorityManagement() { 469 if (manage_priority_source_) { 470 LOG(ERROR) << "Process priority timeout source hasn't been destroyed."; 471 CleanupPriorityManagement(); 472 } 473 const int kPriorityTimeout = 10 * 60; // 10 minutes 474 manage_priority_source_ = g_timeout_source_new_seconds(kPriorityTimeout); 475 g_source_set_callback(manage_priority_source_, 476 StaticManagePriorityCallback, 477 this, 478 NULL); 479 g_source_attach(manage_priority_source_, NULL); 480 SetPriority(utils::kProcessPriorityLow); 481} 482 483void UpdateAttempter::CleanupPriorityManagement() { 484 if (manage_priority_source_) { 485 g_source_destroy(manage_priority_source_); 486 manage_priority_source_ = NULL; 487 } 488 SetPriority(utils::kProcessPriorityNormal); 489} 490 491gboolean UpdateAttempter::StaticManagePriorityCallback(gpointer data) { 492 return reinterpret_cast<UpdateAttempter*>(data)->ManagePriorityCallback(); 493} 494 495bool UpdateAttempter::ManagePriorityCallback() { 496 // If the current process priority is below normal, set it to normal 497 // and let GLib invoke this callback again. 498 if (utils::ComparePriorities(priority_, utils::kProcessPriorityNormal) < 0) { 499 SetPriority(utils::kProcessPriorityNormal); 500 return true; 501 } 502 // Set the priority to high and let GLib destroy the timeout source. 503 SetPriority(utils::kProcessPriorityHigh); 504 manage_priority_source_ = NULL; 505 return false; 506} 507 508void UpdateAttempter::DisableDeltaUpdateIfNeeded() { 509 int64_t delta_failures; 510 if (omaha_request_params_.delta_okay && 511 prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) && 512 delta_failures >= kMaxDeltaUpdateFailures) { 513 LOG(WARNING) << "Too many delta update failures, forcing full update."; 514 omaha_request_params_.delta_okay = false; 515 } 516} 517 518void UpdateAttempter::MarkDeltaUpdateFailure() { 519 CHECK(!is_full_update_); 520 int64_t delta_failures; 521 if (!prefs_->GetInt64(kPrefsDeltaUpdateFailures, &delta_failures) || 522 delta_failures < 0) { 523 delta_failures = 0; 524 } 525 prefs_->SetInt64(kPrefsDeltaUpdateFailures, ++delta_failures); 526} 527 528} // namespace chromeos_update_engine 529