automatic_reboot_manager.cc revision 868fa2fe829687343ffae624259930155e16dbd8
1// Copyright (c) 2013 The Chromium 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 "chrome/browser/chromeos/system/automatic_reboot_manager.h"
6
7#include <fcntl.h>
8#include <sys/stat.h>
9#include <sys/types.h>
10
11#include <algorithm>
12#include <string>
13
14#include "ash/shell.h"
15#include "ash/wm/user_activity_detector.h"
16#include "base/bind.h"
17#include "base/bind_helpers.h"
18#include "base/callback.h"
19#include "base/file_util.h"
20#include "base/files/file_path.h"
21#include "base/location.h"
22#include "base/logging.h"
23#include "base/memory/ref_counted.h"
24#include "base/path_service.h"
25#include "base/posix/eintr_wrapper.h"
26#include "base/prefs/pref_registry_simple.h"
27#include "base/prefs/pref_service.h"
28#include "base/single_thread_task_runner.h"
29#include "base/strings/string_number_conversions.h"
30#include "base/thread_task_runner_handle.h"
31#include "base/threading/sequenced_worker_pool.h"
32#include "base/threading/thread_restrictions.h"
33#include "base/time/tick_clock.h"
34#include "chrome/browser/browser_process.h"
35#include "chrome/browser/chromeos/login/user_manager.h"
36#include "chrome/common/chrome_notification_types.h"
37#include "chrome/common/pref_names.h"
38#include "chromeos/chromeos_paths.h"
39#include "chromeos/chromeos_switches.h"
40#include "chromeos/dbus/dbus_thread_manager.h"
41#include "content/public/browser/browser_thread.h"
42#include "content/public/browser/notification_details.h"
43#include "content/public/browser/notification_service.h"
44#include "content/public/browser/notification_source.h"
45
46namespace chromeos {
47namespace system {
48
49namespace {
50
51const int kMinRebootUptimeMs = 60 * 60 * 1000;     // 1 hour.
52const int kLoginManagerIdleTimeoutMs = 60 * 1000;  // 60 seconds.
53const int kGracePeriodMs = 24 * 60 * 60 * 1000;    // 24 hours.
54const int kOneKilobyte = 1 << 10;                  // 1 kB in bytes.
55
56base::TimeDelta ReadTimeDeltaFromFile(const base::FilePath& path) {
57  base::ThreadRestrictions::AssertIOAllowed();
58  int fd = HANDLE_EINTR(open(path.value().c_str(), O_RDONLY | O_NOFOLLOW));
59  if (fd < 0)
60    return base::TimeDelta();
61  file_util::ScopedFD fd_closer(&fd);
62
63  std::string contents;
64  char buffer[kOneKilobyte];
65  ssize_t length;
66  while ((length = read(fd, buffer, sizeof(buffer))) > 0)
67    contents.append(buffer, length);
68
69  double seconds;
70  if (!base::StringToDouble(contents.substr(0, contents.find(' ')), &seconds) ||
71      seconds < 0.0) {
72    return base::TimeDelta();
73  }
74  return base::TimeDelta::FromMilliseconds(seconds * 1000.0);
75}
76
77void GetSystemEventTimes(
78    scoped_refptr<base::SingleThreadTaskRunner> reply_task_runner,
79    base::Callback<void(
80        const AutomaticRebootManager::SystemEventTimes&)> reply) {
81  base::FilePath uptime_file;
82  CHECK(PathService::Get(chromeos::FILE_UPTIME, &uptime_file));
83  base::FilePath update_reboot_needed_uptime_file;
84  CHECK(PathService::Get(chromeos::FILE_UPDATE_REBOOT_NEEDED_UPTIME,
85                         &update_reboot_needed_uptime_file));
86  reply_task_runner->PostTask(FROM_HERE, base::Bind(reply,
87      AutomaticRebootManager::SystemEventTimes(
88          ReadTimeDeltaFromFile(uptime_file),
89          ReadTimeDeltaFromFile(update_reboot_needed_uptime_file))));
90}
91
92void SaveUpdateRebootNeededUptime() {
93  base::ThreadRestrictions::AssertIOAllowed();
94  const base::TimeDelta kZeroTimeDelta;
95
96  base::FilePath update_reboot_needed_uptime_file;
97  CHECK(PathService::Get(chromeos::FILE_UPDATE_REBOOT_NEEDED_UPTIME,
98                         &update_reboot_needed_uptime_file));
99  const base::TimeDelta last_update_reboot_needed_uptime =
100      ReadTimeDeltaFromFile(update_reboot_needed_uptime_file);
101  if (last_update_reboot_needed_uptime != kZeroTimeDelta)
102    return;
103
104  base::FilePath uptime_file;
105  CHECK(PathService::Get(chromeos::FILE_UPTIME, &uptime_file));
106  const base::TimeDelta uptime = ReadTimeDeltaFromFile(uptime_file);
107  if (uptime == kZeroTimeDelta)
108    return;
109
110  int fd = HANDLE_EINTR(open(update_reboot_needed_uptime_file.value().c_str(),
111                             O_CREAT | O_WRONLY | O_TRUNC | O_NOFOLLOW,
112                             0666));
113  if (fd < 0)
114    return;
115  file_util::ScopedFD fd_closer(&fd);
116
117  std::string update_reboot_needed_uptime =
118      base::DoubleToString(uptime.InSecondsF());
119  file_util::WriteFileDescriptor(fd,
120                                 update_reboot_needed_uptime.c_str(),
121                                 update_reboot_needed_uptime.size());
122}
123
124}  // namespace
125
126AutomaticRebootManager::SystemEventTimes::SystemEventTimes()
127    : has_boot_time(false),
128      has_update_reboot_needed_time(false) {
129}
130
131AutomaticRebootManager::SystemEventTimes::SystemEventTimes(
132    const base::TimeDelta& uptime,
133    const base::TimeDelta& update_reboot_needed_uptime)
134    : has_boot_time(false),
135      has_update_reboot_needed_time(false) {
136  const base::TimeDelta kZeroTimeDelta;
137  if (uptime == kZeroTimeDelta)
138    return;
139  boot_time = base::TimeTicks::Now() - uptime;
140  has_boot_time = true;
141  if (update_reboot_needed_uptime == kZeroTimeDelta)
142    return;
143  // Calculate the time at which an update was applied and a reboot became
144  // necessary in base::TimeTicks::Now() ticks.
145  update_reboot_needed_time = boot_time + update_reboot_needed_uptime;
146  has_update_reboot_needed_time = true;
147}
148
149AutomaticRebootManager::AutomaticRebootManager(
150    scoped_ptr<base::TickClock> clock)
151    : clock_(clock.Pass()),
152      have_boot_time_(false),
153      have_update_reboot_needed_time_(false),
154      reboot_requested_(false),
155      weak_ptr_factory_(this) {
156  local_state_registrar_.Init(g_browser_process->local_state());
157  local_state_registrar_.Add(prefs::kUptimeLimit,
158                             base::Bind(&AutomaticRebootManager::Reschedule,
159                                        base::Unretained(this)));
160  local_state_registrar_.Add(prefs::kRebootAfterUpdate,
161                             base::Bind(&AutomaticRebootManager::Reschedule,
162                                        base::Unretained(this)));
163  notification_registrar_.Add(this, chrome::NOTIFICATION_APP_TERMINATING,
164      content::NotificationService::AllSources());
165
166  DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
167  dbus_thread_manager->GetPowerManagerClient()->AddObserver(this);
168  dbus_thread_manager->GetUpdateEngineClient()->AddObserver(this);
169
170  // If no user is logged in, a reboot may be performed whenever the user is
171  // idle. Start listening for user activity to determine whether the user is
172  // idle or not.
173  if (!UserManager::Get()->IsUserLoggedIn()) {
174    if (ash::Shell::HasInstance())
175      ash::Shell::GetInstance()->user_activity_detector()->AddObserver(this);
176    notification_registrar_.Add(this, chrome::NOTIFICATION_LOGIN_USER_CHANGED,
177        content::NotificationService::AllSources());
178    login_screen_idle_timer_.reset(
179        new base::OneShotTimer<AutomaticRebootManager>);
180    OnUserActivity();
181  }
182
183  // In a regular browser, base::ThreadTaskRunnerHandle::Get() and
184  // base::MessageLoopProxy::current() return pointers to the same object.
185  // In unit tests, using base::ThreadTaskRunnerHandle::Get() has the advantage
186  // that it allows a custom base::SingleThreadTaskRunner to be injected.
187  content::BrowserThread::GetBlockingPool()->PostWorkerTaskWithShutdownBehavior(
188      FROM_HERE,
189      base::Bind(&GetSystemEventTimes,
190                 base::ThreadTaskRunnerHandle::Get(),
191                 base::Bind(&AutomaticRebootManager::Init,
192                            weak_ptr_factory_.GetWeakPtr())),
193      base::SequencedWorkerPool::SKIP_ON_SHUTDOWN);
194}
195
196AutomaticRebootManager::~AutomaticRebootManager() {
197  DBusThreadManager* dbus_thread_manager = DBusThreadManager::Get();
198  dbus_thread_manager->GetPowerManagerClient()->RemoveObserver(this);
199  dbus_thread_manager->GetUpdateEngineClient()->RemoveObserver(this);
200  if (ash::Shell::HasInstance())
201    ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
202}
203
204void AutomaticRebootManager::SystemResumed(
205    const base::TimeDelta& sleep_duration) {
206  MaybeReboot(true);
207}
208
209void AutomaticRebootManager::UpdateStatusChanged(
210    const UpdateEngineClient::Status& status) {
211  // Ignore repeated notifications that a reboot is necessary. This is important
212  // so that only the time of the first notification is taken into account and
213  // repeated notifications do not postpone the reboot request and grace period.
214  if (status.status != UpdateEngineClient::UPDATE_STATUS_UPDATED_NEED_REBOOT ||
215      !have_boot_time_ || have_update_reboot_needed_time_) {
216    return;
217  }
218
219  content::BrowserThread::PostBlockingPoolTask(
220      FROM_HERE, base::Bind(&SaveUpdateRebootNeededUptime));
221
222  update_reboot_needed_time_ = clock_->NowTicks();
223  have_update_reboot_needed_time_ = true;
224
225  Reschedule();
226}
227
228void AutomaticRebootManager::OnUserActivity() {
229  if (!login_screen_idle_timer_)
230    return;
231
232  // Destroying and re-creating the timer ensures that Start() posts a fresh
233  // task with a delay of exactly |kLoginManagerIdleTimeoutMs|, ensuring that
234  // the timer fires predictably in tests.
235  login_screen_idle_timer_.reset(
236      new base::OneShotTimer<AutomaticRebootManager>);
237  login_screen_idle_timer_->Start(
238      FROM_HERE,
239      base::TimeDelta::FromMilliseconds(kLoginManagerIdleTimeoutMs),
240      base::Bind(&AutomaticRebootManager::MaybeReboot,
241                 base::Unretained(this),
242                 false));
243}
244
245void AutomaticRebootManager::Observe(
246    int type,
247    const content::NotificationSource& source,
248    const content::NotificationDetails& details) {
249  if (type == chrome::NOTIFICATION_APP_TERMINATING) {
250    if (UserManager::Get()->IsUserLoggedIn()) {
251      // The browser is terminating during a session, either because the session
252      // is ending or because the browser is being restarted.
253      MaybeReboot(true);
254    }
255  } else if (type == chrome::NOTIFICATION_LOGIN_USER_CHANGED) {
256    // A session is starting. Stop listening for user activity as it no longer
257    // is a relevant criterion.
258    if (ash::Shell::HasInstance())
259      ash::Shell::GetInstance()->user_activity_detector()->RemoveObserver(this);
260    notification_registrar_.Remove(
261        this, chrome::NOTIFICATION_LOGIN_USER_CHANGED,
262        content::NotificationService::AllSources());
263    login_screen_idle_timer_.reset();
264  } else {
265    NOTREACHED();
266  }
267}
268
269// static
270void AutomaticRebootManager::RegisterPrefs(PrefRegistrySimple* registry) {
271  registry->RegisterIntegerPref(prefs::kUptimeLimit, 0);
272  registry->RegisterBooleanPref(prefs::kRebootAfterUpdate, false);
273}
274
275void AutomaticRebootManager::Init(const SystemEventTimes& system_event_times) {
276  const base::TimeDelta offset = clock_->NowTicks() - base::TimeTicks::Now();
277  if (system_event_times.has_boot_time) {
278    // Convert the time at which the device was booted to |clock_| ticks.
279    boot_time_ = system_event_times.boot_time + offset;
280    have_boot_time_ = true;
281  }
282  if (system_event_times.has_update_reboot_needed_time) {
283    // Convert the time at which a reboot became necessary to |clock_| ticks.
284    const base::TimeTicks update_reboot_needed_time =
285        system_event_times.update_reboot_needed_time + offset;
286    update_reboot_needed_time_ = update_reboot_needed_time;
287    have_update_reboot_needed_time_ = true;
288  } else {
289    UpdateStatusChanged(
290        DBusThreadManager::Get()->GetUpdateEngineClient()->GetLastStatus());
291  }
292
293  Reschedule();
294}
295
296void AutomaticRebootManager::Reschedule() {
297  // Safeguard against reboot loops under error conditions: If the boot time is
298  // unavailable because /proc/uptime could not be read, do nothing.
299  if (!have_boot_time_)
300    return;
301
302  // Assume that no reboot has been requested.
303  reboot_requested_ = false;
304
305  const base::TimeDelta kZeroTimeDelta;
306
307  // If an uptime limit is set, calculate the time at which it should cause a
308  // reboot to be requested.
309  const base::TimeDelta uptime_limit = base::TimeDelta::FromSeconds(
310      local_state_registrar_.prefs()->GetInteger(prefs::kUptimeLimit));
311  base::TimeTicks reboot_request_time = boot_time_ + uptime_limit;
312  bool have_reboot_request_time = uptime_limit != kZeroTimeDelta;
313
314  // If the policy to automatically reboot after an update is enabled and an
315  // update has been applied, set the time at which a reboot should be
316  // requested to the minimum of its current value and the time when the reboot
317  // became necessary.
318  if (have_update_reboot_needed_time_ &&
319      local_state_registrar_.prefs()->GetBoolean(prefs::kRebootAfterUpdate) &&
320      (!have_reboot_request_time ||
321       update_reboot_needed_time_ < reboot_request_time)) {
322    reboot_request_time = update_reboot_needed_time_;
323    have_reboot_request_time = true;
324  }
325
326  // If no reboot should be requested, remove any grace period.
327  if (!have_reboot_request_time) {
328    grace_start_timer_.reset();
329    grace_end_timer_.reset();
330    return;
331  }
332
333  // Safeguard against reboot loops: Ensure that the uptime after which a reboot
334  // is actually requested and the grace period begins is never less than
335  // |kMinRebootUptimeMs|.
336  const base::TimeTicks now = clock_->NowTicks();
337  const base::TimeTicks grace_start_time = std::max(reboot_request_time,
338      boot_time_ + base::TimeDelta::FromMilliseconds(kMinRebootUptimeMs));
339  // Set up a timer for the start of the grace period. If the grace period
340  // started in the past, the timer is still used with its delay set to zero.
341  if (!grace_start_timer_)
342    grace_start_timer_.reset(new base::OneShotTimer<AutomaticRebootManager>);
343  grace_start_timer_->Start(FROM_HERE,
344                            std::max(grace_start_time - now, kZeroTimeDelta),
345                            base::Bind(&AutomaticRebootManager::RequestReboot,
346                                       base::Unretained(this)));
347
348  const base::TimeTicks grace_end_time = grace_start_time +
349      base::TimeDelta::FromMilliseconds(kGracePeriodMs);
350  // Set up a timer for the end of the grace period. If the grace period ended
351  // in the past, the timer is still used with its delay set to zero.
352  if (!grace_end_timer_)
353    grace_end_timer_.reset(new base::OneShotTimer<AutomaticRebootManager>);
354  grace_end_timer_->Start(FROM_HERE,
355                          std::max(grace_end_time - now, kZeroTimeDelta),
356                          base::Bind(&AutomaticRebootManager::Reboot,
357                                     base::Unretained(this)));
358}
359
360void AutomaticRebootManager::RequestReboot() {
361  reboot_requested_ = true;
362  MaybeReboot(false);
363}
364
365void AutomaticRebootManager::MaybeReboot(bool ignore_session) {
366  // Do not reboot if any of the following applies:
367  // * No reboot has been requested.
368  // * A user is interacting with the login screen.
369  // * A session is in progress and |ignore_session| is not set.
370  if (!reboot_requested_ ||
371      (login_screen_idle_timer_ && login_screen_idle_timer_->IsRunning()) ||
372      (!ignore_session && UserManager::Get()->IsUserLoggedIn())) {
373    return;
374  }
375
376  Reboot();
377}
378
379void AutomaticRebootManager::Reboot() {
380  // If a non-kiosk-app session is in progress, do not reboot.
381  if (UserManager::Get()->IsUserLoggedIn() &&
382      !UserManager::Get()->IsLoggedInAsKioskApp()) {
383    return;
384  }
385
386  login_screen_idle_timer_.reset();
387  grace_start_timer_.reset();
388  grace_end_timer_.reset();
389  DBusThreadManager::Get()->GetPowerManagerClient()->RequestRestart();
390}
391
392}  // namespace system
393}  // namespace chromeos
394