Revert 264226 "Reduce dependency of TiclInvalidationService on P..."
[chromium-blink-merge.git] / remoting / host / remoting_me2me_host.cc
blobb9f405764142ffef0c69ae35fa560a31d406cfe7
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/host/branding.h"
40 #include "remoting/host/chromoting_host.h"
41 #include "remoting/host/chromoting_host_context.h"
42 #include "remoting/host/chromoting_messages.h"
43 #include "remoting/host/config_file_watcher.h"
44 #include "remoting/host/config_watcher.h"
45 #include "remoting/host/desktop_environment.h"
46 #include "remoting/host/desktop_session_connector.h"
47 #include "remoting/host/dns_blackhole_checker.h"
48 #include "remoting/host/heartbeat_sender.h"
49 #include "remoting/host/host_change_notification_listener.h"
50 #include "remoting/host/host_config.h"
51 #include "remoting/host/host_event_logger.h"
52 #include "remoting/host/host_exit_codes.h"
53 #include "remoting/host/host_main.h"
54 #include "remoting/host/host_status_sender.h"
55 #include "remoting/host/ipc_constants.h"
56 #include "remoting/host/ipc_desktop_environment.h"
57 #include "remoting/host/ipc_host_event_logger.h"
58 #include "remoting/host/json_host_config.h"
59 #include "remoting/host/log_to_server.h"
60 #include "remoting/host/logging.h"
61 #include "remoting/host/me2me_desktop_environment.h"
62 #include "remoting/host/pairing_registry_delegate.h"
63 #include "remoting/host/policy_hack/policy_watcher.h"
64 #include "remoting/host/service_urls.h"
65 #include "remoting/host/session_manager_factory.h"
66 #include "remoting/host/signaling_connector.h"
67 #include "remoting/host/token_validator_factory_impl.h"
68 #include "remoting/host/usage_stats_consent.h"
69 #include "remoting/host/username.h"
70 #include "remoting/jingle_glue/network_settings.h"
71 #include "remoting/jingle_glue/xmpp_signal_strategy.h"
72 #include "remoting/protocol/me2me_host_authenticator_factory.h"
73 #include "remoting/protocol/pairing_registry.h"
74 #include "remoting/protocol/token_validator.h"
76 #if defined(OS_POSIX)
77 #include <signal.h>
78 #include <sys/types.h>
79 #include <unistd.h>
80 #include "base/file_descriptor_posix.h"
81 #include "remoting/host/pam_authorization_factory_posix.h"
82 #include "remoting/host/posix/signal_handler.h"
83 #endif // defined(OS_POSIX)
85 #if defined(OS_MACOSX)
86 #include "base/mac/scoped_cftyperef.h"
87 #endif // defined(OS_MACOSX)
89 #if defined(OS_LINUX)
90 #include "remoting/host/audio_capturer_linux.h"
91 #endif // defined(OS_LINUX)
93 #if defined(OS_WIN)
94 #include <commctrl.h>
95 #include "base/win/registry.h"
96 #include "base/win/scoped_handle.h"
97 #include "remoting/host/pairing_registry_delegate_win.h"
98 #include "remoting/host/win/session_desktop_environment.h"
99 #endif // defined(OS_WIN)
101 #if defined(TOOLKIT_GTK)
102 #include "ui/gfx/gtk_util.h"
103 #endif // defined(TOOLKIT_GTK)
105 using remoting::protocol::PairingRegistry;
107 namespace {
109 // This is used for tagging system event logs.
110 const char kApplicationName[] = "chromoting";
112 #if defined(OS_LINUX)
113 // The command line switch used to pass name of the pipe to capture audio on
114 // linux.
115 const char kAudioPipeSwitchName[] = "audio-pipe-name";
117 // The command line switch used to pass name of the unix domain socket used to
118 // listen for gnubby requests.
119 const char kAuthSocknameSwitchName[] = "ssh-auth-sockname";
120 #endif // defined(OS_LINUX)
122 // The command line switch used by the parent to request the host to signal it
123 // when it is successfully started.
124 const char kSignalParentSwitchName[] = "signal-parent";
126 // Value used for --host-config option to indicate that the path must be read
127 // from stdin.
128 const char kStdinConfigPath[] = "-";
130 } // namespace
132 namespace remoting {
134 class HostProcess
135 : public ConfigWatcher::Delegate,
136 public HeartbeatSender::Listener,
137 public HostChangeNotificationListener::Listener,
138 public IPC::Listener,
139 public base::RefCountedThreadSafe<HostProcess> {
140 public:
141 HostProcess(scoped_ptr<ChromotingHostContext> context,
142 int* exit_code_out);
144 // ConfigWatcher::Delegate interface.
145 virtual void OnConfigUpdated(const std::string& serialized_config) OVERRIDE;
146 virtual void OnConfigWatcherError() OVERRIDE;
148 // IPC::Listener implementation.
149 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
150 virtual void OnChannelError() OVERRIDE;
152 // HeartbeatSender::Listener overrides.
153 virtual void OnHeartbeatSuccessful() OVERRIDE;
154 virtual void OnUnknownHostIdError() OVERRIDE;
156 // HostChangeNotificationListener::Listener overrides.
157 virtual void OnHostDeleted() OVERRIDE;
159 // Initializes the pairing registry on Windows.
160 void OnInitializePairingRegistry(
161 IPC::PlatformFileForTransit privileged_key,
162 IPC::PlatformFileForTransit unprivileged_key);
164 private:
165 enum HostState {
166 // Host process has just been started. Waiting for config and policies to be
167 // read from the disk.
168 HOST_INITIALIZING,
170 // Host is started and running.
171 HOST_STARTED,
173 // Host is being stopped and will need to be started again.
174 HOST_STOPPING_TO_RESTART,
176 // Host is being stopped.
177 HOST_STOPPING,
179 // Host has been stopped.
180 HOST_STOPPED,
182 // Allowed state transitions:
183 // INITIALIZING->STARTED
184 // INITIALIZING->STOPPED
185 // STARTED->STOPPING_TO_RESTART
186 // STARTED->STOPPING
187 // STOPPING_TO_RESTART->STARTED
188 // STOPPING_TO_RESTART->STOPPING
189 // STOPPING->STOPPED
190 // STOPPED->STARTED
192 // |host_| must be NULL in INITIALIZING and STOPPED states and not-NULL in
193 // all other states.
196 friend class base::RefCountedThreadSafe<HostProcess>;
197 virtual ~HostProcess();
199 void StartOnNetworkThread();
201 #if defined(OS_POSIX)
202 // Callback passed to RegisterSignalHandler() to handle SIGTERM events.
203 void SigTermHandler(int signal_number);
204 #endif
206 // Called to initialize resources on the UI thread.
207 void StartOnUiThread();
209 // Initializes IPC control channel and config file path from |cmd_line|.
210 // Called on the UI thread.
211 bool InitWithCommandLine(const CommandLine* cmd_line);
213 // Called on the UI thread to start monitoring the configuration file.
214 void StartWatchingConfigChanges();
216 // Called on the network thread to set the host's Authenticator factory.
217 void CreateAuthenticatorFactory();
219 // Tear down resources that run on the UI thread.
220 void ShutdownOnUiThread();
222 // Applies the host config, returning true if successful.
223 bool ApplyConfig(scoped_ptr<JsonHostConfig> config);
225 void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies);
226 bool OnHostDomainPolicyUpdate(const std::string& host_domain);
227 bool OnUsernamePolicyUpdate(bool curtain_required,
228 bool username_match_required);
229 bool OnNatPolicyUpdate(bool nat_traversal_enabled);
230 void OnCurtainPolicyUpdate(bool curtain_required);
231 bool OnHostTalkGadgetPrefixPolicyUpdate(const std::string& talkgadget_prefix);
232 bool OnHostTokenUrlPolicyUpdate(
233 const GURL& token_url,
234 const GURL& token_validation_url,
235 const std::string& token_validation_cert_issuer);
236 bool OnPairingPolicyUpdate(bool pairing_enabled);
237 bool OnGnubbyAuthPolicyUpdate(bool enable_gnubby_auth);
239 void StartHost();
241 void OnAuthFailed();
243 void RestartHost();
245 // Stops the host and shuts down the process with the specified |exit_code|.
246 void ShutdownHost(HostExitCodes exit_code);
248 void ScheduleHostShutdown();
250 void ShutdownOnNetworkThread();
252 // Crashes the process in response to a daemon's request. The daemon passes
253 // the location of the code that detected the fatal error resulted in this
254 // request.
255 void OnCrash(const std::string& function_name,
256 const std::string& file_name,
257 const int& line_number);
259 scoped_ptr<ChromotingHostContext> context_;
261 // Created on the UI thread but used from the network thread.
262 scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
264 // Accessed on the UI thread.
265 scoped_ptr<IPC::ChannelProxy> daemon_channel_;
267 // XMPP server/remoting bot configuration (initialized from the command line).
268 XmppSignalStrategy::XmppServerConfig xmpp_server_config_;
269 std::string directory_bot_jid_;
271 // Created on the UI thread but used from the network thread.
272 base::FilePath host_config_path_;
273 std::string host_config_;
274 scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_;
276 // Accessed on the network thread.
277 HostState state_;
279 scoped_ptr<ConfigWatcher> config_watcher_;
281 std::string host_id_;
282 protocol::SharedSecretHash host_secret_hash_;
283 scoped_refptr<RsaKeyPair> key_pair_;
284 std::string oauth_refresh_token_;
285 std::string serialized_config_;
286 std::string host_owner_;
287 bool use_service_account_;
288 scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_;
289 bool allow_nat_traversal_;
290 std::string talkgadget_prefix_;
291 bool allow_pairing_;
293 bool curtain_required_;
294 ThirdPartyAuthConfig third_party_auth_config_;
295 bool enable_gnubby_auth_;
297 scoped_ptr<OAuthTokenGetter> oauth_token_getter_;
298 scoped_ptr<XmppSignalStrategy> signal_strategy_;
299 scoped_ptr<SignalingConnector> signaling_connector_;
300 scoped_ptr<HeartbeatSender> heartbeat_sender_;
301 scoped_ptr<HostStatusSender> host_status_sender_;
302 scoped_ptr<HostChangeNotificationListener> host_change_notification_listener_;
303 scoped_ptr<LogToServer> log_to_server_;
304 scoped_ptr<HostEventLogger> host_event_logger_;
306 scoped_ptr<ChromotingHost> host_;
308 // Used to keep this HostProcess alive until it is shutdown.
309 scoped_refptr<HostProcess> self_;
311 #if defined(REMOTING_MULTI_PROCESS)
312 DesktopSessionConnector* desktop_session_connector_;
313 #endif // defined(REMOTING_MULTI_PROCESS)
315 int* exit_code_out_;
316 bool signal_parent_;
318 scoped_ptr<PairingRegistry::Delegate> pairing_registry_delegate_;
321 HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context,
322 int* exit_code_out)
323 : context_(context.Pass()),
324 state_(HOST_INITIALIZING),
325 use_service_account_(false),
326 allow_nat_traversal_(true),
327 allow_pairing_(true),
328 curtain_required_(false),
329 enable_gnubby_auth_(false),
330 #if defined(REMOTING_MULTI_PROCESS)
331 desktop_session_connector_(NULL),
332 #endif // defined(REMOTING_MULTI_PROCESS)
333 self_(this),
334 exit_code_out_(exit_code_out),
335 signal_parent_(false) {
336 StartOnUiThread();
339 HostProcess::~HostProcess() {
340 // Verify that UI components have been torn down.
341 DCHECK(!config_watcher_);
342 DCHECK(!daemon_channel_);
343 DCHECK(!desktop_environment_factory_);
345 // We might be getting deleted on one of the threads the |host_context| owns,
346 // so we need to post it back to the caller thread to safely join & delete the
347 // threads it contains. This will go away when we move to AutoThread.
348 // |context_release()| will null |context_| before the method is invoked, so
349 // we need to pull out the task-runner on which to call DeleteSoon first.
350 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
351 context_->ui_task_runner();
352 task_runner->DeleteSoon(FROM_HERE, context_.release());
355 bool HostProcess::InitWithCommandLine(const CommandLine* cmd_line) {
356 #if defined(REMOTING_MULTI_PROCESS)
357 // Parse the handle value and convert it to a handle/file descriptor.
358 std::string channel_name =
359 cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
361 int pipe_handle = 0;
362 if (channel_name.empty() ||
363 !base::StringToInt(channel_name, &pipe_handle)) {
364 LOG(ERROR) << "Invalid '" << kDaemonPipeSwitchName
365 << "' value: " << channel_name;
366 return false;
369 #if defined(OS_WIN)
370 base::win::ScopedHandle pipe(reinterpret_cast<HANDLE>(pipe_handle));
371 IPC::ChannelHandle channel_handle(pipe);
372 #elif defined(OS_POSIX)
373 base::FileDescriptor pipe(pipe_handle, true);
374 IPC::ChannelHandle channel_handle(channel_name, pipe);
375 #endif // defined(OS_POSIX)
377 // Connect to the daemon process.
378 daemon_channel_.reset(new IPC::ChannelProxy(
379 channel_handle,
380 IPC::Channel::MODE_CLIENT,
381 this,
382 context_->network_task_runner()));
383 #else // !defined(REMOTING_MULTI_PROCESS)
384 // Connect to the daemon process.
385 std::string channel_name =
386 cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
387 if (!channel_name.empty()) {
388 daemon_channel_.reset(
389 new IPC::ChannelProxy(channel_name,
390 IPC::Channel::MODE_CLIENT,
391 this,
392 context_->network_task_runner().get()));
395 if (cmd_line->HasSwitch(kHostConfigSwitchName)) {
396 host_config_path_ = cmd_line->GetSwitchValuePath(kHostConfigSwitchName);
398 // Read config from stdin if necessary.
399 if (host_config_path_ == base::FilePath(kStdinConfigPath)) {
400 char buf[4096];
401 size_t len;
402 while ((len = fread(buf, 1, sizeof(buf), stdin)) > 0) {
403 host_config_.append(buf, len);
406 } else {
407 base::FilePath default_config_dir = remoting::GetConfigDir();
408 host_config_path_ = default_config_dir.Append(kDefaultHostConfigFile);
411 if (host_config_path_ != base::FilePath(kStdinConfigPath) &&
412 !base::PathExists(host_config_path_)) {
413 LOG(ERROR) << "Can't find host config at " << host_config_path_.value();
414 return false;
416 #endif // !defined(REMOTING_MULTI_PROCESS)
418 // Ignore certificate requests - the host currently has no client certificate
419 // support, so ignoring certificate requests allows connecting to servers that
420 // request, but don't require, a certificate (optional client authentication).
421 net::URLFetcher::SetIgnoreCertificateRequests(true);
423 ServiceUrls* service_urls = ServiceUrls::GetInstance();
424 bool xmpp_server_valid = net::ParseHostAndPort(
425 service_urls->xmpp_server_address(),
426 &xmpp_server_config_.host, &xmpp_server_config_.port);
427 if (!xmpp_server_valid) {
428 LOG(ERROR) << "Invalid XMPP server: " <<
429 service_urls->xmpp_server_address();
430 return false;
432 xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
433 directory_bot_jid_ = service_urls->directory_bot_jid();
435 signal_parent_ = cmd_line->HasSwitch(kSignalParentSwitchName);
437 return true;
440 void HostProcess::OnConfigUpdated(
441 const std::string& serialized_config) {
442 if (!context_->network_task_runner()->BelongsToCurrentThread()) {
443 context_->network_task_runner()->PostTask(FROM_HERE,
444 base::Bind(&HostProcess::OnConfigUpdated, this, serialized_config));
445 return;
448 // Filter out duplicates.
449 if (serialized_config_ == serialized_config)
450 return;
452 HOST_LOG << "Processing new host configuration.";
454 serialized_config_ = serialized_config;
455 scoped_ptr<JsonHostConfig> config(new JsonHostConfig(base::FilePath()));
456 if (!config->SetSerializedData(serialized_config)) {
457 LOG(ERROR) << "Invalid configuration.";
458 ShutdownHost(kInvalidHostConfigurationExitCode);
459 return;
462 if (!ApplyConfig(config.Pass())) {
463 LOG(ERROR) << "Failed to apply the configuration.";
464 ShutdownHost(kInvalidHostConfigurationExitCode);
465 return;
468 if (state_ == HOST_INITIALIZING) {
469 // TODO(sergeyu): Currently OnPolicyUpdate() assumes that host config is
470 // already loaded so PolicyWatcher has to be started here. Separate policy
471 // loading from policy verifications and move |policy_watcher_|
472 // initialization to StartOnNetworkThread().
473 policy_watcher_.reset(
474 policy_hack::PolicyWatcher::Create(context_->file_task_runner()));
475 policy_watcher_->StartWatching(
476 base::Bind(&HostProcess::OnPolicyUpdate, base::Unretained(this)));
477 } else if (state_ == HOST_STARTED) {
478 // TODO(sergeyu): Here we assume that PIN is the only part of the config
479 // that may change while the service is running. Change ApplyConfig() to
480 // detect other changes in the config and restart host if necessary here.
481 CreateAuthenticatorFactory();
485 void HostProcess::OnConfigWatcherError() {
486 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
487 ShutdownHost(kInvalidHostConfigurationExitCode);
490 void HostProcess::StartOnNetworkThread() {
491 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
493 #if !defined(REMOTING_MULTI_PROCESS)
494 if (host_config_path_ == base::FilePath(kStdinConfigPath)) {
495 // Process config we've read from stdin.
496 OnConfigUpdated(host_config_);
497 } else {
498 // Start watching the host configuration file.
499 config_watcher_.reset(new ConfigFileWatcher(context_->network_task_runner(),
500 context_->file_task_runner(),
501 host_config_path_));
502 config_watcher_->Watch(this);
504 #endif // !defined(REMOTING_MULTI_PROCESS)
506 #if defined(OS_POSIX)
507 remoting::RegisterSignalHandler(
508 SIGTERM,
509 base::Bind(&HostProcess::SigTermHandler, base::Unretained(this)));
510 #endif // defined(OS_POSIX)
513 #if defined(OS_POSIX)
514 void HostProcess::SigTermHandler(int signal_number) {
515 DCHECK(signal_number == SIGTERM);
516 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
517 HOST_LOG << "Caught SIGTERM: Shutting down...";
518 ShutdownHost(kSuccessExitCode);
520 #endif // OS_POSIX
522 void HostProcess::CreateAuthenticatorFactory() {
523 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
525 if (state_ != HOST_STARTED)
526 return;
528 std::string local_certificate = key_pair_->GenerateCertificate();
529 if (local_certificate.empty()) {
530 LOG(ERROR) << "Failed to generate host certificate.";
531 ShutdownHost(kInitializationFailed);
532 return;
535 scoped_refptr<PairingRegistry> pairing_registry = NULL;
536 if (allow_pairing_) {
537 if (!pairing_registry_delegate_)
538 pairing_registry_delegate_ = CreatePairingRegistryDelegate();
540 if (pairing_registry_delegate_) {
541 pairing_registry = new PairingRegistry(context_->file_task_runner(),
542 pairing_registry_delegate_.Pass());
546 scoped_ptr<protocol::AuthenticatorFactory> factory;
548 if (third_party_auth_config_.is_empty()) {
549 factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
550 use_service_account_, host_owner_, local_certificate, key_pair_,
551 host_secret_hash_, pairing_registry);
553 } else if (third_party_auth_config_.is_valid()) {
554 scoped_ptr<protocol::TokenValidatorFactory> token_validator_factory(
555 new TokenValidatorFactoryImpl(
556 third_party_auth_config_,
557 key_pair_, context_->url_request_context_getter()));
558 factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
559 use_service_account_, host_owner_, local_certificate, key_pair_,
560 token_validator_factory.Pass());
562 } else {
563 // TODO(rmsousa): If the policy is bad the host should not go online. It
564 // should keep running, but not connected, until the policies are fixed.
565 // Having it show up as online and then reject all clients is misleading.
566 LOG(ERROR) << "One of the third-party token URLs is empty or invalid. "
567 << "Host will reject all clients until policies are corrected. "
568 << "TokenUrl: " << third_party_auth_config_.token_url << ", "
569 << "TokenValidationUrl: "
570 << third_party_auth_config_.token_validation_url;
571 factory = protocol::Me2MeHostAuthenticatorFactory::CreateRejecting();
574 #if defined(OS_POSIX)
575 // On Linux and Mac, perform a PAM authorization step after authentication.
576 factory.reset(new PamAuthorizationFactory(factory.Pass()));
577 #endif
578 host_->SetAuthenticatorFactory(factory.Pass());
580 host_->set_pairing_registry(pairing_registry);
583 // IPC::Listener implementation.
584 bool HostProcess::OnMessageReceived(const IPC::Message& message) {
585 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
587 #if defined(REMOTING_MULTI_PROCESS)
588 bool handled = true;
589 IPC_BEGIN_MESSAGE_MAP(HostProcess, message)
590 IPC_MESSAGE_HANDLER(ChromotingDaemonMsg_Crash, OnCrash)
591 IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Configuration,
592 OnConfigUpdated)
593 IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_InitializePairingRegistry,
594 OnInitializePairingRegistry)
595 IPC_MESSAGE_FORWARD(
596 ChromotingDaemonNetworkMsg_DesktopAttached,
597 desktop_session_connector_,
598 DesktopSessionConnector::OnDesktopSessionAgentAttached)
599 IPC_MESSAGE_FORWARD(ChromotingDaemonNetworkMsg_TerminalDisconnected,
600 desktop_session_connector_,
601 DesktopSessionConnector::OnTerminalDisconnected)
602 IPC_MESSAGE_UNHANDLED(handled = false)
603 IPC_END_MESSAGE_MAP()
605 CHECK(handled) << "Received unexpected IPC type: " << message.type();
606 return handled;
608 #else // !defined(REMOTING_MULTI_PROCESS)
609 return false;
610 #endif // !defined(REMOTING_MULTI_PROCESS)
613 void HostProcess::OnChannelError() {
614 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
616 // Shutdown the host if the daemon process disconnects the IPC channel.
617 context_->network_task_runner()->PostTask(
618 FROM_HERE,
619 base::Bind(&HostProcess::ShutdownHost, this, kSuccessExitCode));
622 void HostProcess::StartOnUiThread() {
623 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
625 if (!InitWithCommandLine(CommandLine::ForCurrentProcess())) {
626 // Shutdown the host if the command line is invalid.
627 context_->network_task_runner()->PostTask(
628 FROM_HERE, base::Bind(&HostProcess::ShutdownHost, this,
629 kUsageExitCode));
630 return;
633 #if defined(OS_LINUX)
634 // If an audio pipe is specific on the command-line then initialize
635 // AudioCapturerLinux to capture from it.
636 base::FilePath audio_pipe_name = CommandLine::ForCurrentProcess()->
637 GetSwitchValuePath(kAudioPipeSwitchName);
638 if (!audio_pipe_name.empty()) {
639 remoting::AudioCapturerLinux::InitializePipeReader(
640 context_->audio_task_runner(), audio_pipe_name);
643 base::FilePath gnubby_socket_name = CommandLine::ForCurrentProcess()->
644 GetSwitchValuePath(kAuthSocknameSwitchName);
645 if (!gnubby_socket_name.empty())
646 remoting::GnubbyAuthHandler::SetGnubbySocketName(gnubby_socket_name);
647 #endif // defined(OS_LINUX)
649 // Create a desktop environment factory appropriate to the build type &
650 // platform.
651 #if defined(OS_WIN)
652 IpcDesktopEnvironmentFactory* desktop_environment_factory =
653 new IpcDesktopEnvironmentFactory(
654 context_->audio_task_runner(),
655 context_->network_task_runner(),
656 context_->video_capture_task_runner(),
657 context_->network_task_runner(),
658 daemon_channel_.get());
659 desktop_session_connector_ = desktop_environment_factory;
660 #else // !defined(OS_WIN)
661 DesktopEnvironmentFactory* desktop_environment_factory =
662 new Me2MeDesktopEnvironmentFactory(
663 context_->network_task_runner(),
664 context_->input_task_runner(),
665 context_->ui_task_runner());
666 #endif // !defined(OS_WIN)
668 desktop_environment_factory_.reset(desktop_environment_factory);
669 desktop_environment_factory_->SetEnableGnubbyAuth(enable_gnubby_auth_);
671 context_->network_task_runner()->PostTask(
672 FROM_HERE,
673 base::Bind(&HostProcess::StartOnNetworkThread, this));
676 void HostProcess::ShutdownOnUiThread() {
677 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
679 // Tear down resources that need to be torn down on the UI thread.
680 network_change_notifier_.reset();
681 daemon_channel_.reset();
682 desktop_environment_factory_.reset();
684 // It is now safe for the HostProcess to be deleted.
685 self_ = NULL;
687 #if defined(OS_LINUX)
688 // Cause the global AudioPipeReader to be freed, otherwise the audio
689 // thread will remain in-use and prevent the process from exiting.
690 // TODO(wez): DesktopEnvironmentFactory should own the pipe reader.
691 // See crbug.com/161373 and crbug.com/104544.
692 AudioCapturerLinux::InitializePipeReader(NULL, base::FilePath());
693 #endif
696 // Overridden from HeartbeatSender::Listener
697 void HostProcess::OnUnknownHostIdError() {
698 LOG(ERROR) << "Host ID not found.";
699 ShutdownHost(kInvalidHostIdExitCode);
702 void HostProcess::OnHeartbeatSuccessful() {
703 HOST_LOG << "Host ready to receive connections.";
704 #if defined(OS_POSIX)
705 if (signal_parent_) {
706 kill(getppid(), SIGUSR1);
707 signal_parent_ = false;
709 #endif
712 void HostProcess::OnHostDeleted() {
713 LOG(ERROR) << "Host was deleted from the directory.";
714 ShutdownHost(kInvalidHostIdExitCode);
717 void HostProcess::OnInitializePairingRegistry(
718 IPC::PlatformFileForTransit privileged_key,
719 IPC::PlatformFileForTransit unprivileged_key) {
720 DCHECK(!pairing_registry_delegate_);
722 #if defined(OS_WIN)
723 // Initialize the pairing registry delegate.
724 scoped_ptr<PairingRegistryDelegateWin> delegate(
725 new PairingRegistryDelegateWin());
726 bool result = delegate->SetRootKeys(
727 reinterpret_cast<HKEY>(
728 IPC::PlatformFileForTransitToPlatformFile(privileged_key)),
729 reinterpret_cast<HKEY>(
730 IPC::PlatformFileForTransitToPlatformFile(unprivileged_key)));
731 if (!result)
732 return;
734 pairing_registry_delegate_ = delegate.PassAs<PairingRegistry::Delegate>();
735 #else // !defined(OS_WIN)
736 NOTREACHED();
737 #endif // !defined(OS_WIN)
740 // Applies the host config, returning true if successful.
741 bool HostProcess::ApplyConfig(scoped_ptr<JsonHostConfig> config) {
742 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
744 if (!config->GetString(kHostIdConfigPath, &host_id_)) {
745 LOG(ERROR) << "host_id is not defined in the config.";
746 return false;
749 std::string key_base64;
750 if (!config->GetString(kPrivateKeyConfigPath, &key_base64)) {
751 LOG(ERROR) << "Private key couldn't be read from the config file.";
752 return false;
755 key_pair_ = RsaKeyPair::FromString(key_base64);
756 if (!key_pair_.get()) {
757 LOG(ERROR) << "Invalid private key in the config file.";
758 return false;
761 std::string host_secret_hash_string;
762 if (!config->GetString(kHostSecretHashConfigPath,
763 &host_secret_hash_string)) {
764 host_secret_hash_string = "plain:";
767 if (!host_secret_hash_.Parse(host_secret_hash_string)) {
768 LOG(ERROR) << "Invalid host_secret_hash.";
769 return false;
772 // Use an XMPP connection to the Talk network for session signalling.
773 if (!config->GetString(kXmppLoginConfigPath, &xmpp_server_config_.username) ||
774 !(config->GetString(kXmppAuthTokenConfigPath,
775 &xmpp_server_config_.auth_token) ||
776 config->GetString(kOAuthRefreshTokenConfigPath,
777 &oauth_refresh_token_))) {
778 LOG(ERROR) << "XMPP credentials are not defined in the config.";
779 return false;
782 if (!oauth_refresh_token_.empty()) {
783 // SignalingConnector is responsible for getting OAuth token.
784 xmpp_server_config_.auth_token = "";
785 xmpp_server_config_.auth_service = "oauth2";
786 } else if (!config->GetString(kXmppAuthServiceConfigPath,
787 &xmpp_server_config_.auth_service)) {
788 // For the me2me host, we default to ClientLogin token for chromiumsync
789 // because earlier versions of the host had no HTTP stack with which to
790 // request an OAuth2 access token.
791 xmpp_server_config_.auth_service = kChromotingTokenDefaultServiceName;
794 if (config->GetString(kHostOwnerConfigPath, &host_owner_)) {
795 // Service account configs have a host_owner, different from the xmpp_login.
796 use_service_account_ = true;
797 } else {
798 // User credential configs only have an xmpp_login, which is also the owner.
799 host_owner_ = xmpp_server_config_.username;
800 use_service_account_ = false;
802 return true;
805 void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
806 // TODO(rmsousa): Consolidate all On*PolicyUpdate methods into this one.
807 // TODO(sergeyu): Currently polices are verified only when they are loaded.
808 // Separate policy loading from policy verifications - this will allow to
809 // check policies again later, e.g. when host config changes.
811 if (!context_->network_task_runner()->BelongsToCurrentThread()) {
812 context_->network_task_runner()->PostTask(FROM_HERE, base::Bind(
813 &HostProcess::OnPolicyUpdate, this, base::Passed(&policies)));
814 return;
817 bool restart_required = false;
818 bool bool_value;
819 std::string string_value;
820 if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
821 &string_value)) {
822 restart_required |= OnHostDomainPolicyUpdate(string_value);
824 bool curtain_required = false;
825 if (policies->GetBoolean(
826 policy_hack::PolicyWatcher::kHostRequireCurtainPolicyName,
827 &curtain_required)) {
828 OnCurtainPolicyUpdate(curtain_required);
830 if (policies->GetBoolean(
831 policy_hack::PolicyWatcher::kHostMatchUsernamePolicyName,
832 &bool_value)) {
833 restart_required |= OnUsernamePolicyUpdate(curtain_required, bool_value);
835 if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
836 &bool_value)) {
837 restart_required |= OnNatPolicyUpdate(bool_value);
839 if (policies->GetString(
840 policy_hack::PolicyWatcher::kHostTalkGadgetPrefixPolicyName,
841 &string_value)) {
842 restart_required |= OnHostTalkGadgetPrefixPolicyUpdate(string_value);
844 std::string token_url_string, token_validation_url_string;
845 std::string token_validation_cert_issuer;
846 if (policies->GetString(
847 policy_hack::PolicyWatcher::kHostTokenUrlPolicyName,
848 &token_url_string) &&
849 policies->GetString(
850 policy_hack::PolicyWatcher::kHostTokenValidationUrlPolicyName,
851 &token_validation_url_string) &&
852 policies->GetString(
853 policy_hack::PolicyWatcher::kHostTokenValidationCertIssuerPolicyName,
854 &token_validation_cert_issuer)) {
855 restart_required |= OnHostTokenUrlPolicyUpdate(
856 GURL(token_url_string), GURL(token_validation_url_string),
857 token_validation_cert_issuer);
859 if (policies->GetBoolean(
860 policy_hack::PolicyWatcher::kHostAllowClientPairing,
861 &bool_value)) {
862 restart_required |= OnPairingPolicyUpdate(bool_value);
864 if (policies->GetBoolean(
865 policy_hack::PolicyWatcher::kHostAllowGnubbyAuthPolicyName,
866 &bool_value))
867 restart_required |= OnGnubbyAuthPolicyUpdate(bool_value);
869 if (state_ == HOST_INITIALIZING) {
870 StartHost();
871 } else if (state_ == HOST_STARTED && restart_required) {
872 RestartHost();
876 bool HostProcess::OnHostDomainPolicyUpdate(const std::string& host_domain) {
877 // Returns true if the host has to be restarted after this policy update.
878 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
880 HOST_LOG << "Policy sets host domain: " << host_domain;
882 if (!host_domain.empty() &&
883 !EndsWith(host_owner_, std::string("@") + host_domain, false)) {
884 ShutdownHost(kInvalidHostDomainExitCode);
886 return false;
889 bool HostProcess::OnUsernamePolicyUpdate(bool curtain_required,
890 bool host_username_match_required) {
891 // Returns false: never restart the host after this policy update.
892 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
894 if (host_username_match_required) {
895 HOST_LOG << "Policy requires host username match.";
896 std::string username = GetUsername();
897 bool shutdown = username.empty() ||
898 !StartsWithASCII(host_owner_, username + std::string("@"),
899 false);
901 #if defined(OS_MACOSX)
902 // On Mac, we run as root at the login screen, so the username won't match.
903 // However, there's no need to enforce the policy at the login screen, as
904 // the client will have to reconnect if a login occurs.
905 if (shutdown && getuid() == 0) {
906 shutdown = false;
908 #endif
910 // Curtain-mode on Windows presents the standard OS login prompt to the user
911 // for each connection, removing the need for an explicit user-name matching
912 // check.
913 #if defined(OS_WIN) && defined(REMOTING_RDP_SESSION)
914 if (curtain_required)
915 return false;
916 #endif // defined(OS_WIN) && defined(REMOTING_RDP_SESSION)
918 // Shutdown the host if the username does not match.
919 if (shutdown) {
920 LOG(ERROR) << "The host username does not match.";
921 ShutdownHost(kUsernameMismatchExitCode);
923 } else {
924 HOST_LOG << "Policy does not require host username match.";
927 return false;
930 bool HostProcess::OnNatPolicyUpdate(bool nat_traversal_enabled) {
931 // Returns true if the host has to be restarted after this policy update.
932 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
934 if (allow_nat_traversal_ != nat_traversal_enabled) {
935 if (nat_traversal_enabled)
936 HOST_LOG << "Policy enables NAT traversal.";
937 else
938 HOST_LOG << "Policy disables NAT traversal.";
939 allow_nat_traversal_ = nat_traversal_enabled;
940 return true;
942 return false;
945 void HostProcess::OnCurtainPolicyUpdate(bool curtain_required) {
946 // Returns true if the host has to be restarted after this policy update.
947 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
949 #if defined(OS_MACOSX)
950 if (curtain_required) {
951 // When curtain mode is in effect on Mac, the host process runs in the
952 // user's switched-out session, but launchd will also run an instance at
953 // the console login screen. Even if no user is currently logged-on, we
954 // can't support remote-access to the login screen because the current host
955 // process model disconnects the client during login, which would leave
956 // the logged in session un-curtained on the console until they reconnect.
958 // TODO(jamiewalch): Fix this once we have implemented the multi-process
959 // daemon architecture (crbug.com/134894)
960 if (getuid() == 0) {
961 LOG(ERROR) << "Running the host in the console login session is yet not "
962 "supported.";
963 ShutdownHost(kLoginScreenNotSupportedExitCode);
964 return;
967 #endif
969 if (curtain_required_ != curtain_required) {
970 if (curtain_required)
971 HOST_LOG << "Policy requires curtain-mode.";
972 else
973 HOST_LOG << "Policy does not require curtain-mode.";
974 curtain_required_ = curtain_required;
975 if (host_)
976 host_->SetEnableCurtaining(curtain_required_);
980 bool HostProcess::OnHostTalkGadgetPrefixPolicyUpdate(
981 const std::string& talkgadget_prefix) {
982 // Returns true if the host has to be restarted after this policy update.
983 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
985 if (talkgadget_prefix != talkgadget_prefix_) {
986 HOST_LOG << "Policy sets talkgadget prefix: " << talkgadget_prefix;
987 talkgadget_prefix_ = talkgadget_prefix;
988 return true;
990 return false;
993 bool HostProcess::OnHostTokenUrlPolicyUpdate(
994 const GURL& token_url,
995 const GURL& token_validation_url,
996 const std::string& token_validation_cert_issuer) {
997 // Returns true if the host has to be restarted after this policy update.
998 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1000 if (third_party_auth_config_.token_url != token_url ||
1001 third_party_auth_config_.token_validation_url != token_validation_url ||
1002 third_party_auth_config_.token_validation_cert_issuer !=
1003 token_validation_cert_issuer) {
1004 HOST_LOG << "Policy sets third-party token URLs: "
1005 << "TokenUrl: " << token_url << ", "
1006 << "TokenValidationUrl: " << token_validation_url
1007 << "TokenValidationCertificateIssuer: "
1008 << token_validation_cert_issuer;
1009 third_party_auth_config_.token_url = token_url;
1010 third_party_auth_config_.token_validation_url = token_validation_url;
1011 third_party_auth_config_.token_validation_cert_issuer =
1012 token_validation_cert_issuer;
1013 return true;
1016 return false;
1019 bool HostProcess::OnPairingPolicyUpdate(bool allow_pairing) {
1020 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1022 if (allow_pairing_ == allow_pairing)
1023 return false;
1025 if (allow_pairing)
1026 HOST_LOG << "Policy enables client pairing.";
1027 else
1028 HOST_LOG << "Policy disables client pairing.";
1029 allow_pairing_ = allow_pairing;
1030 return true;
1033 bool HostProcess::OnGnubbyAuthPolicyUpdate(bool enable_gnubby_auth) {
1034 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1036 if (enable_gnubby_auth_ == enable_gnubby_auth)
1037 return false;
1039 if (enable_gnubby_auth) {
1040 HOST_LOG << "Policy enables gnubby auth.";
1041 } else {
1042 HOST_LOG << "Policy disables gnubby auth.";
1044 enable_gnubby_auth_ = enable_gnubby_auth;
1046 if (desktop_environment_factory_)
1047 desktop_environment_factory_->SetEnableGnubbyAuth(enable_gnubby_auth);
1049 return true;
1052 void HostProcess::StartHost() {
1053 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1054 DCHECK(!host_);
1055 DCHECK(!signal_strategy_.get());
1056 DCHECK(state_ == HOST_INITIALIZING || state_ == HOST_STOPPING_TO_RESTART ||
1057 state_ == HOST_STOPPED) << state_;
1058 state_ = HOST_STARTED;
1060 signal_strategy_.reset(
1061 new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
1062 context_->url_request_context_getter(),
1063 xmpp_server_config_));
1065 scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker(
1066 new DnsBlackholeChecker(context_->url_request_context_getter(),
1067 talkgadget_prefix_));
1069 // Create a NetworkChangeNotifier for use by the signaling connector.
1070 network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
1072 signaling_connector_.reset(new SignalingConnector(
1073 signal_strategy_.get(),
1074 dns_blackhole_checker.Pass(),
1075 base::Bind(&HostProcess::OnAuthFailed, this)));
1077 if (!oauth_refresh_token_.empty()) {
1078 scoped_ptr<OAuthTokenGetter::OAuthCredentials> oauth_credentials;
1079 oauth_credentials.reset(
1080 new OAuthTokenGetter::OAuthCredentials(
1081 xmpp_server_config_.username, oauth_refresh_token_,
1082 use_service_account_));
1084 oauth_token_getter_.reset(new OAuthTokenGetter(
1085 oauth_credentials.Pass(), context_->url_request_context_getter()));
1087 signaling_connector_->EnableOAuth(oauth_token_getter_.get());
1090 NetworkSettings network_settings(
1091 allow_nat_traversal_ ?
1092 NetworkSettings::NAT_TRAVERSAL_ENABLED :
1093 NetworkSettings::NAT_TRAVERSAL_DISABLED);
1094 if (!allow_nat_traversal_) {
1095 network_settings.min_port = NetworkSettings::kDefaultMinPort;
1096 network_settings.max_port = NetworkSettings::kDefaultMaxPort;
1099 host_.reset(new ChromotingHost(
1100 signal_strategy_.get(),
1101 desktop_environment_factory_.get(),
1102 CreateHostSessionManager(signal_strategy_.get(), network_settings,
1103 context_->url_request_context_getter()),
1104 context_->audio_task_runner(),
1105 context_->input_task_runner(),
1106 context_->video_capture_task_runner(),
1107 context_->video_encode_task_runner(),
1108 context_->network_task_runner(),
1109 context_->ui_task_runner()));
1111 // TODO(simonmorris): Get the maximum session duration from a policy.
1112 #if defined(OS_LINUX)
1113 host_->SetMaximumSessionDuration(base::TimeDelta::FromHours(20));
1114 #endif
1116 heartbeat_sender_.reset(new HeartbeatSender(
1117 this, host_id_, signal_strategy_.get(), key_pair_,
1118 directory_bot_jid_));
1120 host_status_sender_.reset(new HostStatusSender(
1121 host_id_, signal_strategy_.get(), key_pair_, directory_bot_jid_));
1123 host_change_notification_listener_.reset(new HostChangeNotificationListener(
1124 this, host_id_, signal_strategy_.get(), directory_bot_jid_));
1126 log_to_server_.reset(
1127 new LogToServer(host_->AsWeakPtr(), ServerLogEntry::ME2ME,
1128 signal_strategy_.get(), directory_bot_jid_));
1130 // Set up repoting the host status notifications.
1131 #if defined(REMOTING_MULTI_PROCESS)
1132 host_event_logger_.reset(
1133 new IpcHostEventLogger(host_->AsWeakPtr(), daemon_channel_.get()));
1134 #else // !defined(REMOTING_MULTI_PROCESS)
1135 host_event_logger_ =
1136 HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
1137 #endif // !defined(REMOTING_MULTI_PROCESS)
1139 host_->SetEnableCurtaining(curtain_required_);
1140 host_->Start(host_owner_);
1142 CreateAuthenticatorFactory();
1145 void HostProcess::OnAuthFailed() {
1146 ShutdownHost(kInvalidOauthCredentialsExitCode);
1149 void HostProcess::RestartHost() {
1150 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1151 DCHECK_EQ(state_, HOST_STARTED);
1153 state_ = HOST_STOPPING_TO_RESTART;
1154 ShutdownOnNetworkThread();
1157 void HostProcess::ShutdownHost(HostExitCodes exit_code) {
1158 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1160 *exit_code_out_ = exit_code;
1162 switch (state_) {
1163 case HOST_INITIALIZING:
1164 state_ = HOST_STOPPING;
1165 ShutdownOnNetworkThread();
1166 break;
1168 case HOST_STARTED:
1169 state_ = HOST_STOPPING;
1170 host_status_sender_->SendOfflineStatus(exit_code);
1171 ScheduleHostShutdown();
1172 break;
1174 case HOST_STOPPING_TO_RESTART:
1175 state_ = HOST_STOPPING;
1176 break;
1178 case HOST_STOPPING:
1179 case HOST_STOPPED:
1180 // Host is already stopped or being stopped. No action is required.
1181 break;
1185 // TODO(weitaosu): shut down the host once we get an ACK for the offline status
1186 // XMPP message.
1187 void HostProcess::ScheduleHostShutdown() {
1188 // Delay the shutdown by 2 second to allow SendOfflineStatus to complete.
1189 context_->network_task_runner()->PostDelayedTask(
1190 FROM_HERE,
1191 base::Bind(&HostProcess::ShutdownOnNetworkThread, base::Unretained(this)),
1192 base::TimeDelta::FromSeconds(2));
1195 void HostProcess::ShutdownOnNetworkThread() {
1196 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1198 host_.reset();
1199 host_event_logger_.reset();
1200 log_to_server_.reset();
1201 heartbeat_sender_.reset();
1202 host_status_sender_.reset();
1203 host_change_notification_listener_.reset();
1204 signaling_connector_.reset();
1205 oauth_token_getter_.reset();
1206 signal_strategy_.reset();
1207 network_change_notifier_.reset();
1209 if (state_ == HOST_STOPPING_TO_RESTART) {
1210 StartHost();
1211 } else if (state_ == HOST_STOPPING) {
1212 state_ = HOST_STOPPED;
1214 if (policy_watcher_.get()) {
1215 base::WaitableEvent done_event(true, false);
1216 policy_watcher_->StopWatching(&done_event);
1217 done_event.Wait();
1218 policy_watcher_.reset();
1221 config_watcher_.reset();
1223 // Complete the rest of shutdown on the main thread.
1224 context_->ui_task_runner()->PostTask(
1225 FROM_HERE,
1226 base::Bind(&HostProcess::ShutdownOnUiThread, this));
1227 } else {
1228 // This method is only called in STOPPING_TO_RESTART and STOPPING states.
1229 NOTREACHED();
1233 void HostProcess::OnCrash(const std::string& function_name,
1234 const std::string& file_name,
1235 const int& line_number) {
1236 char message[1024];
1237 base::snprintf(message, sizeof(message),
1238 "Requested by %s at %s, line %d.",
1239 function_name.c_str(), file_name.c_str(), line_number);
1240 base::debug::Alias(message);
1242 // The daemon requested us to crash the process.
1243 CHECK(false) << message;
1246 int HostProcessMain() {
1247 #if defined(TOOLKIT_GTK)
1248 // Required for any calls into GTK functions, such as the Disconnect and
1249 // Continue windows, though these should not be used for the Me2Me case
1250 // (crbug.com/104377).
1251 gfx::GtkInitFromCommandLine(*CommandLine::ForCurrentProcess());
1252 #endif // TOOLKIT_GTK
1254 // Enable support for SSL server sockets, which must be done while still
1255 // single-threaded.
1256 net::EnableSSLServerSockets();
1258 // Ensures runtime specific CPU features are initialized.
1259 media::InitializeCPUSpecificMediaFeatures();
1261 // Create the main message loop and start helper threads.
1262 base::MessageLoopForUI message_loop;
1263 scoped_ptr<ChromotingHostContext> context =
1264 ChromotingHostContext::Create(new AutoThreadTaskRunner(
1265 message_loop.message_loop_proxy(), base::MessageLoop::QuitClosure()));
1266 if (!context)
1267 return kInitializationFailed;
1269 // Create & start the HostProcess using these threads.
1270 // TODO(wez): The HostProcess holds a reference to itself until Shutdown().
1271 // Remove this hack as part of the multi-process refactoring.
1272 int exit_code = kSuccessExitCode;
1273 new HostProcess(context.Pass(), &exit_code);
1275 // Run the main (also UI) message loop until the host no longer needs it.
1276 message_loop.Run();
1278 return exit_code;
1281 } // namespace remoting
1283 #if !defined(OS_WIN)
1284 int main(int argc, char** argv) {
1285 return remoting::HostMain(argc, argv);
1287 #endif // !defined(OS_WIN)