update_attempter.cc revision 777dbfae01be95ac66f385cd96dc6e4b421fefe5
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 19#include "metrics/metrics_library.h" 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/set_bootable_flag_action.h" 29 30using std::tr1::shared_ptr; 31using std::string; 32using std::vector; 33 34namespace chromeos_update_engine { 35 36const char* kUpdateCompletedMarker = "/tmp/update_engine_autoupdate_completed"; 37 38namespace { 39// Returns true on success. 40bool GetCPUClockTime(struct timespec* out) { 41 return clock_gettime(CLOCK_REALTIME, out) == 0; 42} 43// Returns stop - start. 44struct timespec CPUClockTimeElapsed(const struct timespec& start, 45 const struct timespec& stop) { 46 CHECK(start.tv_sec >= 0); 47 CHECK(stop.tv_sec >= 0); 48 CHECK(start.tv_nsec >= 0); 49 CHECK(stop.tv_nsec >= 0); 50 51 const int64_t kOneBillion = 1000000000L; 52 const int64_t start64 = start.tv_sec * kOneBillion + start.tv_nsec; 53 const int64_t stop64 = stop.tv_sec * kOneBillion + stop.tv_nsec; 54 55 const int64_t result64 = stop64 - start64; 56 57 struct timespec ret; 58 ret.tv_sec = result64 / kOneBillion; 59 ret.tv_nsec = result64 % kOneBillion; 60 61 return ret; 62} 63bool CPUClockTimeGreaterThanHalfSecond(const struct timespec& spec) { 64 if (spec.tv_sec >= 1) 65 return true; 66 return (spec.tv_nsec > 500000000); 67} 68} 69 70const char* UpdateStatusToString(UpdateStatus status) { 71 switch (status) { 72 case UPDATE_STATUS_IDLE: 73 return "UPDATE_STATUS_IDLE"; 74 case UPDATE_STATUS_CHECKING_FOR_UPDATE: 75 return "UPDATE_STATUS_CHECKING_FOR_UPDATE"; 76 case UPDATE_STATUS_UPDATE_AVAILABLE: 77 return "UPDATE_STATUS_UPDATE_AVAILABLE"; 78 case UPDATE_STATUS_DOWNLOADING: 79 return "UPDATE_STATUS_DOWNLOADING"; 80 case UPDATE_STATUS_VERIFYING: 81 return "UPDATE_STATUS_VERIFYING"; 82 case UPDATE_STATUS_FINALIZING: 83 return "UPDATE_STATUS_FINALIZING"; 84 case UPDATE_STATUS_UPDATED_NEED_REBOOT: 85 return "UPDATE_STATUS_UPDATED_NEED_REBOOT"; 86 case UPDATE_STATUS_REPORTING_ERROR_EVENT: 87 return "UPDATE_STATUS_REPORTING_ERROR_EVENT"; 88 default: 89 return "unknown status"; 90 } 91} 92 93// Turns a generic kActionCodeError to a generic error code specific 94// to |action| (e.g., kActionCodeFilesystemCopierError). If |code| is 95// not kActionCodeError, or the action is not matched, returns |code| 96// unchanged. 97ActionExitCode GetErrorCodeForAction(AbstractAction* action, 98 ActionExitCode code) { 99 if (code != kActionCodeError) 100 return code; 101 102 const string type = action->Type(); 103 if (type == OmahaRequestAction::StaticType()) 104 return kActionCodeOmahaRequestError; 105 if (type == OmahaResponseHandlerAction::StaticType()) 106 return kActionCodeOmahaResponseHandlerError; 107 if (type == FilesystemCopierAction::StaticType()) 108 return kActionCodeFilesystemCopierError; 109 if (type == PostinstallRunnerAction::StaticType()) 110 return kActionCodePostinstallRunnerError; 111 if (type == SetBootableFlagAction::StaticType()) 112 return kActionCodeSetBootableFlagError; 113 114 return code; 115} 116 117void UpdateAttempter::Update() { 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 if (!omaha_request_params_.Init()) { 128 LOG(ERROR) << "Unable to initialize Omaha request device params."; 129 return; 130 } 131 CHECK(!processor_.IsRunning()); 132 processor_.set_delegate(this); 133 134 // Actions: 135 shared_ptr<OmahaRequestAction> update_check_action( 136 new OmahaRequestAction(omaha_request_params_, 137 NULL, 138 new LibcurlHttpFetcher)); 139 shared_ptr<OmahaResponseHandlerAction> response_handler_action( 140 new OmahaResponseHandlerAction); 141 shared_ptr<FilesystemCopierAction> filesystem_copier_action( 142 new FilesystemCopierAction(false)); 143 shared_ptr<FilesystemCopierAction> kernel_filesystem_copier_action( 144 new FilesystemCopierAction(true)); 145 shared_ptr<OmahaRequestAction> download_started_action( 146 new OmahaRequestAction(omaha_request_params_, 147 new OmahaEvent( 148 OmahaEvent::kTypeUpdateDownloadStarted), 149 new LibcurlHttpFetcher)); 150 shared_ptr<DownloadAction> download_action( 151 new DownloadAction(new LibcurlHttpFetcher)); 152 shared_ptr<OmahaRequestAction> download_finished_action( 153 new OmahaRequestAction(omaha_request_params_, 154 new OmahaEvent( 155 OmahaEvent::kTypeUpdateDownloadFinished), 156 new LibcurlHttpFetcher)); 157 shared_ptr<PostinstallRunnerAction> postinstall_runner_action_precommit( 158 new PostinstallRunnerAction(true)); 159 shared_ptr<SetBootableFlagAction> set_bootable_flag_action( 160 new SetBootableFlagAction); 161 shared_ptr<PostinstallRunnerAction> postinstall_runner_action_postcommit( 162 new PostinstallRunnerAction(false)); 163 shared_ptr<OmahaRequestAction> update_complete_action( 164 new OmahaRequestAction(omaha_request_params_, 165 new OmahaEvent(OmahaEvent::kTypeUpdateComplete), 166 new LibcurlHttpFetcher)); 167 168 download_action->set_delegate(this); 169 response_handler_action_ = response_handler_action; 170 171 actions_.push_back(shared_ptr<AbstractAction>(update_check_action)); 172 actions_.push_back(shared_ptr<AbstractAction>(response_handler_action)); 173 actions_.push_back(shared_ptr<AbstractAction>(filesystem_copier_action)); 174 actions_.push_back(shared_ptr<AbstractAction>( 175 kernel_filesystem_copier_action)); 176 actions_.push_back(shared_ptr<AbstractAction>(download_started_action)); 177 actions_.push_back(shared_ptr<AbstractAction>(download_action)); 178 actions_.push_back(shared_ptr<AbstractAction>(download_finished_action)); 179 actions_.push_back(shared_ptr<AbstractAction>( 180 postinstall_runner_action_precommit)); 181 actions_.push_back(shared_ptr<AbstractAction>(set_bootable_flag_action)); 182 actions_.push_back(shared_ptr<AbstractAction>( 183 postinstall_runner_action_postcommit)); 184 actions_.push_back(shared_ptr<AbstractAction>(update_complete_action)); 185 186 // Enqueue the actions 187 for (vector<shared_ptr<AbstractAction> >::iterator it = actions_.begin(); 188 it != actions_.end(); ++it) { 189 processor_.EnqueueAction(it->get()); 190 } 191 192 // Bond them together. We have to use the leaf-types when calling 193 // BondActions(). 194 BondActions(update_check_action.get(), 195 response_handler_action.get()); 196 BondActions(response_handler_action.get(), 197 filesystem_copier_action.get()); 198 BondActions(filesystem_copier_action.get(), 199 kernel_filesystem_copier_action.get()); 200 BondActions(kernel_filesystem_copier_action.get(), 201 download_action.get()); 202 BondActions(download_action.get(), 203 postinstall_runner_action_precommit.get()); 204 BondActions(postinstall_runner_action_precommit.get(), 205 set_bootable_flag_action.get()); 206 BondActions(set_bootable_flag_action.get(), 207 postinstall_runner_action_postcommit.get()); 208 209 SetStatusAndNotify(UPDATE_STATUS_CHECKING_FOR_UPDATE); 210 processor_.StartProcessing(); 211} 212 213void UpdateAttempter::CheckForUpdate() { 214 if (status_ != UPDATE_STATUS_IDLE) { 215 LOG(INFO) << "Check for update requested, but status is " 216 << UpdateStatusToString(status_) << ", so not checking."; 217 return; 218 } 219 Update(); 220} 221 222// Delegate methods: 223void UpdateAttempter::ProcessingDone(const ActionProcessor* processor, 224 ActionExitCode code) { 225 CHECK(response_handler_action_); 226 LOG(INFO) << "Processing Done."; 227 actions_.clear(); 228 229 if (status_ == UPDATE_STATUS_REPORTING_ERROR_EVENT) { 230 LOG(INFO) << "Error event sent."; 231 SetStatusAndNotify(UPDATE_STATUS_IDLE); 232 return; 233 } 234 235 if (code == kActionCodeSuccess) { 236 SetStatusAndNotify(UPDATE_STATUS_UPDATED_NEED_REBOOT); 237 utils::WriteFile(kUpdateCompletedMarker, "", 0); 238 239 // Report the time it took to update the system. 240 int64_t update_time = time(NULL) - last_checked_time_; 241 metrics_lib_->SendToUMA("Installer.UpdateTime", 242 static_cast<int>(update_time), // sample 243 1, // min = 1 second 244 20 * 60, // max = 20 minutes 245 50); // buckets 246 return; 247 } 248 249 LOG(INFO) << "Update failed."; 250 if (ScheduleErrorEventAction()) 251 return; 252 SetStatusAndNotify(UPDATE_STATUS_IDLE); 253} 254 255void UpdateAttempter::ProcessingStopped(const ActionProcessor* processor) { 256 download_progress_ = 0.0; 257 SetStatusAndNotify(UPDATE_STATUS_IDLE); 258 actions_.clear(); 259 error_event_.reset(NULL); 260} 261 262// Called whenever an action has finished processing, either successfully 263// or otherwise. 264void UpdateAttempter::ActionCompleted(ActionProcessor* processor, 265 AbstractAction* action, 266 ActionExitCode code) { 267 // Reset download progress regardless of whether or not the download action 268 // succeeded. 269 const string type = action->Type(); 270 if (type == DownloadAction::StaticType()) 271 download_progress_ = 0.0; 272 if (code != kActionCodeSuccess) { 273 // On failure, schedule an error event to be sent to Omaha. 274 CreatePendingErrorEvent(action, code); 275 return; 276 } 277 // Find out which action completed. 278 if (type == OmahaResponseHandlerAction::StaticType()) { 279 SetStatusAndNotify(UPDATE_STATUS_DOWNLOADING); 280 OmahaResponseHandlerAction* omaha_response_handler_action = 281 dynamic_cast<OmahaResponseHandlerAction*>(action); 282 CHECK(omaha_response_handler_action); 283 const InstallPlan& plan = omaha_response_handler_action->install_plan(); 284 last_checked_time_ = time(NULL); 285 // TODO(adlr): put version in InstallPlan 286 new_version_ = "0.0.0.0"; 287 new_size_ = plan.size; 288 } else if (type == DownloadAction::StaticType()) { 289 SetStatusAndNotify(UPDATE_STATUS_FINALIZING); 290 } 291} 292 293// Stop updating. An attempt will be made to record status to the disk 294// so that updates can be resumed later. 295void UpdateAttempter::Terminate() { 296 // TODO(adlr): implement this method. 297 NOTIMPLEMENTED(); 298} 299 300// Try to resume from a previously Terminate()d update. 301void UpdateAttempter::ResumeUpdating() { 302 // TODO(adlr): implement this method. 303 NOTIMPLEMENTED(); 304} 305 306void UpdateAttempter::BytesReceived(uint64_t bytes_received, uint64_t total) { 307 if (status_ != UPDATE_STATUS_DOWNLOADING) { 308 LOG(ERROR) << "BytesReceived called while not downloading."; 309 return; 310 } 311 download_progress_ = static_cast<double>(bytes_received) / 312 static_cast<double>(total); 313 // We self throttle here 314 timespec now; 315 now.tv_sec = 0; 316 now.tv_nsec = 0; 317 if (GetCPUClockTime(&now) && 318 CPUClockTimeGreaterThanHalfSecond( 319 CPUClockTimeElapsed(last_notify_time_, now))) { 320 SetStatusAndNotify(UPDATE_STATUS_DOWNLOADING); 321 } 322} 323 324bool UpdateAttempter::GetStatus(int64_t* last_checked_time, 325 double* progress, 326 std::string* current_operation, 327 std::string* new_version, 328 int64_t* new_size) { 329 *last_checked_time = last_checked_time_; 330 *progress = download_progress_; 331 *current_operation = UpdateStatusToString(status_); 332 *new_version = new_version_; 333 *new_size = new_size_; 334 return true; 335} 336 337void UpdateAttempter::SetStatusAndNotify(UpdateStatus status) { 338 status_ = status; 339 if (!dbus_service_) 340 return; 341 GetCPUClockTime(&last_notify_time_); 342 update_engine_service_emit_status_update( 343 dbus_service_, 344 last_checked_time_, 345 download_progress_, 346 UpdateStatusToString(status_), 347 new_version_.c_str(), 348 new_size_); 349} 350 351void UpdateAttempter::CreatePendingErrorEvent(AbstractAction* action, 352 ActionExitCode code) { 353 if (error_event_.get()) { 354 // This shouldn't really happen. 355 LOG(WARNING) << "There's already an existing pending error event."; 356 return; 357 } 358 359 // For now assume that Omaha response action failure means that 360 // there's no update so don't send an event. Also, double check that 361 // the failure has not occurred while sending an error event -- in 362 // which case don't schedule another. This shouldn't really happen 363 // but just in case... 364 if (action->Type() == OmahaResponseHandlerAction::StaticType() || 365 status_ == UPDATE_STATUS_REPORTING_ERROR_EVENT) { 366 return; 367 } 368 369 code = GetErrorCodeForAction(action, code); 370 error_event_.reset(new OmahaEvent(OmahaEvent::kTypeUpdateComplete, 371 OmahaEvent::kResultError, 372 code)); 373} 374 375bool UpdateAttempter::ScheduleErrorEventAction() { 376 if (error_event_.get() == NULL) 377 return false; 378 379 shared_ptr<OmahaRequestAction> error_event_action( 380 new OmahaRequestAction(omaha_request_params_, 381 error_event_.release(), // Pass ownership. 382 new LibcurlHttpFetcher)); 383 actions_.push_back(shared_ptr<AbstractAction>(error_event_action)); 384 processor_.EnqueueAction(error_event_action.get()); 385 SetStatusAndNotify(UPDATE_STATUS_REPORTING_ERROR_EVENT); 386 processor_.StartProcessing(); 387 return true; 388} 389 390} // namespace chromeos_update_engine 391