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