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