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