From 3bf5f99d1514dfdba43093d978aff2ac530e945a Mon Sep 17 00:00:00 2001 From: cschuet Date: Mon, 16 Feb 2015 07:50:56 -0800 Subject: [PATCH] Implementation of the remote reboot command. This CL introduces a remote device command which allows to remotely reboot the device. BUG=218540 Review URL: https://codereview.chromium.org/885203003 Cr-Commit-Position: refs/heads/master@{#316484} --- .../remote_commands/device_command_reboot_job.cc | 90 ++++++++++++++++++++++ .../remote_commands/device_command_reboot_job.h | 51 ++++++++++++ chrome/chrome_browser_chromeos.gypi | 2 + .../common/remote_commands/remote_command_job.cc | 10 +++ .../common/remote_commands/remote_command_job.h | 4 + .../remote_commands/remote_commands_queue.cc | 13 +--- .../policy/proto/device_management_backend.proto | 3 + 7 files changed, 163 insertions(+), 10 deletions(-) create mode 100644 chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.cc create mode 100644 chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.cc b/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.cc new file mode 100644 index 000000000000..f21b96fbbcbb --- /dev/null +++ b/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.cc @@ -0,0 +1,90 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h" + +#include + +#include "base/bind.h" +#include "base/location.h" +#include "base/logging.h" +#include "base/sys_info.h" +#include "base/time/time.h" +#include "chromeos/dbus/power_manager_client.h" +#include "policy/proto/device_management_backend.pb.h" + +namespace policy { + +namespace { + +// Determines the time, measured from the time of issue, after which the command +// queue will consider this command expired if the command has not been started. +const int kCommandExpirationTimeInMinutes = 10; + +// Determines the minimum uptime after which a reboot might be scheduled. Note: +// |kCommandExpirationTimeInMinutes| >= |kMinimumUptimeInMinutes| as +// otherwise, a valid command issued right after boot may time out. +const int kMinimumUptimeInMinutes = 10; + +} // namespace + +DeviceCommandRebootJob::DeviceCommandRebootJob( + chromeos::PowerManagerClient* power_manager_client) + : power_manager_client_(power_manager_client), weak_ptr_factory_(this) { + CHECK(power_manager_client_); +} + +DeviceCommandRebootJob::~DeviceCommandRebootJob() { +} + +enterprise_management::RemoteCommand_Type DeviceCommandRebootJob::GetType() + const { + return enterprise_management::RemoteCommand_Type_DEVICE_REBOOT; +} + +bool DeviceCommandRebootJob::IsExpired(base::Time now) { + return now > issued_time() + base::TimeDelta::FromMinutes( + kCommandExpirationTimeInMinutes); +} + +void DeviceCommandRebootJob::RunImpl( + const SucceededCallback& succeeded_callback, + const FailedCallback& failed_callback) { + // Determines the time delta between the command having been issued and the + // boot time of the system. + const base::TimeDelta uptime = + base::TimeDelta::FromMilliseconds(base::SysInfo::Uptime()); + const base::Time boot_time = base::Time::Now() - uptime; + const base::TimeDelta delta = boot_time - issued_time(); + // If the reboot command was issued before the system booted, we inform the + // server that the reboot succeeded. Otherwise, the reboot must still be + // performed and we invoke it. |kMinimumUptimeInMinutes| defines a lower limit + // on the uptime to avoid uninterruptable reboot loops. + if (delta > base::TimeDelta()) { + succeeded_callback.Run(nullptr); + return; + } + + const base::TimeDelta kZeroTimeDelta; + reboot_timer_.Start( + FROM_HERE, + std::max(base::TimeDelta::FromMinutes(kMinimumUptimeInMinutes) - uptime, + kZeroTimeDelta), + base::Bind(&DeviceCommandRebootJob::Reboot, + weak_ptr_factory_.GetWeakPtr())); +} + +void DeviceCommandRebootJob::TerminateImpl() { + weak_ptr_factory_.InvalidateWeakPtrs(); +} + +base::TimeDelta DeviceCommandRebootJob::GetCommmandTimeout() const { + return base::TimeDelta::FromMinutes(kMinimumUptimeInMinutes); +} + +void DeviceCommandRebootJob::Reboot() const { + power_manager_client_->RequestRestart(); +} + +} // namespace policy diff --git a/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h b/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h new file mode 100644 index 000000000000..dfc78cc12ef1 --- /dev/null +++ b/chrome/browser/chromeos/policy/remote_commands/device_command_reboot_job.h @@ -0,0 +1,51 @@ +// Copyright 2015 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_REBOOT_JOB_H_ +#define CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_REBOOT_JOB_H_ + +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "base/timer/timer.h" +#include "components/policy/core/common/remote_commands/remote_command_job.h" + +namespace chromeos { + +class PowerManagerClient; + +} // namespace chromeos + +namespace policy { + +class DeviceCommandRebootJob : public RemoteCommandJob { + public: + explicit DeviceCommandRebootJob( + chromeos::PowerManagerClient* power_manager_client); + ~DeviceCommandRebootJob() override; + + // RemoteCommandJob: + enterprise_management::RemoteCommand_Type GetType() const override; + + private: + // RemoteCommandJob: + bool IsExpired(base::Time now) override; + void RunImpl(const SucceededCallback& succeeded_callback, + const FailedCallback& failed_callback) override; + void TerminateImpl() override; + base::TimeDelta GetCommmandTimeout() const override; + + void Reboot() const; + + chromeos::PowerManagerClient* power_manager_client_; + + base::OneShotTimer reboot_timer_; + + base::WeakPtrFactory weak_ptr_factory_; + + DISALLOW_COPY_AND_ASSIGN(DeviceCommandRebootJob); +}; + +} // namespace policy + +#endif // CHROME_BROWSER_CHROMEOS_POLICY_REMOTE_COMMANDS_DEVICE_COMMAND_REBOOT_JOB_H_ diff --git a/chrome/chrome_browser_chromeos.gypi b/chrome/chrome_browser_chromeos.gypi index bb7ffcae3c3d..90748ac21779 100644 --- a/chrome/chrome_browser_chromeos.gypi +++ b/chrome/chrome_browser_chromeos.gypi @@ -820,6 +820,8 @@ 'browser/chromeos/policy/recommendation_restorer.h', 'browser/chromeos/policy/recommendation_restorer_factory.cc', 'browser/chromeos/policy/recommendation_restorer_factory.h', + 'browser/chromeos/policy/remote_commands/device_command_reboot_job.cc', + 'browser/chromeos/policy/remote_commands/device_command_reboot_job.h', 'browser/chromeos/policy/server_backed_device_state.cc', 'browser/chromeos/policy/server_backed_device_state.h', 'browser/chromeos/policy/server_backed_state_keys_broker.cc', diff --git a/components/policy/core/common/remote_commands/remote_command_job.cc b/components/policy/core/common/remote_commands/remote_command_job.cc index 839b1860ceb7..5fc3c99cea58 100644 --- a/components/policy/core/common/remote_commands/remote_command_job.cc +++ b/components/policy/core/common/remote_commands/remote_command_job.cc @@ -9,6 +9,12 @@ namespace policy { +namespace { + +const int kDefaultCommandTimeoutInMinutes = 3; + +} // namespace + namespace em = enterprise_management; RemoteCommandJob::~RemoteCommandJob() { @@ -90,6 +96,10 @@ void RemoteCommandJob::Terminate() { finished_callback_.Run(); } +base::TimeDelta RemoteCommandJob::GetCommmandTimeout() const { + return base::TimeDelta::FromMinutes(kDefaultCommandTimeoutInMinutes); +} + bool RemoteCommandJob::IsExecutionFinished() const { return status_ == SUCCEEDED || status_ == FAILED || status_ == TERMINATED; } diff --git a/components/policy/core/common/remote_commands/remote_command_job.h b/components/policy/core/common/remote_commands/remote_command_job.h index dd23747ead94..c941525db2e1 100644 --- a/components/policy/core/common/remote_commands/remote_command_job.h +++ b/components/policy/core/common/remote_commands/remote_command_job.h @@ -67,6 +67,10 @@ class POLICY_EXPORT RemoteCommandJob { // Returns the remote command type that this class is able to handle. virtual enterprise_management::RemoteCommand_Type GetType() const = 0; + // Returns the remote command timeout. If the command takes longer than the + // returned time interval to execute, the command queue will kill it. + virtual base::TimeDelta GetCommmandTimeout() const; + // Helpful accessors. UniqueIDType unique_id() const { return unique_id_; } base::Time issued_time() const { return issued_time_; } diff --git a/components/policy/core/common/remote_commands/remote_commands_queue.cc b/components/policy/core/common/remote_commands/remote_commands_queue.cc index 803fbca47b57..6234c9261843 100644 --- a/components/policy/core/common/remote_commands/remote_commands_queue.cc +++ b/components/policy/core/common/remote_commands/remote_commands_queue.cc @@ -10,17 +10,10 @@ #include "base/logging.h" #include "base/time/clock.h" #include "base/time/default_clock.h" -#include "base/time/time.h" #include "components/policy/core/common/remote_commands/remote_command_job.h" namespace policy { -namespace { - -const int kCommandTimeoutInMinutes = 3; - -} // namespace - RemoteCommandsQueue::RemoteCommandsQueue() : clock_(new base::DefaultClock()) { } @@ -77,9 +70,9 @@ void RemoteCommandsQueue::ScheduleNextJob() { running_command_.reset(incoming_commands_.front().release()); incoming_commands_.pop(); - execution_timeout_timer_.Start( - FROM_HERE, base::TimeDelta::FromMinutes(kCommandTimeoutInMinutes), this, - &RemoteCommandsQueue::OnCommandTimeout); + execution_timeout_timer_.Start(FROM_HERE, + running_command_->GetCommmandTimeout(), this, + &RemoteCommandsQueue::OnCommandTimeout); if (running_command_->Run(clock_->Now(), base::Bind(&RemoteCommandsQueue::CurrentJobFinished, diff --git a/components/policy/proto/device_management_backend.proto b/components/policy/proto/device_management_backend.proto index 900b9b65982d..c908dc9b754f 100644 --- a/components/policy/proto/device_management_backend.proto +++ b/components/policy/proto/device_management_backend.proto @@ -410,6 +410,9 @@ message RemoteCommand { enum Type { // Simple echo command for testing, will be ignored in production code. COMMAND_ECHO_TEST = -1; + + // Reboot the device. + DEVICE_REBOOT = 0; } // The command type. -- 2.11.4.GIT