Chromium Code Reviews| Index: remoting/host/remoting_me2me_host.cc |
| diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc |
| index b66f8376f791bea52a9ff8cc57ce6339324331d2..4e5bffe35dd4c1b83a52a5743e8939eb2f2098a3 100644 |
| --- a/remoting/host/remoting_me2me_host.cc |
| +++ b/remoting/host/remoting_me2me_host.cc |
| @@ -147,6 +147,12 @@ const int kShutdownTimeoutSeconds = 15; |
| // before continuing normal process shutdown. |
| 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 remoting { |
| @@ -183,34 +189,83 @@ class HostProcess : public ConfigWatcher::Delegate, |
| private: |
| enum HostState { |
| - // Host process has just been started. Waiting for config and policies to be |
| - // read from the disk. |
| + // Waiting for valid config and policies to be read from the disk. |
| + // Either the host process has just been started, or it is trying to start |
| + // again after temporarily going offline due to policy change or error. |
| HOST_INITIALIZING, |
|
Wez
2015/02/14 00:49:15
Suggest renaming this to HOST_STARTING since it ca
Łukasz Anforowicz
2015/02/17 21:02:27
Done.
|
| // Host is started and running. |
| HOST_STARTED, |
| - // Host is being stopped and will need to be started again. |
| - HOST_STOPPING_TO_RESTART, |
| + // Host is sending offline reason, before trying to (re)initialize again. |
| + // Used when |
| + // 1) policy change (or a config change in the future) requires a restart. |
|
Wez
2015/02/14 00:49:15
How can we detect a config change in the future...
Łukasz Anforowicz
2015/02/17 21:02:27
I meant that a config change can theoretically als
|
| + // 2) policy error puts the host offline until we have a valid policy again. |
| + HOST_GOING_OFFLINE_TO_RESTART, |
|
Wez
2015/02/14 00:49:15
Why not simply HOST_RESTARTING or HOST_STOPPING_TO
Łukasz Anforowicz
2015/02/17 21:02:27
I like your suggestion for its brevity, but I thin
|
| - // Host is being stopped. |
| - HOST_STOPPING, |
| + // Host is sending offline reason, before shutting down. |
| + HOST_GOING_OFFLINE_TO_STOP, |
|
Wez
2015/02/14 00:49:15
Why not simply HOST_STOPPING as it was before?
Łukasz Anforowicz
2015/02/17 21:02:27
Please see my other comment.
|
| - // Host has been stopped. |
| + // Host has been stopped (host process will end soon). |
| HOST_STOPPED, |
|
Łukasz Anforowicz
2015/02/13 20:41:00
Summary of the changes I made to "allowed state tr
|
| - // Allowed state transitions: |
| - // INITIALIZING->STARTED |
| - // INITIALIZING->STOPPED |
| - // STARTED->STOPPING_TO_RESTART |
| - // STARTED->STOPPING |
| - // STOPPING_TO_RESTART->STARTED |
| - // STOPPING_TO_RESTART->STOPPING |
| - // STOPPING->STOPPED |
| - // STOPPED->STARTED |
| + // Allowed state transitions (enforced via DCHECKs in SetState method): |
| + // INITIALIZING->STARTED (once we have valid config + policy) |
| + // INITIALIZING->GOING_OFFLINE_TO_STOP |
| + // INITIALIZING->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->INITIALIZING (after OfflineReasonAck) |
| + // GOING_OFFLINE_TO_STOP->STOPPED (after OfflineReasonAck) |
| // |
| - // |host_| must be nullptr in INITIALIZING and STOPPED states and not |
| - // nullptr in all other states. |
| + // |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 SetState(HostState target_state) { |
| + DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
| + |
| + // DCHECKs below enforce state allowed transitions listed in HostState. |
| + switch (state_) { |
| + case HOST_INITIALIZING: |
| + DCHECK((target_state == HOST_STARTED) || |
| + (target_state == HOST_GOING_OFFLINE_TO_STOP) || |
| + (target_state == HOST_GOING_OFFLINE_TO_RESTART)); |
| + break; |
| + case HOST_STARTED: |
| + DCHECK((target_state == HOST_GOING_OFFLINE_TO_STOP) || |
| + (target_state == HOST_GOING_OFFLINE_TO_RESTART)); |
| + break; |
| + case HOST_GOING_OFFLINE_TO_RESTART: |
| + DCHECK((target_state == HOST_GOING_OFFLINE_TO_STOP) || |
| + (target_state == HOST_INITIALIZING)); |
| + break; |
| + case HOST_GOING_OFFLINE_TO_STOP: |
| + DCHECK_EQ(target_state, HOST_STOPPED); |
| + break; |
| + case HOST_STOPPED: |
| + default: |
| + NOTREACHED(); // HOST_STOPPED is a terminal state. |
| + break; |
| + } |
| + state_ = target_state; |
| + } |
| + |
| + enum PolicyState { |
| + // Cannot start the host, because a valid policy has not been read yet. |
| + POLICY_INITIALIZING, |
| + |
| + // Policy was loaded successfully. |
| + POLICY_LOADED, |
| + |
| + // Policy error was detected, and we haven't yet sent out a |
| + // host-offline-reason (i.e. because we haven't yet read the config). |
| + POLICY_ERROR_REPORT_PENDING, |
| + |
| + // Policy error was detected, and we have sent out a host-offline-reason. |
| + POLICY_ERROR_REPORTED, |
| }; |
| friend class base::RefCountedThreadSafe<HostProcess>; |
| @@ -245,6 +300,7 @@ class HostProcess : public ConfigWatcher::Delegate, |
| // Handles policy updates, by calling On*PolicyUpdate methods. |
| void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies); |
| void OnPolicyError(); |
| + void ReportPolicyErrorAndRestartHost(); |
| void ApplyHostDomainPolicy(); |
| void ApplyUsernamePolicy(); |
| bool OnHostDomainPolicyUpdate(base::DictionaryValue* policies); |
| @@ -268,16 +324,12 @@ class HostProcess : public ConfigWatcher::Delegate, |
| void OnUnknownHostIdError() override; |
| void OnAuthFailed() override; |
| - void RestartHost(); |
| - |
| - // Stops the host and shuts down the process with the specified |exit_code|. |
| + void RestartHost(const std::string& host_offline_reason); |
| void ShutdownHost(HostExitCodes exit_code); |
| - // Private helper used by ShutdownHost method to initiate sending of |
| - // host-offline-reason before continuing shutdown. |
| - void SendOfflineReasonAndShutdownOnNetworkThread(HostExitCodes exit_code); |
| - |
| - void ShutdownOnNetworkThread(); |
| + // Helper methods doing the work needed by RestartHost and ShutdownHost. |
| + void GoOffline(const std::string& host_offline_reason); |
| + void OnHostOfflineReasonAck(bool success); |
| #if defined(OS_WIN) |
| // Initializes the pairing registry on Windows. This should be invoked on the |
| @@ -325,7 +377,7 @@ class HostProcess : public ConfigWatcher::Delegate, |
| int64_t frame_recorder_buffer_size_; |
| scoped_ptr<PolicyWatcher> policy_watcher_; |
| - bool policies_loaded_; |
| + PolicyState policy_state_; |
| std::string host_domain_; |
| bool host_username_match_required_; |
| bool allow_nat_traversal_; |
| @@ -381,7 +433,7 @@ HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context, |
| use_service_account_(false), |
| enable_vp9_(false), |
| frame_recorder_buffer_size_(0), |
| - policies_loaded_(false), |
| + policy_state_(POLICY_INITIALIZING), |
| host_username_match_required_(false), |
| allow_nat_traversal_(true), |
| allow_relay_(true), |
| @@ -554,9 +606,8 @@ void HostProcess::OnConfigUpdated( |
| if (state_ == HOST_INITIALIZING) { |
| StartHostIfReady(); |
| } else if (state_ == HOST_STARTED) { |
| - DCHECK(policies_loaded_); |
| - |
| // Reapply policies that could be affected by a new config. |
| + DCHECK_EQ(policy_state_, POLICY_LOADED); |
| ApplyHostDomainPolicy(); |
| ApplyUsernamePolicy(); |
| @@ -786,7 +837,6 @@ void HostProcess::ShutdownOnUiThread() { |
| // Tear down resources that need to be torn down on the UI thread. |
| daemon_channel_.reset(); |
| desktop_environment_factory_.reset(); |
| - |
| policy_watcher_.reset(); |
| // It is now safe for the HostProcess to be deleted. |
| @@ -981,13 +1031,13 @@ void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) { |
| restart_required |= OnPairingPolicyUpdate(policies.get()); |
| restart_required |= OnGnubbyAuthPolicyUpdate(policies.get()); |
| - policies_loaded_ = true; |
| + policy_state_ = POLICY_LOADED; |
| if (state_ == HOST_INITIALIZING) { |
| StartHostIfReady(); |
| } else if (state_ == HOST_STARTED) { |
| if (restart_required) |
| - RestartHost(); |
| + RestartHost(kHostOfflineReasonPolicyChangeRequiresRestart); |
| } |
| } |
| @@ -998,7 +1048,23 @@ void HostProcess::OnPolicyError() { |
| return; |
| } |
| - ShutdownHost(kInvalidHostConfigurationExitCode); |
| + if (policy_state_ != POLICY_ERROR_REPORTED) { |
| + policy_state_ = POLICY_ERROR_REPORT_PENDING; |
| + if (((state_ == HOST_INITIALIZING) || (state_ == HOST_STARTED)) && |
| + (!serialized_config_.empty())) { |
| + ReportPolicyErrorAndRestartHost(); |
| + } |
| + } |
| +} |
| + |
| +void HostProcess::ReportPolicyErrorAndRestartHost() { |
| + DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
| + |
| + 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() { |
| @@ -1300,11 +1366,10 @@ scoped_ptr<HostSignalingManager> HostProcess::CreateHostSignalingManager() { |
| oauth_refresh_token_, |
| use_service_account_)); |
| - return HostSignalingManager::Create(this, context_->network_task_runner(), |
| - context_->url_request_context_getter(), |
| - xmpp_server_config_, talkgadget_prefix_, |
| - host_id_, key_pair_, directory_bot_jid_, |
| - oauth_credentials.Pass()); |
| + return HostSignalingManager::Create( |
| + this, context_->url_request_context_getter(), xmpp_server_config_, |
| + talkgadget_prefix_, host_id_, key_pair_, directory_bot_jid_, |
| + oauth_credentials.Pass()); |
| } |
| void HostProcess::StartHostIfReady() { |
| @@ -1312,8 +1377,13 @@ void HostProcess::StartHostIfReady() { |
| DCHECK_EQ(state_, HOST_INITIALIZING); |
| // Start the host if both the config and the policies are loaded. |
| - if (!serialized_config_.empty() && policies_loaded_) |
| - StartHost(); |
| + if (!serialized_config_.empty()) { |
| + if (policy_state_ == POLICY_LOADED) { |
| + StartHost(); |
| + } else if (policy_state_ == POLICY_ERROR_REPORT_PENDING) { |
| + ReportPolicyErrorAndRestartHost(); |
| + } |
| + } |
| } |
| void HostProcess::StartHost() { |
| @@ -1321,10 +1391,7 @@ void HostProcess::StartHost() { |
| DCHECK(!host_); |
| DCHECK(!host_signaling_manager_); |
| - DCHECK(state_ == HOST_INITIALIZING || state_ == HOST_STOPPING_TO_RESTART || |
| - state_ == HOST_STOPPED) |
| - << "state_ = " << state_; |
| - state_ = HOST_STARTED; |
| + SetState(HOST_STARTED); |
| host_signaling_manager_ = CreateHostSignalingManager(); |
| @@ -1409,22 +1476,12 @@ void HostProcess::OnAuthFailed() { |
| ShutdownHost(kInvalidOauthCredentialsExitCode); |
| } |
| -void HostProcess::RestartHost() { |
| +void HostProcess::RestartHost(const std::string& host_offline_reason) { |
| DCHECK(context_->network_task_runner()->BelongsToCurrentThread()); |
| - DCHECK_EQ(state_, HOST_STARTED); |
| - |
| - state_ = HOST_STOPPING_TO_RESTART; |
| - ShutdownOnNetworkThread(); |
| -} |
| + DCHECK(!host_offline_reason.empty()); |
| -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(); |
| + SetState(HOST_GOING_OFFLINE_TO_RESTART); |
| + GoOffline(host_offline_reason); |
| } |
| void HostProcess::ShutdownHost(HostExitCodes exit_code) { |
| @@ -1434,41 +1491,66 @@ void HostProcess::ShutdownHost(HostExitCodes exit_code) { |
| switch (state_) { |
| case HOST_INITIALIZING: |
| - state_ = HOST_STOPPING; |
| - DCHECK(!host_signaling_manager_); |
| - host_signaling_manager_ = CreateHostSignalingManager(); |
| - SendOfflineReasonAndShutdownOnNetworkThread(exit_code); |
| - break; |
| - |
| case HOST_STARTED: |
| - state_ = HOST_STOPPING; |
| - SendOfflineReasonAndShutdownOnNetworkThread(exit_code); |
| + SetState(HOST_GOING_OFFLINE_TO_STOP); |
| + GoOffline(ExitCodeToString(exit_code)); |
| break; |
| - case HOST_STOPPING_TO_RESTART: |
| - state_ = HOST_STOPPING; |
| + case HOST_GOING_OFFLINE_TO_RESTART: |
| + SetState(HOST_GOING_OFFLINE_TO_STOP); |
| break; |
| - case HOST_STOPPING: |
| + case HOST_GOING_OFFLINE_TO_STOP: |
| case HOST_STOPPED: |
| // Host is already stopped or being stopped. No action is required. |
| break; |
| } |
| } |
| -void HostProcess::ShutdownOnNetworkThread() { |
| +void HostProcess::GoOffline(const std::string& host_offline_reason) { |
| 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_event_logger_.reset(); |
| host_status_logger_.reset(); |
| - host_signaling_manager_.reset(); |
| host_change_notification_listener_.reset(); |
| - if (state_ == HOST_STOPPING_TO_RESTART) { |
| - StartHost(); |
| - } else if (state_ == HOST_STOPPING) { |
| - state_ = HOST_STOPPED; |
| + // Before shutting down HostSignalingManager, send the |host_offline_reason| |
| + // if possible (i.e. if we have the config). |
| + if (!serialized_config_.empty()) { |
| + 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 << "SendHostOfflineReason( " << host_offline_reason << ") " |
| + << "not possible without a config..."; |
| + OnHostOfflineReasonAck(false); |
| +} |
| + |
| +void HostProcess::OnHostOfflineReasonAck(bool success) { |
|
Wez
2015/02/14 00:49:15
nit: It's weird for this to be OnHostOfflineReason
Łukasz Anforowicz
2015/02/17 21:02:27
This (presence of OnXxx method, without DoXxx meth
|
| + 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_INITIALIZING); |
| + StartHostIfReady(); |
| + } else if (state_ == HOST_GOING_OFFLINE_TO_STOP) { |
| + SetState(HOST_STOPPED); |
| shutdown_watchdog_->SetExitCode(*exit_code_out_); |
| shutdown_watchdog_->Arm(); |
| @@ -1479,7 +1561,6 @@ void HostProcess::ShutdownOnNetworkThread() { |
| context_->ui_task_runner()->PostTask( |
| FROM_HERE, base::Bind(&HostProcess::ShutdownOnUiThread, this)); |
| } else { |
| - // This method is only called in STOPPING_TO_RESTART and STOPPING states. |
| NOTREACHED(); |
| } |
| } |