Commit fecb18b7 authored by lukasza's avatar lukasza Committed by Commit bot

Suspend (rather than shut down) the host upon policy errors.

This changelist primarily changes HostProcess::OnPolicyError, so that it
doesn't anymore shut down the host, but instead restarts the host and prevents
the host from starting again until a valid policy has been read (the
"preventing" functionality has already been present in the form of
StartHostIfReady method for some time now).

This changelist also simplifies HostSignalingManager's functionality related to
sending host-offline-reason.  Previously HostProcess was destroyed while
HostSignalingManager was trying to send host-offline-reason.  Things are
simpler if HostProcess waits until HostSignalingManager sends
host-offline-reason or times out.  This synchronization is especially helpful
when restarting the host (to avoid having to deal with an old
HostSignalingManager from previous SendHostOfflineReason attempt, while we
construct a new HostSignalingManager when starting a host after a reset).

This changelist also introduces a separate host-offline-reason for policy
errors (and updates WebApp resources strings to reflect that).  Note that since
we are not shutting down the host, we probably cannot use one of HostExitCodes
as the host-offline-reason.

Testing done:
1. On Linux manually introduce policy errors at startup, verify host doesn't
   start and sends host-offline-reason, fix errors, verify host starts,
   reintroduce errors, verify host suspends and sends host-offline-reason, fix
   errors, verify host starts again.
2. Verify WebApp UI shows correct tooltip text for the new host-offline-reason.

BUG=455903

Review URL: https://codereview.chromium.org/910403002

Cr-Commit-Position: refs/heads/master@{#317708}
parent 1e259c47
...@@ -48,8 +48,9 @@ class IqSender; ...@@ -48,8 +48,9 @@ class IqSender;
// accept new connections from a client, but the rem:heartbeat xml element can // accept new connections from a client, but the rem:heartbeat xml element can
// optionally include a rem:host-offline-reason attribute, which indicates that // optionally include a rem:host-offline-reason attribute, which indicates that
// the host cannot accept connections from the client (and might possibly be // the host cannot accept connections from the client (and might possibly be
// shutting down). The value of the host-offline-reason attribute can be a // shutting down). The value of the host-offline-reason attribute can be either
// string from host_exit_codes.cc (i.e. "INVALID_HOST_CONFIGURATION" string). // a string from host_exit_codes.cc (i.e. "INVALID_HOST_CONFIGURATION" string)
// or one of kHostOfflineReasonXxx constants (i.e. "POLICY_READ_ERROR" string).
// //
// The sequence-id attribute of the heartbeat is a zero-based incrementally // The sequence-id attribute of the heartbeat is a zero-based incrementally
// increasing integer unique to each heartbeat from a single host. // increasing integer unique to each heartbeat from a single host.
......
...@@ -19,21 +19,16 @@ ...@@ -19,21 +19,16 @@
namespace remoting { namespace remoting {
HostSignalingManager::HostSignalingManager( HostSignalingManager::HostSignalingManager(
scoped_ptr<base::WeakPtrFactory<Listener>> weak_factory_for_listener,
const scoped_refptr<AutoThreadTaskRunner>& network_task_runner,
scoped_ptr<SignalStrategy> signal_strategy, scoped_ptr<SignalStrategy> signal_strategy,
scoped_ptr<SignalingConnector> signaling_connector, scoped_ptr<SignalingConnector> signaling_connector,
scoped_ptr<HeartbeatSender> heartbeat_sender) scoped_ptr<HeartbeatSender> heartbeat_sender)
: weak_factory_for_listener_(weak_factory_for_listener.Pass()), : signal_strategy_(signal_strategy.Pass()),
network_task_runner_(network_task_runner),
signal_strategy_(signal_strategy.Pass()),
signaling_connector_(signaling_connector.Pass()), signaling_connector_(signaling_connector.Pass()),
heartbeat_sender_(heartbeat_sender.Pass()) { heartbeat_sender_(heartbeat_sender.Pass()) {
} }
scoped_ptr<HostSignalingManager> HostSignalingManager::Create( scoped_ptr<HostSignalingManager> HostSignalingManager::Create(
Listener* listener, Listener* listener,
const scoped_refptr<AutoThreadTaskRunner>& network_task_runner,
const scoped_refptr<net::URLRequestContextGetter>& const scoped_refptr<net::URLRequestContextGetter>&
url_request_context_getter, url_request_context_getter,
const XmppSignalStrategy::XmppServerConfig& xmpp_server_config, const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
...@@ -42,11 +37,6 @@ scoped_ptr<HostSignalingManager> HostSignalingManager::Create( ...@@ -42,11 +37,6 @@ scoped_ptr<HostSignalingManager> HostSignalingManager::Create(
const scoped_refptr<const RsaKeyPair>& host_key_pair, const scoped_refptr<const RsaKeyPair>& host_key_pair,
const std::string& directory_bot_jid, const std::string& directory_bot_jid,
scoped_ptr<OAuthTokenGetter::OAuthCredentials> oauth_credentials) { scoped_ptr<OAuthTokenGetter::OAuthCredentials> oauth_credentials) {
DCHECK(network_task_runner->BelongsToCurrentThread());
scoped_ptr<base::WeakPtrFactory<Listener>> weak_factory(
new base::WeakPtrFactory<Listener>(listener));
scoped_ptr<XmppSignalStrategy> signal_strategy( scoped_ptr<XmppSignalStrategy> signal_strategy(
new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(), new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
url_request_context_getter, xmpp_server_config)); url_request_context_getter, xmpp_server_config));
...@@ -56,7 +46,7 @@ scoped_ptr<HostSignalingManager> HostSignalingManager::Create( ...@@ -56,7 +46,7 @@ scoped_ptr<HostSignalingManager> HostSignalingManager::Create(
scoped_ptr<SignalingConnector> signaling_connector(new SignalingConnector( scoped_ptr<SignalingConnector> signaling_connector(new SignalingConnector(
signal_strategy.get(), dns_blackhole_checker.Pass(), signal_strategy.get(), dns_blackhole_checker.Pass(),
base::Bind(&Listener::OnAuthFailed, weak_factory->GetWeakPtr()))); base::Bind(&Listener::OnAuthFailed, base::Unretained(listener))));
if (!oauth_credentials->refresh_token.empty()) { if (!oauth_credentials->refresh_token.empty()) {
scoped_ptr<OAuthTokenGetter> oauth_token_getter(new OAuthTokenGetter( scoped_ptr<OAuthTokenGetter> oauth_token_getter(new OAuthTokenGetter(
...@@ -66,45 +56,27 @@ scoped_ptr<HostSignalingManager> HostSignalingManager::Create( ...@@ -66,45 +56,27 @@ scoped_ptr<HostSignalingManager> HostSignalingManager::Create(
} }
scoped_ptr<HeartbeatSender> heartbeat_sender(new HeartbeatSender( scoped_ptr<HeartbeatSender> heartbeat_sender(new HeartbeatSender(
base::Bind(&Listener::OnHeartbeatSuccessful, weak_factory->GetWeakPtr()), base::Bind(&Listener::OnHeartbeatSuccessful, base::Unretained(listener)),
base::Bind(&Listener::OnUnknownHostIdError, weak_factory->GetWeakPtr()), base::Bind(&Listener::OnUnknownHostIdError, base::Unretained(listener)),
host_id, signal_strategy.get(), host_key_pair, directory_bot_jid)); host_id, signal_strategy.get(), host_key_pair, directory_bot_jid));
return scoped_ptr<HostSignalingManager>(new HostSignalingManager( return scoped_ptr<HostSignalingManager>(new HostSignalingManager(
weak_factory.Pass(), network_task_runner, signal_strategy.Pass(), signal_strategy.Pass(), signaling_connector.Pass(),
signaling_connector.Pass(), heartbeat_sender.Pass())); heartbeat_sender.Pass()));
} }
HostSignalingManager::~HostSignalingManager() { HostSignalingManager::~HostSignalingManager() {
DCHECK(network_task_runner_->BelongsToCurrentThread()); DCHECK(thread_checker_.CalledOnValidThread());
} }
void HostSignalingManager::SendHostOfflineReasonAndDelete( void HostSignalingManager::SendHostOfflineReason(
const std::string& host_offline_reason, const std::string& host_offline_reason,
const base::TimeDelta& timeout) { const base::TimeDelta& timeout,
DCHECK(network_task_runner_->BelongsToCurrentThread()); const base::Callback<void(bool success)>& ack_callback) {
DCHECK(thread_checker_.CalledOnValidThread());
HOST_LOG << "SendHostOfflineReason: sending " << host_offline_reason; HOST_LOG << "SendHostOfflineReason: sending " << host_offline_reason << ".";
heartbeat_sender_->SetHostOfflineReason(host_offline_reason, timeout,
// Ensure that any subsequent callbacks from the HeartbeatSender or ack_callback);
// SignalingConnector don't touch the Listener.
weak_factory_for_listener_->InvalidateWeakPtrs();
heartbeat_sender_->SetHostOfflineReason(
host_offline_reason, timeout,
base::Bind(&HostSignalingManager::OnHostOfflineReasonAck,
base::Unretained(this)));
}
void HostSignalingManager::OnHostOfflineReasonAck(bool success) {
DCHECK(network_task_runner_->BelongsToCurrentThread());
if (success) {
HOST_LOG << "SendHostOfflineReason: succeeded";
} else {
HOST_LOG << "SendHostOfflineReason: timed out";
}
delete this;
} }
} // namespace remoting } // namespace remoting
...@@ -9,8 +9,7 @@ ...@@ -9,8 +9,7 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/scoped_ptr.h" #include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h" #include "base/threading/thread_checker.h"
#include "remoting/base/auto_thread_task_runner.h"
#include "remoting/base/rsa_key_pair.h" #include "remoting/base/rsa_key_pair.h"
#include "remoting/host/oauth_token_getter.h" #include "remoting/host/oauth_token_getter.h"
#include "remoting/signaling/xmpp_signal_strategy.h" #include "remoting/signaling/xmpp_signal_strategy.h"
...@@ -19,22 +18,15 @@ namespace base { ...@@ -19,22 +18,15 @@ namespace base {
class TimeDelta; class TimeDelta;
} }
namespace net {
class NetworkChangeNotifier;
}
namespace remoting { namespace remoting {
class ChromotingHostContext;
class DnsBlackholeChecker;
class HeartbeatSender; class HeartbeatSender;
class OAuthTokenGetter; class OAuthTokenGetter;
class SignalStrategy; class SignalStrategy;
class SignalingConnector; class SignalingConnector;
// HostSignalingManager has 2 functions: // HostSignalingManager manages objects needed for sending regular heartbeats to
// 1. Keep sending regular heartbeats to the Chromoting Directory. // the Chromoting Directory.
// 2. Keep the host process alive while sending host-offline-reason heartbeat.
class HostSignalingManager { class HostSignalingManager {
public: public:
class Listener { class Listener {
...@@ -55,12 +47,10 @@ class HostSignalingManager { ...@@ -55,12 +47,10 @@ class HostSignalingManager {
// Probably necessitates refactoring HostProcess to extract a new // Probably necessitates refactoring HostProcess to extract a new
// class to read and store config/policy/cmdline values. // class to read and store config/policy/cmdline values.
// //
// |listener| has to be valid until either // |listener| has to be valid until the returned HostSignalingManager is
// 1) the returned HostSignalingManager is destroyed // destroyed.
// or 2) SendHostOfflineReasonAndDelete is called.
static scoped_ptr<HostSignalingManager> Create( static scoped_ptr<HostSignalingManager> Create(
Listener* listener, Listener* listener,
const scoped_refptr<AutoThreadTaskRunner>& network_task_runner,
const scoped_refptr<net::URLRequestContextGetter>& const scoped_refptr<net::URLRequestContextGetter>&
url_request_context_getter, url_request_context_getter,
const XmppSignalStrategy::XmppServerConfig& xmpp_server_config, const XmppSignalStrategy::XmppServerConfig& xmpp_server_config,
...@@ -77,37 +67,23 @@ class HostSignalingManager { ...@@ -77,37 +67,23 @@ class HostSignalingManager {
SignalStrategy* signal_strategy() { return signal_strategy_.get(); } SignalStrategy* signal_strategy() { return signal_strategy_.get(); }
// Kicks off sending a heartbeat containing a host-offline-reason attribute. // Kicks off sending a heartbeat containing a host-offline-reason attribute.
// Prevents future calls to the |listener| provided to the Create method. // Will call |ack_callback| once either the bot acks receiving the
// |host_offline_reason|, or the |timeout| is reached.
// //
// Will delete |this| once either the bot acks receiving the // For discussion of allowed values for |host_offline_reason| argument,
// |host_offline_reason|, or the |timeout| is reached. Deleting // please see the description of rem:host-offline-reason xml attribute in
// |this| will release |network_task_runner_| and allow the host // the class-level comments for HeartbeatReasonSender.
// process to exit. void SendHostOfflineReason(
void SendHostOfflineReasonAndDelete(const std::string& host_offline_reason, const std::string& host_offline_reason,
const base::TimeDelta& timeout); const base::TimeDelta& timeout,
const base::Callback<void(bool success)>& ack_callback);
private: private:
HostSignalingManager( HostSignalingManager(
scoped_ptr<base::WeakPtrFactory<Listener>> weak_factory_for_listener,
const scoped_refptr<AutoThreadTaskRunner>& network_task_runner,
scoped_ptr<SignalStrategy> signal_strategy, scoped_ptr<SignalStrategy> signal_strategy,
scoped_ptr<SignalingConnector> signaling_connector, scoped_ptr<SignalingConnector> signaling_connector,
scoped_ptr<HeartbeatSender> heartbeat_sender); scoped_ptr<HeartbeatSender> heartbeat_sender);
void OnHostOfflineReasonAck(bool success);
// Used to bind HeartbeatSender and SignalingConnector callbacks to |listener|
// in a way that allows "detaching" the |listener| when either |this| is
// destroyed or when SendHostOfflineReasonAndDelete method is called.
scoped_ptr<base::WeakPtrFactory<Listener>> weak_factory_for_listener_;
// By holding a reference to |network_task_runner_|, HostSignalingManager is
// extending the lifetime of the host process. This is needed for the case
// where an instance of HostProcess has already been destroyed, but we want
// to keep the process running while we try to establish a connection and send
// host-offline-reason.
scoped_refptr<AutoThreadTaskRunner> network_task_runner_;
// |heartbeat_sender_| and |signaling_connector_| have to be destroyed before // |heartbeat_sender_| and |signaling_connector_| have to be destroyed before
// |signal_strategy_| because their destructors need to call // |signal_strategy_| because their destructors need to call
// signal_strategy_->RemoveListener(this) // signal_strategy_->RemoveListener(this)
...@@ -115,6 +91,9 @@ class HostSignalingManager { ...@@ -115,6 +91,9 @@ class HostSignalingManager {
scoped_ptr<SignalingConnector> signaling_connector_; scoped_ptr<SignalingConnector> signaling_connector_;
scoped_ptr<HeartbeatSender> heartbeat_sender_; scoped_ptr<HeartbeatSender> heartbeat_sender_;
// Used to verify thread-safe usage.
base::ThreadChecker thread_checker_;
DISALLOW_COPY_AND_ASSIGN(HostSignalingManager); DISALLOW_COPY_AND_ASSIGN(HostSignalingManager);
}; };
......
...@@ -145,6 +145,7 @@ void PolicyWatcher::UpdatePolicies( ...@@ -145,6 +145,7 @@ void PolicyWatcher::UpdatePolicies(
} }
void PolicyWatcher::SignalPolicyError() { void PolicyWatcher::SignalPolicyError() {
old_policies_->Clear();
policy_error_callback_.Run(); policy_error_callback_.Run();
} }
......
...@@ -147,6 +147,12 @@ const int kShutdownTimeoutSeconds = 15; ...@@ -147,6 +147,12 @@ const int kShutdownTimeoutSeconds = 15;
// before continuing normal process shutdown. // before continuing normal process shutdown.
const int kHostOfflineReasonTimeoutSeconds = 10; const int kHostOfflineReasonTimeoutSeconds = 10;
// Host offline reasons not associated with shutting down the host process
// and therefore not expressible through HostExitCodes enum.
const char kHostOfflineReasonPolicyReadError[] = "POLICY_READ_ERROR";
const char kHostOfflineReasonPolicyChangeRequiresRestart[] =
"POLICY_CHANGE_REQUIRES_RESTART";
} // namespace } // namespace
namespace remoting { namespace remoting {
...@@ -182,40 +188,46 @@ class HostProcess : public ConfigWatcher::Delegate, ...@@ -182,40 +188,46 @@ class HostProcess : public ConfigWatcher::Delegate,
IPC::PlatformFileForTransit unprivileged_key); IPC::PlatformFileForTransit unprivileged_key);
private: private:
// See SetState method for a list of allowed state transitions.
enum HostState { enum HostState {
// Host process has just been started. Waiting for config and policies to be // Waiting for valid config and policies to be read from the disk.
// read from the disk. // Either the host process has just been started, or it is trying to start
HOST_INITIALIZING, // again after temporarily going offline due to policy change or error.
HOST_STARTING,
// Host is started and running. // Host is started and running.
HOST_STARTED, HOST_STARTED,
// Host is being stopped and will need to be started again. // Host is sending offline reason, before trying to restart.
HOST_STOPPING_TO_RESTART, HOST_GOING_OFFLINE_TO_RESTART,
// Host is being stopped. // Host is sending offline reason, before shutting down.
HOST_STOPPING, HOST_GOING_OFFLINE_TO_STOP,
// Host has been stopped. // Host has been stopped (host process will end soon).
HOST_STOPPED, HOST_STOPPED,
};
// Allowed state transitions: enum PolicyState {
// INITIALIZING->STARTED // Cannot start the host, because a valid policy has not been read yet.
// INITIALIZING->STOPPED POLICY_INITIALIZING,
// STARTED->STOPPING_TO_RESTART
// STARTED->STOPPING // Policy was loaded successfully.
// STOPPING_TO_RESTART->STARTED POLICY_LOADED,
// STOPPING_TO_RESTART->STOPPING
// STOPPING->STOPPED // Policy error was detected, and we haven't yet sent out a
// STOPPED->STARTED // host-offline-reason (i.e. because we haven't yet read the config).
// POLICY_ERROR_REPORT_PENDING,
// |host_| must be nullptr in INITIALIZING and STOPPED states and not
// nullptr in all other states. // Policy error was detected, and we have sent out a host-offline-reason.
POLICY_ERROR_REPORTED,
}; };
friend class base::RefCountedThreadSafe<HostProcess>; friend class base::RefCountedThreadSafe<HostProcess>;
~HostProcess() override; ~HostProcess() override;
void SetState(HostState target_state);
void StartOnNetworkThread(); void StartOnNetworkThread();
#if defined(OS_POSIX) #if defined(OS_POSIX)
...@@ -245,6 +257,7 @@ class HostProcess : public ConfigWatcher::Delegate, ...@@ -245,6 +257,7 @@ class HostProcess : public ConfigWatcher::Delegate,
// Handles policy updates, by calling On*PolicyUpdate methods. // Handles policy updates, by calling On*PolicyUpdate methods.
void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies); void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies);
void OnPolicyError(); void OnPolicyError();
void ReportPolicyErrorAndRestartHost();
void ApplyHostDomainPolicy(); void ApplyHostDomainPolicy();
void ApplyUsernamePolicy(); void ApplyUsernamePolicy();
bool OnHostDomainPolicyUpdate(base::DictionaryValue* policies); bool OnHostDomainPolicyUpdate(base::DictionaryValue* policies);
...@@ -268,16 +281,12 @@ class HostProcess : public ConfigWatcher::Delegate, ...@@ -268,16 +281,12 @@ class HostProcess : public ConfigWatcher::Delegate,
void OnUnknownHostIdError() override; void OnUnknownHostIdError() override;
void OnAuthFailed() override; void OnAuthFailed() override;
void RestartHost(); void RestartHost(const std::string& host_offline_reason);
// Stops the host and shuts down the process with the specified |exit_code|.
void ShutdownHost(HostExitCodes exit_code); void ShutdownHost(HostExitCodes exit_code);
// Private helper used by ShutdownHost method to initiate sending of // Helper methods doing the work needed by RestartHost and ShutdownHost.
// host-offline-reason before continuing shutdown. void GoOffline(const std::string& host_offline_reason);
void SendOfflineReasonAndShutdownOnNetworkThread(HostExitCodes exit_code); void OnHostOfflineReasonAck(bool success);
void ShutdownOnNetworkThread();
#if defined(OS_WIN) #if defined(OS_WIN)
// Initializes the pairing registry on Windows. This should be invoked on the // Initializes the pairing registry on Windows. This should be invoked on the
...@@ -325,7 +334,7 @@ class HostProcess : public ConfigWatcher::Delegate, ...@@ -325,7 +334,7 @@ class HostProcess : public ConfigWatcher::Delegate,
int64_t frame_recorder_buffer_size_; int64_t frame_recorder_buffer_size_;
scoped_ptr<PolicyWatcher> policy_watcher_; scoped_ptr<PolicyWatcher> policy_watcher_;
bool policies_loaded_; PolicyState policy_state_;
std::string host_domain_; std::string host_domain_;
bool host_username_match_required_; bool host_username_match_required_;
bool allow_nat_traversal_; bool allow_nat_traversal_;
...@@ -377,11 +386,11 @@ HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context, ...@@ -377,11 +386,11 @@ HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context,
int* exit_code_out, int* exit_code_out,
ShutdownWatchdog* shutdown_watchdog) ShutdownWatchdog* shutdown_watchdog)
: context_(context.Pass()), : context_(context.Pass()),
state_(HOST_INITIALIZING), state_(HOST_STARTING),
use_service_account_(false), use_service_account_(false),
enable_vp9_(false), enable_vp9_(false),
frame_recorder_buffer_size_(0), frame_recorder_buffer_size_(0),
policies_loaded_(false), policy_state_(POLICY_INITIALIZING),
host_username_match_required_(false), host_username_match_required_(false),
allow_nat_traversal_(true), allow_nat_traversal_(true),
allow_relay_(true), allow_relay_(true),
...@@ -551,12 +560,11 @@ void HostProcess::OnConfigUpdated( ...@@ -551,12 +560,11 @@ void HostProcess::OnConfigUpdated(
return; return;
} }
if (state_ == HOST_INITIALIZING) { if (state_ == HOST_STARTING) {
StartHostIfReady(); StartHostIfReady();
} else if (state_ == HOST_STARTED) { } else if (state_ == HOST_STARTED) {
DCHECK(policies_loaded_);
// Reapply policies that could be affected by a new config. // Reapply policies that could be affected by a new config.
DCHECK_EQ(policy_state_, POLICY_LOADED);
ApplyHostDomainPolicy(); ApplyHostDomainPolicy();
ApplyUsernamePolicy(); ApplyUsernamePolicy();
...@@ -572,6 +580,51 @@ void HostProcess::OnConfigWatcherError() { ...@@ -572,6 +580,51 @@ void HostProcess::OnConfigWatcherError() {
ShutdownHost(kInvalidHostConfigurationExitCode); ShutdownHost(kInvalidHostConfigurationExitCode);
} }
// Allowed state transitions (enforced via DCHECKs in SetState method):
// STARTING->STARTED (once we have valid config + policy)
// STARTING->GOING_OFFLINE_TO_STOP
// STARTING->GOING_OFFLINE_TO_RESTART
// STARTED->GOING_OFFLINE_TO_STOP
// STARTED->GOING_OFFLINE_TO_RESTART
// GOING_OFFLINE_TO_RESTART->GOING_OFFLINE_TO_STOP
// GOING_OFFLINE_TO_RESTART->STARTING (after OnHostOfflineReasonAck)
// GOING_OFFLINE_TO_STOP->STOPPED (after OnHostOfflineReasonAck)
//
// |host_| must be not-null in STARTED state and nullptr in all other states
// (although this invariant can be temporarily violated when doing
// synchronous processing on the networking thread).
void HostProcess::SetState(HostState target_state) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
// DCHECKs below enforce state allowed transitions listed in HostState.
switch (state_) {
case HOST_STARTING:
DCHECK((target_state == HOST_STARTED) ||
(target_state == HOST_GOING_OFFLINE_TO_STOP) ||
(target_state == HOST_GOING_OFFLINE_TO_RESTART))
<< state_ << " -> " << target_state;
break;
case HOST_STARTED:
DCHECK((target_state == HOST_GOING_OFFLINE_TO_STOP) ||
(target_state == HOST_GOING_OFFLINE_TO_RESTART))
<< state_ << " -> " << target_state;
break;
case HOST_GOING_OFFLINE_TO_RESTART:
DCHECK((target_state == HOST_GOING_OFFLINE_TO_STOP) ||
(target_state == HOST_STARTING))
<< state_ << " -> " << target_state;
break;
case HOST_GOING_OFFLINE_TO_STOP:
DCHECK_EQ(target_state, HOST_STOPPED);
break;
case HOST_STOPPED: // HOST_STOPPED is a terminal state.
default:
NOTREACHED() << state_ << " -> " << target_state;
break;
}
state_ = target_state;
}
void HostProcess::StartOnNetworkThread() { void HostProcess::StartOnNetworkThread() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
...@@ -786,7 +839,6 @@ void HostProcess::ShutdownOnUiThread() { ...@@ -786,7 +839,6 @@ void HostProcess::ShutdownOnUiThread() {
// Tear down resources that need to be torn down on the UI thread. // Tear down resources that need to be torn down on the UI thread.
daemon_channel_.reset(); daemon_channel_.reset();
desktop_environment_factory_.reset(); desktop_environment_factory_.reset();
policy_watcher_.reset(); policy_watcher_.reset();
// It is now safe for the HostProcess to be deleted. // It is now safe for the HostProcess to be deleted.
...@@ -981,13 +1033,13 @@ void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) { ...@@ -981,13 +1033,13 @@ void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
restart_required |= OnPairingPolicyUpdate(policies.get()); restart_required |= OnPairingPolicyUpdate(policies.get());
restart_required |= OnGnubbyAuthPolicyUpdate(policies.get()); restart_required |= OnGnubbyAuthPolicyUpdate(policies.get());
policies_loaded_ = true; policy_state_ = POLICY_LOADED;
if (state_ == HOST_INITIALIZING) { if (state_ == HOST_STARTING) {
StartHostIfReady(); StartHostIfReady();
} else if (state_ == HOST_STARTED) { } else if (state_ == HOST_STARTED) {
if (restart_required) if (restart_required)
RestartHost(); RestartHost(kHostOfflineReasonPolicyChangeRequiresRestart);
} }
} }
...@@ -998,7 +1050,24 @@ void HostProcess::OnPolicyError() { ...@@ -998,7 +1050,24 @@ void HostProcess::OnPolicyError() {
return; return;
} }
ShutdownHost(kInvalidHostConfigurationExitCode); if (policy_state_ != POLICY_ERROR_REPORTED) {
policy_state_ = POLICY_ERROR_REPORT_PENDING;
if ((state_ == HOST_STARTED) ||
(state_ == HOST_STARTING && !serialized_config_.empty())) {
ReportPolicyErrorAndRestartHost();
}
}
}
void HostProcess::ReportPolicyErrorAndRestartHost() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
DCHECK(!serialized_config_.empty());
DCHECK_EQ(policy_state_, POLICY_ERROR_REPORT_PENDING);
policy_state_ = POLICY_ERROR_REPORTED;
LOG(INFO) << "Restarting the host due to policy errors.";
RestartHost(kHostOfflineReasonPolicyReadError);
} }
void HostProcess::ApplyHostDomainPolicy() { void HostProcess::ApplyHostDomainPolicy() {
...@@ -1300,20 +1369,24 @@ scoped_ptr<HostSignalingManager> HostProcess::CreateHostSignalingManager() { ...@@ -1300,20 +1369,24 @@ scoped_ptr<HostSignalingManager> HostProcess::CreateHostSignalingManager() {
oauth_refresh_token_, oauth_refresh_token_,
use_service_account_)); use_service_account_));
return HostSignalingManager::Create(this, context_->network_task_runner(), return HostSignalingManager::Create(
context_->url_request_context_getter(), this, context_->url_request_context_getter(), xmpp_server_config_,
xmpp_server_config_, talkgadget_prefix_, talkgadget_prefix_, host_id_, key_pair_, directory_bot_jid_,
host_id_, key_pair_, directory_bot_jid_, oauth_credentials.Pass());
oauth_credentials.Pass());
} }
void HostProcess::StartHostIfReady() { void HostProcess::StartHostIfReady() {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
DCHECK_EQ(state_, HOST_INITIALIZING); DCHECK_EQ(state_, HOST_STARTING);
// Start the host if both the config and the policies are loaded. // Start the host if both the config and the policies are loaded.
if (!serialized_config_.empty() && policies_loaded_) if (!serialized_config_.empty()) {
StartHost(); if (policy_state_ == POLICY_LOADED) {
StartHost();
} else if (policy_state_ == POLICY_ERROR_REPORT_PENDING) {
ReportPolicyErrorAndRestartHost();
}
}
} }
void HostProcess::StartHost() { void HostProcess::StartHost() {
...@@ -1321,10 +1394,7 @@ void HostProcess::StartHost() { ...@@ -1321,10 +1394,7 @@ void HostProcess::StartHost() {
DCHECK(!host_); DCHECK(!host_);
DCHECK(!host_signaling_manager_); DCHECK(!host_signaling_manager_);
DCHECK(state_ == HOST_INITIALIZING || state_ == HOST_STOPPING_TO_RESTART || SetState(HOST_STARTED);
state_ == HOST_STOPPED)
<< "state_ = " << state_;
state_ = HOST_STARTED;
host_signaling_manager_ = CreateHostSignalingManager(); host_signaling_manager_ = CreateHostSignalingManager();
...@@ -1409,22 +1479,12 @@ void HostProcess::OnAuthFailed() { ...@@ -1409,22 +1479,12 @@ void HostProcess::OnAuthFailed() {
ShutdownHost(kInvalidOauthCredentialsExitCode); ShutdownHost(kInvalidOauthCredentialsExitCode);
} }
void HostProcess::RestartHost() { void HostProcess::RestartHost(const std::string& host_offline_reason) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
DCHECK_EQ(state_, HOST_STARTED); DCHECK(!host_offline_reason.empty());
state_ = HOST_STOPPING_TO_RESTART; SetState(HOST_GOING_OFFLINE_TO_RESTART);
ShutdownOnNetworkThread(); GoOffline(host_offline_reason);
}
void HostProcess::SendOfflineReasonAndShutdownOnNetworkThread(
HostExitCodes exit_code) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
DCHECK(host_signaling_manager_);
host_signaling_manager_.release()->SendHostOfflineReasonAndDelete(
ExitCodeToString(exit_code),
base::TimeDelta::FromSeconds(kHostOfflineReasonTimeoutSeconds));
ShutdownOnNetworkThread();
} }
void HostProcess::ShutdownHost(HostExitCodes exit_code) { void HostProcess::ShutdownHost(HostExitCodes exit_code) {
...@@ -1433,42 +1493,67 @@ void HostProcess::ShutdownHost(HostExitCodes exit_code) { ...@@ -1433,42 +1493,67 @@ void HostProcess::ShutdownHost(HostExitCodes exit_code) {
*exit_code_out_ = exit_code; *exit_code_out_ = exit_code;
switch (state_) { switch (state_) {
case HOST_INITIALIZING: case HOST_STARTING:
state_ = HOST_STOPPING;
DCHECK(!host_signaling_manager_);
host_signaling_manager_ = CreateHostSignalingManager();
SendOfflineReasonAndShutdownOnNetworkThread(exit_code);
break;
case HOST_STARTED: case HOST_STARTED:
state_ = HOST_STOPPING; SetState(HOST_GOING_OFFLINE_TO_STOP);
SendOfflineReasonAndShutdownOnNetworkThread(exit_code); GoOffline(ExitCodeToString(exit_code));
break; break;
case HOST_STOPPING_TO_RESTART: case HOST_GOING_OFFLINE_TO_RESTART:
state_ = HOST_STOPPING; SetState(HOST_GOING_OFFLINE_TO_STOP);
break; break;
case HOST_STOPPING: case HOST_GOING_OFFLINE_TO_STOP:
case HOST_STOPPED: case HOST_STOPPED:
// Host is already stopped or being stopped. No action is required. // Host is already stopped or being stopped. No action is required.
break; break;
} }
} }
void HostProcess::ShutdownOnNetworkThread() { void HostProcess::GoOffline(const std::string& host_offline_reason) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
DCHECK(!host_offline_reason.empty());
DCHECK((state_ == HOST_GOING_OFFLINE_TO_STOP) ||
(state_ == HOST_GOING_OFFLINE_TO_RESTART));
// Shut down everything except the HostSignalingManager.
host_.reset(); host_.reset();
host_event_logger_.reset(); host_event_logger_.reset();
host_status_logger_.reset(); host_status_logger_.reset();
host_signaling_manager_.reset();
host_change_notification_listener_.reset(); host_change_notification_listener_.reset();
if (state_ == HOST_STOPPING_TO_RESTART) { // Before shutting down HostSignalingManager, send the |host_offline_reason|
StartHost(); // if possible (i.e. if we have the config).
} else if (state_ == HOST_STOPPING) { if (!serialized_config_.empty()) {
state_ = HOST_STOPPED; if (!host_signaling_manager_) {
host_signaling_manager_ = CreateHostSignalingManager();
}
host_signaling_manager_->SendHostOfflineReason(
host_offline_reason,
base::TimeDelta::FromSeconds(kHostOfflineReasonTimeoutSeconds),
base::Bind(&HostProcess::OnHostOfflineReasonAck, this));
return; // Shutdown will resume after OnHostOfflineReasonAck.
}
// Continue the shutdown without sending the host offline reason.
HOST_LOG << "Can't send offline reason (" << host_offline_reason << ") "
<< "without a valid host config.";
OnHostOfflineReasonAck(false);
}
void HostProcess::OnHostOfflineReasonAck(bool success) {
DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
DCHECK(!host_); // Assert that the host is really offline at this point.
HOST_LOG << "SendHostOfflineReason " << (success ? "succeeded." : "failed.");
host_signaling_manager_.reset();
if (state_ == HOST_GOING_OFFLINE_TO_RESTART) {
SetState(HOST_STARTING);
StartHostIfReady();
} else if (state_ == HOST_GOING_OFFLINE_TO_STOP) {
SetState(HOST_STOPPED);
shutdown_watchdog_->SetExitCode(*exit_code_out_); shutdown_watchdog_->SetExitCode(*exit_code_out_);
shutdown_watchdog_->Arm(); shutdown_watchdog_->Arm();
...@@ -1479,7 +1564,6 @@ void HostProcess::ShutdownOnNetworkThread() { ...@@ -1479,7 +1564,6 @@ void HostProcess::ShutdownOnNetworkThread() {
context_->ui_task_runner()->PostTask( context_->ui_task_runner()->PostTask(
FROM_HERE, base::Bind(&HostProcess::ShutdownOnUiThread, this)); FROM_HERE, base::Bind(&HostProcess::ShutdownOnUiThread, this));
} else { } else {
// This method is only called in STOPPING_TO_RESTART and STOPPING states.
NOTREACHED(); NOTREACHED();
} }
} }
......
...@@ -886,6 +886,18 @@ For information about privacy, please see the Google Privacy Policy (http://goo. ...@@ -886,6 +886,18 @@ For information about privacy, please see the Google Privacy Policy (http://goo.
name="IDS_OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED"> name="IDS_OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED">
Host running at the console logic screen has shutdown to support curtain mode by switching to a host running in a user-specific session. Host running at the console logic screen has shutdown to support curtain mode by switching to a host running in a user-specific session.
</message> </message>
<message desc="Error message indicating that the host is offline, because the host policy was unreadable (unparseable, inaccessible, mistyped or malformed)."
name="IDS_OFFLINE_REASON_POLICY_READ_ERROR">
Host failed to read the policy.
</message>
<message desc="Error message indicating that the host is temporarily offline, because the host is restarting due to a policy change."
name="IDS_OFFLINE_REASON_POLICY_CHANGE_REQUIRES_RESTART">
Host is restarting, to take into account a policy change.
</message>
<message desc="Error message indicating that the host is offline, because another process has requested the host to shut down (via a SIGTERM signal)."
name="IDS_OFFLINE_REASON_SUCCESS_EXIT">
Host has shut down.
</message>
<message desc="Error message indicating that the host is offline, because RemoteAccessHostMatchUsername policy is enabled and there is a mismatch between the name of the local user (that the host is associated with) and the name of the Google account registered as the host owner (i.e. 'johndoe' if the host is owned by 'johndoe@example.com' Google account)" <message desc="Error message indicating that the host is offline, because RemoteAccessHostMatchUsername policy is enabled and there is a mismatch between the name of the local user (that the host is associated with) and the name of the Google account registered as the host owner (i.e. 'johndoe' if the host is owned by 'johndoe@example.com' Google account)"
name="IDS_OFFLINE_REASON_USERNAME_MISMATCH"> name="IDS_OFFLINE_REASON_USERNAME_MISMATCH">
Invalid host owner. Invalid host owner.
......
...@@ -344,10 +344,13 @@ function formatHostOfflineReason(hostOfflineReason) { ...@@ -344,10 +344,13 @@ function formatHostOfflineReason(hostOfflineReason) {
var knownReasonTags = [ var knownReasonTags = [
/*i18n-content*/ 'OFFLINE_REASON_INITIALIZATION_FAILED', /*i18n-content*/ 'OFFLINE_REASON_INITIALIZATION_FAILED',
/*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_CONFIGURATION', /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_CONFIGURATION',
/*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_DOMAIN',
/*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_ID', /*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_ID',
/*i18n-content*/ 'OFFLINE_REASON_INVALID_OAUTH_CREDENTIALS', /*i18n-content*/ 'OFFLINE_REASON_INVALID_OAUTH_CREDENTIALS',
/*i18n-content*/ 'OFFLINE_REASON_INVALID_HOST_DOMAIN',
/*i18n-content*/ 'OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED', /*i18n-content*/ 'OFFLINE_REASON_LOGIN_SCREEN_NOT_SUPPORTED',
/*i18n-content*/ 'OFFLINE_REASON_POLICY_CHANGE_REQUIRES_RESTART',
/*i18n-content*/ 'OFFLINE_REASON_POLICY_READ_ERROR',
/*i18n-content*/ 'OFFLINE_REASON_SUCCESS_EXIT',
/*i18n-content*/ 'OFFLINE_REASON_USERNAME_MISMATCH' /*i18n-content*/ 'OFFLINE_REASON_USERNAME_MISMATCH'
]; ];
var offlineReasonTag = 'OFFLINE_REASON_' + hostOfflineReason; var offlineReasonTag = 'OFFLINE_REASON_' + hostOfflineReason;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment