MemorySanitizer: blacklist two false positives.
[chromium-blink-merge.git] / remoting / host / remoting_me2me_host.cc
blob78daa67c52dc09d15bdc3a3cefb53be4b5c5ea44
1 // Copyright (c) 2012 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 // This file implements a standalone host process for Me2Me.
7 #include <string>
9 #include "base/at_exit.h"
10 #include "base/bind.h"
11 #include "base/callback.h"
12 #include "base/command_line.h"
13 #include "base/debug/alias.h"
14 #include "base/file_util.h"
15 #include "base/files/file_path.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop/message_loop.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/synchronization/waitable_event.h"
23 #include "base/threading/thread.h"
24 #include "build/build_config.h"
25 #include "crypto/nss_util.h"
26 #include "ipc/ipc_channel.h"
27 #include "ipc/ipc_channel_proxy.h"
28 #include "ipc/ipc_listener.h"
29 #include "media/base/media.h"
30 #include "net/base/network_change_notifier.h"
31 #include "net/socket/client_socket_factory.h"
32 #include "net/socket/ssl_server_socket.h"
33 #include "net/url_request/url_fetcher.h"
34 #include "remoting/base/auto_thread_task_runner.h"
35 #include "remoting/base/breakpad.h"
36 #include "remoting/base/constants.h"
37 #include "remoting/base/logging.h"
38 #include "remoting/base/rsa_key_pair.h"
39 #include "remoting/base/service_urls.h"
40 #include "remoting/base/util.h"
41 #include "remoting/host/branding.h"
42 #include "remoting/host/chromoting_host.h"
43 #include "remoting/host/chromoting_host_context.h"
44 #include "remoting/host/chromoting_messages.h"
45 #include "remoting/host/config_file_watcher.h"
46 #include "remoting/host/config_watcher.h"
47 #include "remoting/host/desktop_environment.h"
48 #include "remoting/host/desktop_session_connector.h"
49 #include "remoting/host/dns_blackhole_checker.h"
50 #include "remoting/host/heartbeat_sender.h"
51 #include "remoting/host/host_change_notification_listener.h"
52 #include "remoting/host/host_config.h"
53 #include "remoting/host/host_event_logger.h"
54 #include "remoting/host/host_exit_codes.h"
55 #include "remoting/host/host_main.h"
56 #include "remoting/host/host_status_logger.h"
57 #include "remoting/host/host_status_sender.h"
58 #include "remoting/host/ipc_constants.h"
59 #include "remoting/host/ipc_desktop_environment.h"
60 #include "remoting/host/ipc_host_event_logger.h"
61 #include "remoting/host/json_host_config.h"
62 #include "remoting/host/logging.h"
63 #include "remoting/host/me2me_desktop_environment.h"
64 #include "remoting/host/pairing_registry_delegate.h"
65 #include "remoting/host/policy_hack/policy_watcher.h"
66 #include "remoting/host/session_manager_factory.h"
67 #include "remoting/host/signaling_connector.h"
68 #include "remoting/host/token_validator_factory_impl.h"
69 #include "remoting/host/usage_stats_consent.h"
70 #include "remoting/host/username.h"
71 #include "remoting/protocol/me2me_host_authenticator_factory.h"
72 #include "remoting/protocol/network_settings.h"
73 #include "remoting/protocol/pairing_registry.h"
74 #include "remoting/protocol/token_validator.h"
75 #include "remoting/signaling/xmpp_signal_strategy.h"
77 #if defined(OS_POSIX)
78 #include <signal.h>
79 #include <sys/types.h>
80 #include <unistd.h>
81 #include "base/file_descriptor_posix.h"
82 #include "remoting/host/pam_authorization_factory_posix.h"
83 #include "remoting/host/posix/signal_handler.h"
84 #endif // defined(OS_POSIX)
86 #if defined(OS_MACOSX)
87 #include "base/mac/scoped_cftyperef.h"
88 #endif // defined(OS_MACOSX)
90 #if defined(OS_LINUX)
91 #include <gtk/gtk.h>
92 #include "remoting/host/audio_capturer_linux.h"
93 #endif // defined(OS_LINUX)
95 #if defined(OS_WIN)
96 #include <commctrl.h>
97 #include "base/win/registry.h"
98 #include "base/win/scoped_handle.h"
99 #include "remoting/host/pairing_registry_delegate_win.h"
100 #include "remoting/host/win/session_desktop_environment.h"
101 #endif // defined(OS_WIN)
103 using remoting::protocol::PairingRegistry;
104 using remoting::protocol::NetworkSettings;
106 namespace {
108 // This is used for tagging system event logs.
109 const char kApplicationName[] = "chromoting";
111 #if defined(OS_LINUX)
112 // The command line switch used to pass name of the pipe to capture audio on
113 // linux.
114 const char kAudioPipeSwitchName[] = "audio-pipe-name";
116 // The command line switch used to pass name of the unix domain socket used to
117 // listen for gnubby requests.
118 const char kAuthSocknameSwitchName[] = "ssh-auth-sockname";
119 #endif // defined(OS_LINUX)
121 // The command line switch used by the parent to request the host to signal it
122 // when it is successfully started.
123 const char kSignalParentSwitchName[] = "signal-parent";
125 // Command line switch used to enable VP9 encoding.
126 const char kEnableVp9SwitchName[] = "enable-vp9";
128 // Value used for --host-config option to indicate that the path must be read
129 // from stdin.
130 const char kStdinConfigPath[] = "-";
132 } // namespace
134 namespace remoting {
136 class HostProcess
137 : public ConfigWatcher::Delegate,
138 public HeartbeatSender::Listener,
139 public HostChangeNotificationListener::Listener,
140 public IPC::Listener,
141 public base::RefCountedThreadSafe<HostProcess> {
142 public:
143 HostProcess(scoped_ptr<ChromotingHostContext> context,
144 int* exit_code_out);
146 // ConfigWatcher::Delegate interface.
147 virtual void OnConfigUpdated(const std::string& serialized_config) OVERRIDE;
148 virtual void OnConfigWatcherError() OVERRIDE;
150 // IPC::Listener implementation.
151 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
152 virtual void OnChannelError() OVERRIDE;
154 // HeartbeatSender::Listener overrides.
155 virtual void OnHeartbeatSuccessful() OVERRIDE;
156 virtual void OnUnknownHostIdError() OVERRIDE;
158 // HostChangeNotificationListener::Listener overrides.
159 virtual void OnHostDeleted() OVERRIDE;
161 // Initializes the pairing registry on Windows.
162 void OnInitializePairingRegistry(
163 IPC::PlatformFileForTransit privileged_key,
164 IPC::PlatformFileForTransit unprivileged_key);
166 private:
167 enum HostState {
168 // Host process has just been started. Waiting for config and policies to be
169 // read from the disk.
170 HOST_INITIALIZING,
172 // Host is started and running.
173 HOST_STARTED,
175 // Host is being stopped and will need to be started again.
176 HOST_STOPPING_TO_RESTART,
178 // Host is being stopped.
179 HOST_STOPPING,
181 // Host has been stopped.
182 HOST_STOPPED,
184 // Allowed state transitions:
185 // INITIALIZING->STARTED
186 // INITIALIZING->STOPPED
187 // STARTED->STOPPING_TO_RESTART
188 // STARTED->STOPPING
189 // STOPPING_TO_RESTART->STARTED
190 // STOPPING_TO_RESTART->STOPPING
191 // STOPPING->STOPPED
192 // STOPPED->STARTED
194 // |host_| must be NULL in INITIALIZING and STOPPED states and not-NULL in
195 // all other states.
198 friend class base::RefCountedThreadSafe<HostProcess>;
199 virtual ~HostProcess();
201 void StartOnNetworkThread();
203 #if defined(OS_POSIX)
204 // Callback passed to RegisterSignalHandler() to handle SIGTERM events.
205 void SigTermHandler(int signal_number);
206 #endif
208 // Called to initialize resources on the UI thread.
209 void StartOnUiThread();
211 // Initializes IPC control channel and config file path from |cmd_line|.
212 // Called on the UI thread.
213 bool InitWithCommandLine(const base::CommandLine* cmd_line);
215 // Called on the UI thread to start monitoring the configuration file.
216 void StartWatchingConfigChanges();
218 // Called on the network thread to set the host's Authenticator factory.
219 void CreateAuthenticatorFactory();
221 // Tear down resources that run on the UI thread.
222 void ShutdownOnUiThread();
224 // Applies the host config, returning true if successful.
225 bool ApplyConfig(scoped_ptr<JsonHostConfig> config);
227 // Handles policy updates, by calling On*PolicyUpdate methods.
228 void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies);
229 void ApplyHostDomainPolicy();
230 void ApplyUsernamePolicy();
231 bool OnHostDomainPolicyUpdate(base::DictionaryValue* policies);
232 bool OnUsernamePolicyUpdate(base::DictionaryValue* policies);
233 bool OnNatPolicyUpdate(base::DictionaryValue* policies);
234 bool OnRelayPolicyUpdate(base::DictionaryValue* policies);
235 bool OnUdpPortPolicyUpdate(base::DictionaryValue* policies);
236 bool OnCurtainPolicyUpdate(base::DictionaryValue* policies);
237 bool OnHostTalkGadgetPrefixPolicyUpdate(base::DictionaryValue* policies);
238 bool OnHostTokenUrlPolicyUpdate(base::DictionaryValue* policies);
239 bool OnPairingPolicyUpdate(base::DictionaryValue* policies);
240 bool OnGnubbyAuthPolicyUpdate(base::DictionaryValue* policies);
242 void StartHost();
244 void OnAuthFailed();
246 void RestartHost();
248 // Stops the host and shuts down the process with the specified |exit_code|.
249 void ShutdownHost(HostExitCodes exit_code);
251 void ScheduleHostShutdown();
253 void ShutdownOnNetworkThread();
255 // Crashes the process in response to a daemon's request. The daemon passes
256 // the location of the code that detected the fatal error resulted in this
257 // request.
258 void OnCrash(const std::string& function_name,
259 const std::string& file_name,
260 const int& line_number);
262 scoped_ptr<ChromotingHostContext> context_;
264 // Created on the UI thread but used from the network thread.
265 scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
267 // Accessed on the UI thread.
268 scoped_ptr<IPC::ChannelProxy> daemon_channel_;
270 // XMPP server/remoting bot configuration (initialized from the command line).
271 XmppSignalStrategy::XmppServerConfig xmpp_server_config_;
272 std::string directory_bot_jid_;
274 // Created on the UI thread but used from the network thread.
275 base::FilePath host_config_path_;
276 std::string host_config_;
277 scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_;
279 // Accessed on the network thread.
280 HostState state_;
282 scoped_ptr<ConfigWatcher> config_watcher_;
284 std::string host_id_;
285 protocol::SharedSecretHash host_secret_hash_;
286 scoped_refptr<RsaKeyPair> key_pair_;
287 std::string oauth_refresh_token_;
288 std::string serialized_config_;
289 std::string host_owner_;
290 bool use_service_account_;
291 bool enable_vp9_;
293 scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_;
294 std::string host_domain_;
295 bool host_username_match_required_;
296 bool allow_nat_traversal_;
297 bool allow_relay_;
298 int min_udp_port_;
299 int max_udp_port_;
300 std::string talkgadget_prefix_;
301 bool allow_pairing_;
303 bool curtain_required_;
304 ThirdPartyAuthConfig third_party_auth_config_;
305 bool enable_gnubby_auth_;
307 scoped_ptr<OAuthTokenGetter> oauth_token_getter_;
308 scoped_ptr<XmppSignalStrategy> signal_strategy_;
309 scoped_ptr<SignalingConnector> signaling_connector_;
310 scoped_ptr<HeartbeatSender> heartbeat_sender_;
311 scoped_ptr<HostStatusSender> host_status_sender_;
312 scoped_ptr<HostChangeNotificationListener> host_change_notification_listener_;
313 scoped_ptr<HostStatusLogger> host_status_logger_;
314 scoped_ptr<HostEventLogger> host_event_logger_;
316 scoped_ptr<ChromotingHost> host_;
318 // Used to keep this HostProcess alive until it is shutdown.
319 scoped_refptr<HostProcess> self_;
321 #if defined(REMOTING_MULTI_PROCESS)
322 DesktopSessionConnector* desktop_session_connector_;
323 #endif // defined(REMOTING_MULTI_PROCESS)
325 int* exit_code_out_;
326 bool signal_parent_;
328 scoped_ptr<PairingRegistry::Delegate> pairing_registry_delegate_;
331 HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context,
332 int* exit_code_out)
333 : context_(context.Pass()),
334 state_(HOST_INITIALIZING),
335 use_service_account_(false),
336 enable_vp9_(false),
337 host_username_match_required_(false),
338 allow_nat_traversal_(true),
339 allow_relay_(true),
340 min_udp_port_(0),
341 max_udp_port_(0),
342 allow_pairing_(true),
343 curtain_required_(false),
344 enable_gnubby_auth_(false),
345 #if defined(REMOTING_MULTI_PROCESS)
346 desktop_session_connector_(NULL),
347 #endif // defined(REMOTING_MULTI_PROCESS)
348 self_(this),
349 exit_code_out_(exit_code_out),
350 signal_parent_(false) {
351 StartOnUiThread();
354 HostProcess::~HostProcess() {
355 // Verify that UI components have been torn down.
356 DCHECK(!config_watcher_);
357 DCHECK(!daemon_channel_);
358 DCHECK(!desktop_environment_factory_);
360 // We might be getting deleted on one of the threads the |host_context| owns,
361 // so we need to post it back to the caller thread to safely join & delete the
362 // threads it contains. This will go away when we move to AutoThread.
363 // |context_release()| will null |context_| before the method is invoked, so
364 // we need to pull out the task-runner on which to call DeleteSoon first.
365 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
366 context_->ui_task_runner();
367 task_runner->DeleteSoon(FROM_HERE, context_.release());
370 bool HostProcess::InitWithCommandLine(const base::CommandLine* cmd_line) {
371 #if defined(REMOTING_MULTI_PROCESS)
372 // Parse the handle value and convert it to a handle/file descriptor.
373 std::string channel_name =
374 cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
376 int pipe_handle = 0;
377 if (channel_name.empty() ||
378 !base::StringToInt(channel_name, &pipe_handle)) {
379 LOG(ERROR) << "Invalid '" << kDaemonPipeSwitchName
380 << "' value: " << channel_name;
381 return false;
384 #if defined(OS_WIN)
385 base::win::ScopedHandle pipe(reinterpret_cast<HANDLE>(pipe_handle));
386 IPC::ChannelHandle channel_handle(pipe);
387 #elif defined(OS_POSIX)
388 base::FileDescriptor pipe(pipe_handle, true);
389 IPC::ChannelHandle channel_handle(channel_name, pipe);
390 #endif // defined(OS_POSIX)
392 // Connect to the daemon process.
393 daemon_channel_ = IPC::ChannelProxy::Create(channel_handle,
394 IPC::Channel::MODE_CLIENT,
395 this,
396 context_->network_task_runner());
397 #else // !defined(REMOTING_MULTI_PROCESS)
398 // Connect to the daemon process.
399 std::string channel_name =
400 cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
401 if (!channel_name.empty()) {
402 daemon_channel_ =
403 IPC::ChannelProxy::Create(channel_name,
404 IPC::Channel::MODE_CLIENT,
405 this,
406 context_->network_task_runner().get());
409 if (cmd_line->HasSwitch(kHostConfigSwitchName)) {
410 host_config_path_ = cmd_line->GetSwitchValuePath(kHostConfigSwitchName);
412 // Read config from stdin if necessary.
413 if (host_config_path_ == base::FilePath(kStdinConfigPath)) {
414 char buf[4096];
415 size_t len;
416 while ((len = fread(buf, 1, sizeof(buf), stdin)) > 0) {
417 host_config_.append(buf, len);
420 } else {
421 base::FilePath default_config_dir = remoting::GetConfigDir();
422 host_config_path_ = default_config_dir.Append(kDefaultHostConfigFile);
425 if (host_config_path_ != base::FilePath(kStdinConfigPath) &&
426 !base::PathExists(host_config_path_)) {
427 LOG(ERROR) << "Can't find host config at " << host_config_path_.value();
428 return false;
430 #endif // !defined(REMOTING_MULTI_PROCESS)
432 // Ignore certificate requests - the host currently has no client certificate
433 // support, so ignoring certificate requests allows connecting to servers that
434 // request, but don't require, a certificate (optional client authentication).
435 net::URLFetcher::SetIgnoreCertificateRequests(true);
437 ServiceUrls* service_urls = ServiceUrls::GetInstance();
438 bool xmpp_server_valid = net::ParseHostAndPort(
439 service_urls->xmpp_server_address(),
440 &xmpp_server_config_.host, &xmpp_server_config_.port);
441 if (!xmpp_server_valid) {
442 LOG(ERROR) << "Invalid XMPP server: " <<
443 service_urls->xmpp_server_address();
444 return false;
446 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
447 directory_bot_jid_ = service_urls->directory_bot_jid();
449 signal_parent_ = cmd_line->HasSwitch(kSignalParentSwitchName);
451 return true;
454 void HostProcess::OnConfigUpdated(
455 const std::string& serialized_config) {
456 if (!context_->network_task_runner()->BelongsToCurrentThread()) {
457 context_->network_task_runner()->PostTask(FROM_HERE,
458 base::Bind(&HostProcess::OnConfigUpdated, this, serialized_config));
459 return;
462 // Filter out duplicates.
463 if (serialized_config_ == serialized_config)
464 return;
466 HOST_LOG << "Processing new host configuration.";
468 serialized_config_ = serialized_config;
469 scoped_ptr<JsonHostConfig> config(new JsonHostConfig(base::FilePath()));
470 if (!config->SetSerializedData(serialized_config)) {
471 LOG(ERROR) << "Invalid configuration.";
472 ShutdownHost(kInvalidHostConfigurationExitCode);
473 return;
476 if (!ApplyConfig(config.Pass())) {
477 LOG(ERROR) << "Failed to apply the configuration.";
478 ShutdownHost(kInvalidHostConfigurationExitCode);
479 return;
482 if (state_ == HOST_INITIALIZING) {
483 // TODO(sergeyu): Currently OnPolicyUpdate() assumes that host config is
484 // already loaded so PolicyWatcher has to be started here. Separate policy
485 // loading from policy verifications and move |policy_watcher_|
486 // initialization to StartOnNetworkThread().
487 policy_watcher_.reset(
488 policy_hack::PolicyWatcher::Create(context_->file_task_runner()));
489 policy_watcher_->StartWatching(
490 base::Bind(&HostProcess::OnPolicyUpdate, base::Unretained(this)));
491 } else {
492 // Reapply policies that could be affected by a new config.
493 ApplyHostDomainPolicy();
494 ApplyUsernamePolicy();
496 if (state_ == HOST_STARTED) {
497 // TODO(sergeyu): Here we assume that PIN is the only part of the config
498 // that may change while the service is running. Change ApplyConfig() to
499 // detect other changes in the config and restart host if necessary here.
500 CreateAuthenticatorFactory();
505 void HostProcess::OnConfigWatcherError() {
506 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
507 ShutdownHost(kInvalidHostConfigurationExitCode);
510 void HostProcess::StartOnNetworkThread() {
511 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
513 #if !defined(REMOTING_MULTI_PROCESS)
514 if (host_config_path_ == base::FilePath(kStdinConfigPath)) {
515 // Process config we've read from stdin.
516 OnConfigUpdated(host_config_);
517 } else {
518 // Start watching the host configuration file.
519 config_watcher_.reset(new ConfigFileWatcher(context_->network_task_runner(),
520 context_->file_task_runner(),
521 host_config_path_));
522 config_watcher_->Watch(this);
524 #endif // !defined(REMOTING_MULTI_PROCESS)
526 #if defined(OS_POSIX)
527 remoting::RegisterSignalHandler(
528 SIGTERM,
529 base::Bind(&HostProcess::SigTermHandler, base::Unretained(this)));
530 #endif // defined(OS_POSIX)
533 #if defined(OS_POSIX)
534 void HostProcess::SigTermHandler(int signal_number) {
535 DCHECK(signal_number == SIGTERM);
536 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
537 HOST_LOG << "Caught SIGTERM: Shutting down...";
538 ShutdownHost(kSuccessExitCode);
540 #endif // OS_POSIX
542 void HostProcess::CreateAuthenticatorFactory() {
543 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
545 if (state_ != HOST_STARTED)
546 return;
548 std::string local_certificate = key_pair_->GenerateCertificate();
549 if (local_certificate.empty()) {
550 LOG(ERROR) << "Failed to generate host certificate.";
551 ShutdownHost(kInitializationFailed);
552 return;
555 scoped_refptr<PairingRegistry> pairing_registry = NULL;
556 if (allow_pairing_) {
557 if (!pairing_registry_delegate_)
558 pairing_registry_delegate_ = CreatePairingRegistryDelegate();
560 if (pairing_registry_delegate_) {
561 pairing_registry = new PairingRegistry(context_->file_task_runner(),
562 pairing_registry_delegate_.Pass());
566 scoped_ptr<protocol::AuthenticatorFactory> factory;
568 if (third_party_auth_config_.is_empty()) {
569 factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
570 use_service_account_, host_owner_, local_certificate, key_pair_,
571 host_secret_hash_, pairing_registry);
573 } else if (third_party_auth_config_.is_valid()) {
574 scoped_ptr<protocol::TokenValidatorFactory> token_validator_factory(
575 new TokenValidatorFactoryImpl(
576 third_party_auth_config_,
577 key_pair_, context_->url_request_context_getter()));
578 factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
579 use_service_account_, host_owner_, local_certificate, key_pair_,
580 token_validator_factory.Pass());
582 } else {
583 // TODO(rmsousa): If the policy is bad the host should not go online. It
584 // should keep running, but not connected, until the policies are fixed.
585 // Having it show up as online and then reject all clients is misleading.
586 LOG(ERROR) << "One of the third-party token URLs is empty or invalid. "
587 << "Host will reject all clients until policies are corrected. "
588 << "TokenUrl: " << third_party_auth_config_.token_url << ", "
589 << "TokenValidationUrl: "
590 << third_party_auth_config_.token_validation_url;
591 factory = protocol::Me2MeHostAuthenticatorFactory::CreateRejecting();
594 #if defined(OS_POSIX)
595 // On Linux and Mac, perform a PAM authorization step after authentication.
596 factory.reset(new PamAuthorizationFactory(factory.Pass()));
597 #endif
598 host_->SetAuthenticatorFactory(factory.Pass());
600 host_->set_pairing_registry(pairing_registry);
603 // IPC::Listener implementation.
604 bool HostProcess::OnMessageReceived(const IPC::Message& message) {
605 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
607 #if defined(REMOTING_MULTI_PROCESS)
608 bool handled = true;
609 IPC_BEGIN_MESSAGE_MAP(HostProcess, message)
610 IPC_MESSAGE_HANDLER(ChromotingDaemonMsg_Crash, OnCrash)
611 IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Configuration,
612 OnConfigUpdated)
613 IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_InitializePairingRegistry,
614 OnInitializePairingRegistry)
615 IPC_MESSAGE_FORWARD(
616 ChromotingDaemonNetworkMsg_DesktopAttached,
617 desktop_session_connector_,
618 DesktopSessionConnector::OnDesktopSessionAgentAttached)
619 IPC_MESSAGE_FORWARD(ChromotingDaemonNetworkMsg_TerminalDisconnected,
620 desktop_session_connector_,
621 DesktopSessionConnector::OnTerminalDisconnected)
622 IPC_MESSAGE_UNHANDLED(handled = false)
623 IPC_END_MESSAGE_MAP()
625 CHECK(handled) << "Received unexpected IPC type: " << message.type();
626 return handled;
628 #else // !defined(REMOTING_MULTI_PROCESS)
629 return false;
630 #endif // !defined(REMOTING_MULTI_PROCESS)
633 void HostProcess::OnChannelError() {
634 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
636 // Shutdown the host if the daemon process disconnects the IPC channel.
637 context_->network_task_runner()->PostTask(
638 FROM_HERE,
639 base::Bind(&HostProcess::ShutdownHost, this, kSuccessExitCode));
642 void HostProcess::StartOnUiThread() {
643 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
645 if (!InitWithCommandLine(base::CommandLine::ForCurrentProcess())) {
646 // Shutdown the host if the command line is invalid.
647 context_->network_task_runner()->PostTask(
648 FROM_HERE, base::Bind(&HostProcess::ShutdownHost, this,
649 kUsageExitCode));
650 return;
653 #if defined(OS_LINUX)
654 // If an audio pipe is specific on the command-line then initialize
655 // AudioCapturerLinux to capture from it.
656 base::FilePath audio_pipe_name = base::CommandLine::ForCurrentProcess()->
657 GetSwitchValuePath(kAudioPipeSwitchName);
658 if (!audio_pipe_name.empty()) {
659 remoting::AudioCapturerLinux::InitializePipeReader(
660 context_->audio_task_runner(), audio_pipe_name);
663 base::FilePath gnubby_socket_name = base::CommandLine::ForCurrentProcess()->
664 GetSwitchValuePath(kAuthSocknameSwitchName);
665 if (!gnubby_socket_name.empty())
666 remoting::GnubbyAuthHandler::SetGnubbySocketName(gnubby_socket_name);
667 #endif // defined(OS_LINUX)
669 // Create a desktop environment factory appropriate to the build type &
670 // platform.
671 #if defined(OS_WIN)
672 IpcDesktopEnvironmentFactory* desktop_environment_factory =
673 new IpcDesktopEnvironmentFactory(
674 context_->audio_task_runner(),
675 context_->network_task_runner(),
676 context_->video_capture_task_runner(),
677 context_->network_task_runner(),
678 daemon_channel_.get());
679 desktop_session_connector_ = desktop_environment_factory;
680 #else // !defined(OS_WIN)
681 DesktopEnvironmentFactory* desktop_environment_factory =
682 new Me2MeDesktopEnvironmentFactory(
683 context_->network_task_runner(),
684 context_->input_task_runner(),
685 context_->ui_task_runner());
686 #endif // !defined(OS_WIN)
688 desktop_environment_factory_.reset(desktop_environment_factory);
689 desktop_environment_factory_->SetEnableGnubbyAuth(enable_gnubby_auth_);
691 context_->network_task_runner()->PostTask(
692 FROM_HERE,
693 base::Bind(&HostProcess::StartOnNetworkThread, this));
696 void HostProcess::ShutdownOnUiThread() {
697 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
699 // Tear down resources that need to be torn down on the UI thread.
700 network_change_notifier_.reset();
701 daemon_channel_.reset();
702 desktop_environment_factory_.reset();
704 // It is now safe for the HostProcess to be deleted.
705 self_ = NULL;
707 #if defined(OS_LINUX)
708 // Cause the global AudioPipeReader to be freed, otherwise the audio
709 // thread will remain in-use and prevent the process from exiting.
710 // TODO(wez): DesktopEnvironmentFactory should own the pipe reader.
711 // See crbug.com/161373 and crbug.com/104544.
712 AudioCapturerLinux::InitializePipeReader(NULL, base::FilePath());
713 #endif
716 // Overridden from HeartbeatSender::Listener
717 void HostProcess::OnUnknownHostIdError() {
718 LOG(ERROR) << "Host ID not found.";
719 ShutdownHost(kInvalidHostIdExitCode);
722 void HostProcess::OnHeartbeatSuccessful() {
723 HOST_LOG << "Host ready to receive connections.";
724 #if defined(OS_POSIX)
725 if (signal_parent_) {
726 kill(getppid(), SIGUSR1);
727 signal_parent_ = false;
729 #endif
732 void HostProcess::OnHostDeleted() {
733 LOG(ERROR) << "Host was deleted from the directory.";
734 ShutdownHost(kInvalidHostIdExitCode);
737 void HostProcess::OnInitializePairingRegistry(
738 IPC::PlatformFileForTransit privileged_key,
739 IPC::PlatformFileForTransit unprivileged_key) {
740 DCHECK(!pairing_registry_delegate_);
742 #if defined(OS_WIN)
743 // Initialize the pairing registry delegate.
744 scoped_ptr<PairingRegistryDelegateWin> delegate(
745 new PairingRegistryDelegateWin());
746 bool result = delegate->SetRootKeys(
747 reinterpret_cast<HKEY>(
748 IPC::PlatformFileForTransitToPlatformFile(privileged_key)),
749 reinterpret_cast<HKEY>(
750 IPC::PlatformFileForTransitToPlatformFile(unprivileged_key)));
751 if (!result)
752 return;
754 pairing_registry_delegate_ = delegate.PassAs<PairingRegistry::Delegate>();
755 #else // !defined(OS_WIN)
756 NOTREACHED();
757 #endif // !defined(OS_WIN)
760 // Applies the host config, returning true if successful.
761 bool HostProcess::ApplyConfig(scoped_ptr<JsonHostConfig> config) {
762 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
764 if (!config->GetString(kHostIdConfigPath, &host_id_)) {
765 LOG(ERROR) << "host_id is not defined in the config.";
766 return false;
769 std::string key_base64;
770 if (!config->GetString(kPrivateKeyConfigPath, &key_base64)) {
771 LOG(ERROR) << "Private key couldn't be read from the config file.";
772 return false;
775 key_pair_ = RsaKeyPair::FromString(key_base64);
776 if (!key_pair_.get()) {
777 LOG(ERROR) << "Invalid private key in the config file.";
778 return false;
781 std::string host_secret_hash_string;
782 if (!config->GetString(kHostSecretHashConfigPath,
783 &host_secret_hash_string)) {
784 host_secret_hash_string = "plain:";
787 if (!host_secret_hash_.Parse(host_secret_hash_string)) {
788 LOG(ERROR) << "Invalid host_secret_hash.";
789 return false;
792 // Use an XMPP connection to the Talk network for session signalling.
793 if (!config->GetString(kXmppLoginConfigPath, &xmpp_server_config_.username) ||
794 !(config->GetString(kXmppAuthTokenConfigPath,
795 &xmpp_server_config_.auth_token) ||
796 config->GetString(kOAuthRefreshTokenConfigPath,
797 &oauth_refresh_token_))) {
798 LOG(ERROR) << "XMPP credentials are not defined in the config.";
799 return false;
802 if (!oauth_refresh_token_.empty()) {
803 // SignalingConnector is responsible for getting OAuth token.
804 xmpp_server_config_.auth_token = "";
805 xmpp_server_config_.auth_service = "oauth2";
806 } else if (!config->GetString(kXmppAuthServiceConfigPath,
807 &xmpp_server_config_.auth_service)) {
808 // For the me2me host, we default to ClientLogin token for chromiumsync
809 // because earlier versions of the host had no HTTP stack with which to
810 // request an OAuth2 access token.
811 xmpp_server_config_.auth_service = kChromotingTokenDefaultServiceName;
814 if (config->GetString(kHostOwnerConfigPath, &host_owner_)) {
815 // Service account configs have a host_owner, different from the xmpp_login.
816 use_service_account_ = true;
817 } else {
818 // User credential configs only have an xmpp_login, which is also the owner.
819 host_owner_ = xmpp_server_config_.username;
820 use_service_account_ = false;
823 // Allow offering of VP9 encoding to be overridden by the command-line.
824 if (CommandLine::ForCurrentProcess()->HasSwitch(kEnableVp9SwitchName)) {
825 enable_vp9_ = true;
826 } else {
827 config->GetBoolean(kEnableVp9ConfigPath, &enable_vp9_);
830 return true;
833 void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
834 if (!context_->network_task_runner()->BelongsToCurrentThread()) {
835 context_->network_task_runner()->PostTask(FROM_HERE, base::Bind(
836 &HostProcess::OnPolicyUpdate, this, base::Passed(&policies)));
837 return;
840 bool restart_required = false;
841 restart_required |= OnHostDomainPolicyUpdate(policies.get());
842 restart_required |= OnCurtainPolicyUpdate(policies.get());
843 // Note: UsernamePolicyUpdate must run after OnCurtainPolicyUpdate.
844 restart_required |= OnUsernamePolicyUpdate(policies.get());
845 restart_required |= OnNatPolicyUpdate(policies.get());
846 restart_required |= OnRelayPolicyUpdate(policies.get());
847 restart_required |= OnUdpPortPolicyUpdate(policies.get());
848 restart_required |= OnHostTalkGadgetPrefixPolicyUpdate(policies.get());
849 restart_required |= OnHostTokenUrlPolicyUpdate(policies.get());
850 restart_required |= OnPairingPolicyUpdate(policies.get());
851 restart_required |= OnGnubbyAuthPolicyUpdate(policies.get());
853 if (state_ == HOST_INITIALIZING) {
854 StartHost();
855 } else if (state_ == HOST_STARTED && restart_required) {
856 RestartHost();
860 void HostProcess::ApplyHostDomainPolicy() {
861 HOST_LOG << "Policy sets host domain: " << host_domain_;
862 if (!host_domain_.empty() &&
863 !EndsWith(host_owner_, std::string("@") + host_domain_, false)) {
864 LOG(ERROR) << "The host domain does not match the policy.";
865 ShutdownHost(kInvalidHostDomainExitCode);
869 bool HostProcess::OnHostDomainPolicyUpdate(base::DictionaryValue* policies) {
870 // Returns true if the host has to be restarted after this policy update.
871 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
873 if (!policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
874 &host_domain_)) {
875 return false;
878 ApplyHostDomainPolicy();
879 return false;
882 void HostProcess::ApplyUsernamePolicy() {
883 if (host_username_match_required_) {
884 HOST_LOG << "Policy requires host username match.";
885 std::string username = GetUsername();
886 bool shutdown = username.empty() ||
887 !StartsWithASCII(host_owner_, username + std::string("@"),
888 false);
890 #if defined(OS_MACOSX)
891 // On Mac, we run as root at the login screen, so the username won't match.
892 // However, there's no need to enforce the policy at the login screen, as
893 // the client will have to reconnect if a login occurs.
894 if (shutdown && getuid() == 0) {
895 shutdown = false;
897 #endif
899 // Curtain-mode on Windows presents the standard OS login prompt to the user
900 // for each connection, removing the need for an explicit user-name matching
901 // check.
902 #if defined(OS_WIN) && defined(REMOTING_RDP_SESSION)
903 if (curtain_required_)
904 return;
905 #endif // defined(OS_WIN) && defined(REMOTING_RDP_SESSION)
907 // Shutdown the host if the username does not match.
908 if (shutdown) {
909 LOG(ERROR) << "The host username does not match.";
910 ShutdownHost(kUsernameMismatchExitCode);
912 } else {
913 HOST_LOG << "Policy does not require host username match.";
917 bool HostProcess::OnUsernamePolicyUpdate(base::DictionaryValue* policies) {
918 // Returns false: never restart the host after this policy update.
919 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
921 if (!policies->GetBoolean(
922 policy_hack::PolicyWatcher::kHostMatchUsernamePolicyName,
923 &host_username_match_required_)) {
924 return false;
927 ApplyUsernamePolicy();
928 return false;
931 bool HostProcess::OnNatPolicyUpdate(base::DictionaryValue* policies) {
932 // Returns true if the host has to be restarted after this policy update.
933 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
935 if (!policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
936 &allow_nat_traversal_)) {
937 return false;
940 if (allow_nat_traversal_) {
941 HOST_LOG << "Policy enables NAT traversal.";
942 } else {
943 HOST_LOG << "Policy disables NAT traversal.";
945 return true;
948 bool HostProcess::OnRelayPolicyUpdate(base::DictionaryValue* policies) {
949 // Returns true if the host has to be restarted after this policy update.
950 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
952 if (!policies->GetBoolean(policy_hack::PolicyWatcher::kRelayPolicyName,
953 &allow_relay_)) {
954 return false;
957 if (allow_relay_) {
958 HOST_LOG << "Policy enables use of relay server.";
959 } else {
960 HOST_LOG << "Policy disables use of relay server.";
962 return true;
965 bool HostProcess::OnUdpPortPolicyUpdate(base::DictionaryValue* policies) {
966 // Returns true if the host has to be restarted after this policy update.
967 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
969 std::string udp_port_range;
970 if (!policies->GetString(policy_hack::PolicyWatcher::kUdpPortRangePolicyName,
971 &udp_port_range)) {
972 return false;
975 // Use default values if policy setting is empty or invalid.
976 int min_udp_port = 0;
977 int max_udp_port = 0;
978 if (!udp_port_range.empty() &&
979 !NetworkSettings::ParsePortRange(udp_port_range, &min_udp_port,
980 &max_udp_port)) {
981 LOG(WARNING) << "Invalid port range policy: \"" << udp_port_range
982 << "\". Using default values.";
985 if (min_udp_port_ != min_udp_port || max_udp_port_ != max_udp_port) {
986 if (min_udp_port != 0 && max_udp_port != 0) {
987 HOST_LOG << "Policy restricts UDP port range to [" << min_udp_port
988 << ", " << max_udp_port << "]";
989 } else {
990 HOST_LOG << "Policy does not restrict UDP port range.";
992 min_udp_port_ = min_udp_port;
993 max_udp_port_ = max_udp_port;
994 return true;
996 return false;
999 bool HostProcess::OnCurtainPolicyUpdate(base::DictionaryValue* policies) {
1000 // Returns true if the host has to be restarted after this policy update.
1001 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1003 if (!policies->GetBoolean(
1004 policy_hack::PolicyWatcher::kHostRequireCurtainPolicyName,
1005 &curtain_required_)) {
1006 return false;
1009 #if defined(OS_MACOSX)
1010 if (curtain_required_) {
1011 // When curtain mode is in effect on Mac, the host process runs in the
1012 // user's switched-out session, but launchd will also run an instance at
1013 // the console login screen. Even if no user is currently logged-on, we
1014 // can't support remote-access to the login screen because the current host
1015 // process model disconnects the client during login, which would leave
1016 // the logged in session un-curtained on the console until they reconnect.
1018 // TODO(jamiewalch): Fix this once we have implemented the multi-process
1019 // daemon architecture (crbug.com/134894)
1020 if (getuid() == 0) {
1021 LOG(ERROR) << "Running the host in the console login session is yet not "
1022 "supported.";
1023 ShutdownHost(kLoginScreenNotSupportedExitCode);
1024 return false;
1027 #endif
1029 if (curtain_required_) {
1030 HOST_LOG << "Policy requires curtain-mode.";
1031 } else {
1032 HOST_LOG << "Policy does not require curtain-mode.";
1035 if (host_)
1036 host_->SetEnableCurtaining(curtain_required_);
1037 return false;
1040 bool HostProcess::OnHostTalkGadgetPrefixPolicyUpdate(
1041 base::DictionaryValue* policies) {
1042 // Returns true if the host has to be restarted after this policy update.
1043 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1045 if (!policies->GetString(
1046 policy_hack::PolicyWatcher::kHostTalkGadgetPrefixPolicyName,
1047 &talkgadget_prefix_)) {
1048 return false;
1051 HOST_LOG << "Policy sets talkgadget prefix: " << talkgadget_prefix_;
1052 return true;
1055 bool HostProcess::OnHostTokenUrlPolicyUpdate(base::DictionaryValue* policies) {
1056 // Returns true if the host has to be restarted after this policy update.
1057 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1059 bool token_policy_changed = false;
1060 std::string token_url_string;
1061 if (policies->GetString(
1062 policy_hack::PolicyWatcher::kHostTokenUrlPolicyName,
1063 &token_url_string)) {
1064 token_policy_changed = true;
1065 third_party_auth_config_.token_url = GURL(token_url_string);
1067 std::string token_validation_url_string;
1068 if (policies->GetString(
1069 policy_hack::PolicyWatcher::kHostTokenValidationUrlPolicyName,
1070 &token_validation_url_string)) {
1071 token_policy_changed = true;
1072 third_party_auth_config_.token_validation_url =
1073 GURL(token_validation_url_string);
1075 if (policies->GetString(
1076 policy_hack::PolicyWatcher::kHostTokenValidationCertIssuerPolicyName,
1077 &third_party_auth_config_.token_validation_cert_issuer)) {
1078 token_policy_changed = true;
1081 if (token_policy_changed) {
1082 HOST_LOG << "Policy sets third-party token URLs: "
1083 << "TokenUrl: "
1084 << third_party_auth_config_.token_url << ", "
1085 << "TokenValidationUrl: "
1086 << third_party_auth_config_.token_validation_url << ", "
1087 << "TokenValidationCertificateIssuer: "
1088 << third_party_auth_config_.token_validation_cert_issuer;
1090 return token_policy_changed;
1093 bool HostProcess::OnPairingPolicyUpdate(base::DictionaryValue* policies) {
1094 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1096 if (!policies->GetBoolean(
1097 policy_hack::PolicyWatcher::kHostAllowClientPairing,
1098 &allow_pairing_)) {
1099 return false;
1102 if (allow_pairing_) {
1103 HOST_LOG << "Policy enables client pairing.";
1104 } else {
1105 HOST_LOG << "Policy disables client pairing.";
1107 return true;
1110 bool HostProcess::OnGnubbyAuthPolicyUpdate(base::DictionaryValue* policies) {
1111 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1113 if (!policies->GetBoolean(
1114 policy_hack::PolicyWatcher::kHostAllowGnubbyAuthPolicyName,
1115 &enable_gnubby_auth_)) {
1116 return false;
1119 if (enable_gnubby_auth_) {
1120 HOST_LOG << "Policy enables gnubby auth.";
1121 } else {
1122 HOST_LOG << "Policy disables gnubby auth.";
1125 if (desktop_environment_factory_)
1126 desktop_environment_factory_->SetEnableGnubbyAuth(enable_gnubby_auth_);
1128 return true;
1131 void HostProcess::StartHost() {
1132 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1133 DCHECK(!host_);
1134 DCHECK(!signal_strategy_.get());
1135 DCHECK(state_ == HOST_INITIALIZING || state_ == HOST_STOPPING_TO_RESTART ||
1136 state_ == HOST_STOPPED) << state_;
1137 state_ = HOST_STARTED;
1139 signal_strategy_.reset(
1140 new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
1141 context_->url_request_context_getter(),
1142 xmpp_server_config_));
1144 scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker(
1145 new DnsBlackholeChecker(context_->url_request_context_getter(),
1146 talkgadget_prefix_));
1148 // Create a NetworkChangeNotifier for use by the signaling connector.
1149 network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
1151 signaling_connector_.reset(new SignalingConnector(
1152 signal_strategy_.get(),
1153 dns_blackhole_checker.Pass(),
1154 base::Bind(&HostProcess::OnAuthFailed, this)));
1156 if (!oauth_refresh_token_.empty()) {
1157 scoped_ptr<OAuthTokenGetter::OAuthCredentials> oauth_credentials;
1158 oauth_credentials.reset(
1159 new OAuthTokenGetter::OAuthCredentials(
1160 xmpp_server_config_.username, oauth_refresh_token_,
1161 use_service_account_));
1163 oauth_token_getter_.reset(new OAuthTokenGetter(
1164 oauth_credentials.Pass(), context_->url_request_context_getter(),
1165 false));
1167 signaling_connector_->EnableOAuth(oauth_token_getter_.get());
1170 uint32 network_flags = 0;
1171 if (allow_nat_traversal_) {
1172 network_flags = NetworkSettings::NAT_TRAVERSAL_STUN |
1173 NetworkSettings::NAT_TRAVERSAL_OUTGOING;
1174 if (allow_relay_)
1175 network_flags |= NetworkSettings::NAT_TRAVERSAL_RELAY;
1178 NetworkSettings network_settings(network_flags);
1180 if (min_udp_port_ && max_udp_port_) {
1181 network_settings.min_port = min_udp_port_;
1182 network_settings.max_port = max_udp_port_;
1183 } else if (!allow_nat_traversal_) {
1184 // For legacy reasons we have to restrict the port range to a set of default
1185 // values when nat traversal is disabled, even if the port range was not
1186 // set in policy.
1187 network_settings.min_port = NetworkSettings::kDefaultMinPort;
1188 network_settings.max_port = NetworkSettings::kDefaultMaxPort;
1191 host_.reset(new ChromotingHost(
1192 signal_strategy_.get(),
1193 desktop_environment_factory_.get(),
1194 CreateHostSessionManager(signal_strategy_.get(), network_settings,
1195 context_->url_request_context_getter()),
1196 context_->audio_task_runner(),
1197 context_->input_task_runner(),
1198 context_->video_capture_task_runner(),
1199 context_->video_encode_task_runner(),
1200 context_->network_task_runner(),
1201 context_->ui_task_runner()));
1203 if (enable_vp9_) {
1204 scoped_ptr<protocol::CandidateSessionConfig> config =
1205 host_->protocol_config()->Clone();
1206 config->EnableVideoCodec(protocol::ChannelConfig::CODEC_VP9);
1207 host_->set_protocol_config(config.Pass());
1210 // TODO(simonmorris): Get the maximum session duration from a policy.
1211 #if defined(OS_LINUX)
1212 host_->SetMaximumSessionDuration(base::TimeDelta::FromHours(20));
1213 #endif
1215 heartbeat_sender_.reset(new HeartbeatSender(
1216 this, host_id_, signal_strategy_.get(), key_pair_,
1217 directory_bot_jid_));
1219 host_status_sender_.reset(new HostStatusSender(
1220 host_id_, signal_strategy_.get(), key_pair_, directory_bot_jid_));
1222 host_change_notification_listener_.reset(new HostChangeNotificationListener(
1223 this, host_id_, signal_strategy_.get(), directory_bot_jid_));
1225 host_status_logger_.reset(
1226 new HostStatusLogger(host_->AsWeakPtr(), ServerLogEntry::ME2ME,
1227 signal_strategy_.get(), directory_bot_jid_));
1229 // Set up repoting the host status notifications.
1230 #if defined(REMOTING_MULTI_PROCESS)
1231 host_event_logger_.reset(
1232 new IpcHostEventLogger(host_->AsWeakPtr(), daemon_channel_.get()));
1233 #else // !defined(REMOTING_MULTI_PROCESS)
1234 host_event_logger_ =
1235 HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
1236 #endif // !defined(REMOTING_MULTI_PROCESS)
1238 host_->SetEnableCurtaining(curtain_required_);
1239 host_->Start(host_owner_);
1241 CreateAuthenticatorFactory();
1244 void HostProcess::OnAuthFailed() {
1245 ShutdownHost(kInvalidOauthCredentialsExitCode);
1248 void HostProcess::RestartHost() {
1249 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1250 DCHECK_EQ(state_, HOST_STARTED);
1252 state_ = HOST_STOPPING_TO_RESTART;
1253 ShutdownOnNetworkThread();
1256 void HostProcess::ShutdownHost(HostExitCodes exit_code) {
1257 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1259 *exit_code_out_ = exit_code;
1261 switch (state_) {
1262 case HOST_INITIALIZING:
1263 state_ = HOST_STOPPING;
1264 ShutdownOnNetworkThread();
1265 break;
1267 case HOST_STARTED:
1268 state_ = HOST_STOPPING;
1269 host_status_sender_->SendOfflineStatus(exit_code);
1270 ScheduleHostShutdown();
1271 break;
1273 case HOST_STOPPING_TO_RESTART:
1274 state_ = HOST_STOPPING;
1275 break;
1277 case HOST_STOPPING:
1278 case HOST_STOPPED:
1279 // Host is already stopped or being stopped. No action is required.
1280 break;
1284 // TODO(weitaosu): shut down the host once we get an ACK for the offline status
1285 // XMPP message.
1286 void HostProcess::ScheduleHostShutdown() {
1287 // Delay the shutdown by 2 second to allow SendOfflineStatus to complete.
1288 context_->network_task_runner()->PostDelayedTask(
1289 FROM_HERE,
1290 base::Bind(&HostProcess::ShutdownOnNetworkThread, base::Unretained(this)),
1291 base::TimeDelta::FromSeconds(2));
1294 void HostProcess::ShutdownOnNetworkThread() {
1295 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1297 host_.reset();
1298 host_event_logger_.reset();
1299 host_status_logger_.reset();
1300 heartbeat_sender_.reset();
1301 host_status_sender_.reset();
1302 host_change_notification_listener_.reset();
1303 signaling_connector_.reset();
1304 oauth_token_getter_.reset();
1305 signal_strategy_.reset();
1306 network_change_notifier_.reset();
1308 if (state_ == HOST_STOPPING_TO_RESTART) {
1309 StartHost();
1310 } else if (state_ == HOST_STOPPING) {
1311 state_ = HOST_STOPPED;
1313 if (policy_watcher_.get()) {
1314 base::WaitableEvent done_event(true, false);
1315 policy_watcher_->StopWatching(&done_event);
1316 done_event.Wait();
1317 policy_watcher_.reset();
1320 config_watcher_.reset();
1322 // Complete the rest of shutdown on the main thread.
1323 context_->ui_task_runner()->PostTask(
1324 FROM_HERE,
1325 base::Bind(&HostProcess::ShutdownOnUiThread, this));
1326 } else {
1327 // This method is only called in STOPPING_TO_RESTART and STOPPING states.
1328 NOTREACHED();
1332 void HostProcess::OnCrash(const std::string& function_name,
1333 const std::string& file_name,
1334 const int& line_number) {
1335 char message[1024];
1336 base::snprintf(message, sizeof(message),
1337 "Requested by %s at %s, line %d.",
1338 function_name.c_str(), file_name.c_str(), line_number);
1339 base::debug::Alias(message);
1341 // The daemon requested us to crash the process.
1342 CHECK(false) << message;
1345 int HostProcessMain() {
1346 #if defined(OS_LINUX)
1347 // Required for any calls into GTK functions, such as the Disconnect and
1348 // Continue windows, though these should not be used for the Me2Me case
1349 // (crbug.com/104377).
1350 gtk_init(NULL, NULL);
1351 #endif
1353 // Enable support for SSL server sockets, which must be done while still
1354 // single-threaded.
1355 net::EnableSSLServerSockets();
1357 // Ensures runtime specific CPU features are initialized.
1358 media::InitializeCPUSpecificMediaFeatures();
1360 // Create the main message loop and start helper threads.
1361 base::MessageLoopForUI message_loop;
1362 scoped_ptr<ChromotingHostContext> context =
1363 ChromotingHostContext::Create(new AutoThreadTaskRunner(
1364 message_loop.message_loop_proxy(), base::MessageLoop::QuitClosure()));
1365 if (!context)
1366 return kInitializationFailed;
1368 // Create & start the HostProcess using these threads.
1369 // TODO(wez): The HostProcess holds a reference to itself until Shutdown().
1370 // Remove this hack as part of the multi-process refactoring.
1371 int exit_code = kSuccessExitCode;
1372 new HostProcess(context.Pass(), &exit_code);
1374 // Run the main (also UI) message loop until the host no longer needs it.
1375 message_loop.Run();
1377 return exit_code;
1380 } // namespace remoting