• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
6 
7 #include <string>
8 
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 
75 #if defined(OS_POSIX)
76 #include <signal.h>
77 #include <sys/types.h>
78 #include <unistd.h>
79 #include "base/file_descriptor_posix.h"
80 #include "remoting/host/pam_authorization_factory_posix.h"
81 #include "remoting/host/posix/signal_handler.h"
82 #endif  // defined(OS_POSIX)
83 
84 #if defined(OS_MACOSX)
85 #include "base/mac/scoped_cftyperef.h"
86 #endif  // defined(OS_MACOSX)
87 
88 #if defined(OS_LINUX)
89 #include "remoting/host/audio_capturer_linux.h"
90 #endif  // defined(OS_LINUX)
91 
92 #if defined(OS_WIN)
93 #include <commctrl.h>
94 #include "base/win/scoped_handle.h"
95 #include "remoting/host/win/session_desktop_environment.h"
96 #endif  // defined(OS_WIN)
97 
98 #if defined(TOOLKIT_GTK)
99 #include "ui/gfx/gtk_util.h"
100 #endif  // defined(TOOLKIT_GTK)
101 
102 // This is used for tagging system event logs.
103 const char kApplicationName[] = "chromoting";
104 
105 #if defined(OS_LINUX)
106 // The command line switch used to pass name of the pipe to capture audio on
107 // linux.
108 const char kAudioPipeSwitchName[] = "audio-pipe-name";
109 #endif  // defined(OS_LINUX)
110 
111 // The command line switch used by the parent to request the host to signal it
112 // when it is successfully started.
113 const char kSignalParentSwitchName[] = "signal-parent";
114 
115 // Value used for --host-config option to indicate that the path must be read
116 // from stdin.
117 const char kStdinConfigPath[] = "-";
118 
119 namespace remoting {
120 
121 class HostProcess
122     : public ConfigWatcher::Delegate,
123       public HeartbeatSender::Listener,
124       public HostChangeNotificationListener::Listener,
125       public IPC::Listener,
126       public base::RefCountedThreadSafe<HostProcess> {
127  public:
128   HostProcess(scoped_ptr<ChromotingHostContext> context,
129               int* exit_code_out);
130 
131   // ConfigWatcher::Delegate interface.
132   virtual void OnConfigUpdated(const std::string& serialized_config) OVERRIDE;
133   virtual void OnConfigWatcherError() OVERRIDE;
134 
135   // IPC::Listener implementation.
136   virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
137   virtual void OnChannelError() OVERRIDE;
138 
139   // HeartbeatSender::Listener overrides.
140   virtual void OnHeartbeatSuccessful() OVERRIDE;
141   virtual void OnUnknownHostIdError() OVERRIDE;
142 
143   // HostChangeNotificationListener::Listener overrides.
144   virtual void OnHostDeleted() OVERRIDE;
145 
146  private:
147   enum HostState {
148     // Host process has just been started. Waiting for config and policies to be
149     // read from the disk.
150     HOST_INITIALIZING,
151 
152     // Host is started and running.
153     HOST_STARTED,
154 
155     // Host is being stopped and will need to be started again.
156     HOST_STOPPING_TO_RESTART,
157 
158     // Host is being stopped.
159     HOST_STOPPING,
160 
161     // Host has been stopped.
162     HOST_STOPPED,
163 
164     // Allowed state transitions:
165     //   INITIALIZING->STARTED
166     //   INITIALIZING->STOPPED
167     //   STARTED->STOPPING_TO_RESTART
168     //   STARTED->STOPPING
169     //   STOPPING_TO_RESTART->STARTED
170     //   STOPPING_TO_RESTART->STOPPING
171     //   STOPPING->STOPPED
172     //   STOPPED->STARTED
173     //
174     // |host_| must be NULL in INITIALIZING and STOPPED states and not-NULL in
175     // all other states.
176   };
177 
178   friend class base::RefCountedThreadSafe<HostProcess>;
179   virtual ~HostProcess();
180 
181   void StartOnNetworkThread();
182 
183 #if defined(OS_POSIX)
184   // Callback passed to RegisterSignalHandler() to handle SIGTERM events.
185   void SigTermHandler(int signal_number);
186 #endif
187 
188   // Called to initialize resources on the UI thread.
189   void StartOnUiThread();
190 
191   // Initializes IPC control channel and config file path from |cmd_line|.
192   // Called on the UI thread.
193   bool InitWithCommandLine(const CommandLine* cmd_line);
194 
195   // Called on the UI thread to start monitoring the configuration file.
196   void StartWatchingConfigChanges();
197 
198   // Called on the network thread to set the host's Authenticator factory.
199   void CreateAuthenticatorFactory();
200 
201   // Tear down resources that run on the UI thread.
202   void ShutdownOnUiThread();
203 
204   // Applies the host config, returning true if successful.
205   bool ApplyConfig(scoped_ptr<JsonHostConfig> config);
206 
207   void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies);
208   bool OnHostDomainPolicyUpdate(const std::string& host_domain);
209   bool OnUsernamePolicyUpdate(bool curtain_required,
210                               bool username_match_required);
211   bool OnNatPolicyUpdate(bool nat_traversal_enabled);
212   void OnCurtainPolicyUpdate(bool curtain_required);
213   bool OnHostTalkGadgetPrefixPolicyUpdate(const std::string& talkgadget_prefix);
214   bool OnHostTokenUrlPolicyUpdate(const GURL& token_url,
215                                   const GURL& token_validation_url);
216   bool OnPairingPolicyUpdate(bool pairing_enabled);
217 
218   void StartHost();
219 
220   void OnAuthFailed();
221 
222   void RestartHost();
223 
224   // Stops the host and shuts down the process with the specified |exit_code|.
225   void ShutdownHost(HostExitCodes exit_code);
226 
227   void ScheduleHostShutdown();
228 
229   void ShutdownOnNetworkThread();
230 
231   // Crashes the process in response to a daemon's request. The daemon passes
232   // the location of the code that detected the fatal error resulted in this
233   // request.
234   void OnCrash(const std::string& function_name,
235                const std::string& file_name,
236                const int& line_number);
237 
238   scoped_ptr<ChromotingHostContext> context_;
239 
240   // Created on the UI thread but used from the network thread.
241   scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
242 
243   // Accessed on the UI thread.
244   scoped_ptr<IPC::ChannelProxy> daemon_channel_;
245 
246   // XMPP server/remoting bot configuration (initialized from the command line).
247   XmppSignalStrategy::XmppServerConfig xmpp_server_config_;
248   std::string directory_bot_jid_;
249 
250   // Created on the UI thread but used from the network thread.
251   base::FilePath host_config_path_;
252   std::string host_config_;
253   scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_;
254 
255   // Accessed on the network thread.
256   HostState state_;
257 
258   scoped_ptr<ConfigWatcher> config_watcher_;
259 
260   std::string host_id_;
261   protocol::SharedSecretHash host_secret_hash_;
262   scoped_refptr<RsaKeyPair> key_pair_;
263   std::string oauth_refresh_token_;
264   std::string serialized_config_;
265   std::string host_owner_;
266   bool use_service_account_;
267   scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_;
268   bool allow_nat_traversal_;
269   std::string talkgadget_prefix_;
270   bool allow_pairing_;
271 
272   bool curtain_required_;
273   GURL token_url_;
274   GURL token_validation_url_;
275 
276   scoped_ptr<XmppSignalStrategy> signal_strategy_;
277   scoped_ptr<SignalingConnector> signaling_connector_;
278   scoped_ptr<HeartbeatSender> heartbeat_sender_;
279   scoped_ptr<HostStatusSender> host_status_sender_;
280   scoped_ptr<HostChangeNotificationListener> host_change_notification_listener_;
281   scoped_ptr<LogToServer> log_to_server_;
282   scoped_ptr<HostEventLogger> host_event_logger_;
283 
284   scoped_ptr<ChromotingHost> host_;
285 
286   // Used to keep this HostProcess alive until it is shutdown.
287   scoped_refptr<HostProcess> self_;
288 
289 #if defined(REMOTING_MULTI_PROCESS)
290   DesktopSessionConnector* desktop_session_connector_;
291 #endif  // defined(REMOTING_MULTI_PROCESS)
292 
293   int* exit_code_out_;
294   bool signal_parent_;
295 };
296 
HostProcess(scoped_ptr<ChromotingHostContext> context,int * exit_code_out)297 HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context,
298                          int* exit_code_out)
299     : context_(context.Pass()),
300       state_(HOST_INITIALIZING),
301       use_service_account_(false),
302       allow_nat_traversal_(true),
303       allow_pairing_(true),
304       curtain_required_(false),
305 #if defined(REMOTING_MULTI_PROCESS)
306       desktop_session_connector_(NULL),
307 #endif  // defined(REMOTING_MULTI_PROCESS)
308       self_(this),
309       exit_code_out_(exit_code_out),
310       signal_parent_(false) {
311   StartOnUiThread();
312 }
313 
~HostProcess()314 HostProcess::~HostProcess() {
315   // Verify that UI components have been torn down.
316   DCHECK(!config_watcher_);
317   DCHECK(!daemon_channel_);
318   DCHECK(!desktop_environment_factory_);
319 
320   // We might be getting deleted on one of the threads the |host_context| owns,
321   // so we need to post it back to the caller thread to safely join & delete the
322   // threads it contains.  This will go away when we move to AutoThread.
323   // |context_release()| will null |context_| before the method is invoked, so
324   // we need to pull out the task-runner on which to call DeleteSoon first.
325   scoped_refptr<base::SingleThreadTaskRunner> task_runner =
326       context_->ui_task_runner();
327   task_runner->DeleteSoon(FROM_HERE, context_.release());
328 }
329 
InitWithCommandLine(const CommandLine * cmd_line)330 bool HostProcess::InitWithCommandLine(const CommandLine* cmd_line) {
331 #if defined(REMOTING_MULTI_PROCESS)
332   // Parse the handle value and convert it to a handle/file descriptor.
333   std::string channel_name =
334       cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
335 
336   int pipe_handle = 0;
337   if (channel_name.empty() ||
338       !base::StringToInt(channel_name, &pipe_handle)) {
339     LOG(ERROR) << "Invalid '" << kDaemonPipeSwitchName
340                << "' value: " << channel_name;
341     return false;
342   }
343 
344 #if defined(OS_WIN)
345   base::win::ScopedHandle pipe(reinterpret_cast<HANDLE>(pipe_handle));
346   IPC::ChannelHandle channel_handle(pipe);
347 #elif defined(OS_POSIX)
348   base::FileDescriptor pipe(pipe_handle, true);
349   IPC::ChannelHandle channel_handle(channel_name, pipe);
350 #endif  // defined(OS_POSIX)
351 
352   // Connect to the daemon process.
353   daemon_channel_.reset(new IPC::ChannelProxy(
354       channel_handle,
355       IPC::Channel::MODE_CLIENT,
356       this,
357       context_->network_task_runner()));
358 #else  // !defined(REMOTING_MULTI_PROCESS)
359   // Connect to the daemon process.
360   std::string channel_name =
361       cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
362   if (!channel_name.empty()) {
363     daemon_channel_.reset(
364         new IPC::ChannelProxy(channel_name,
365                               IPC::Channel::MODE_CLIENT,
366                               this,
367                               context_->network_task_runner().get()));
368   }
369 
370   if (cmd_line->HasSwitch(kHostConfigSwitchName)) {
371     host_config_path_ = cmd_line->GetSwitchValuePath(kHostConfigSwitchName);
372 
373     // Read config from stdin if necessary.
374     if (host_config_path_ == base::FilePath(kStdinConfigPath)) {
375       char buf[4096];
376       size_t len;
377       while ((len = fread(buf, 1, sizeof(buf), stdin)) > 0) {
378         host_config_.append(buf, len);
379       }
380     }
381   } else {
382     base::FilePath default_config_dir = remoting::GetConfigDir();
383     host_config_path_ = default_config_dir.Append(kDefaultHostConfigFile);
384   }
385 
386   if (host_config_path_ != base::FilePath(kStdinConfigPath) &&
387       !base::PathExists(host_config_path_)) {
388     LOG(ERROR) << "Can't find host config at " << host_config_path_.value();
389     return false;
390   }
391 #endif  // !defined(REMOTING_MULTI_PROCESS)
392 
393   // Ignore certificate requests - the host currently has no client certificate
394   // support, so ignoring certificate requests allows connecting to servers that
395   // request, but don't require, a certificate (optional client authentication).
396   net::URLFetcher::SetIgnoreCertificateRequests(true);
397 
398   ServiceUrls* service_urls = ServiceUrls::GetInstance();
399   bool xmpp_server_valid = net::ParseHostAndPort(
400       service_urls->xmpp_server_address(),
401       &xmpp_server_config_.host, &xmpp_server_config_.port);
402   if (!xmpp_server_valid) {
403     LOG(ERROR) << "Invalid XMPP server: " <<
404         service_urls->xmpp_server_address();
405     return false;
406   }
407   xmpp_server_config_.use_tls = service_urls->xmpp_server_use_tls();
408   directory_bot_jid_ = service_urls->directory_bot_jid();
409 
410   signal_parent_ = cmd_line->HasSwitch(kSignalParentSwitchName);
411 
412   return true;
413 }
414 
OnConfigUpdated(const std::string & serialized_config)415 void HostProcess::OnConfigUpdated(
416     const std::string& serialized_config) {
417   if (!context_->network_task_runner()->BelongsToCurrentThread()) {
418     context_->network_task_runner()->PostTask(FROM_HERE,
419         base::Bind(&HostProcess::OnConfigUpdated, this, serialized_config));
420     return;
421   }
422 
423   // Filter out duplicates.
424   if (serialized_config_ == serialized_config)
425     return;
426 
427   HOST_LOG << "Processing new host configuration.";
428 
429   serialized_config_ = serialized_config;
430   scoped_ptr<JsonHostConfig> config(new JsonHostConfig(base::FilePath()));
431   if (!config->SetSerializedData(serialized_config)) {
432     LOG(ERROR) << "Invalid configuration.";
433     ShutdownHost(kInvalidHostConfigurationExitCode);
434     return;
435   }
436 
437   if (!ApplyConfig(config.Pass())) {
438     LOG(ERROR) << "Failed to apply the configuration.";
439     ShutdownHost(kInvalidHostConfigurationExitCode);
440     return;
441   }
442 
443   if (state_ == HOST_INITIALIZING) {
444     // TODO(sergeyu): Currently OnPolicyUpdate() assumes that host config is
445     // already loaded so PolicyWatcher has to be started here. Separate policy
446     // loading from policy verifications and move |policy_watcher_|
447     // initialization to StartOnNetworkThread().
448     policy_watcher_.reset(
449         policy_hack::PolicyWatcher::Create(context_->file_task_runner()));
450     policy_watcher_->StartWatching(
451         base::Bind(&HostProcess::OnPolicyUpdate, base::Unretained(this)));
452   } else if (state_ == HOST_STARTED) {
453     // TODO(sergeyu): Here we assume that PIN is the only part of the config
454     // that may change while the service is running. Change ApplyConfig() to
455     // detect other changes in the config and restart host if necessary here.
456     CreateAuthenticatorFactory();
457   }
458 }
459 
OnConfigWatcherError()460 void HostProcess::OnConfigWatcherError() {
461   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
462   ShutdownHost(kInvalidHostConfigurationExitCode);
463 }
464 
StartOnNetworkThread()465 void HostProcess::StartOnNetworkThread() {
466   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
467 
468 #if !defined(REMOTING_MULTI_PROCESS)
469   if (host_config_path_ == base::FilePath(kStdinConfigPath)) {
470     // Process config we've read from stdin.
471     OnConfigUpdated(host_config_);
472   } else {
473     // Start watching the host configuration file.
474     config_watcher_.reset(new ConfigFileWatcher(context_->network_task_runner(),
475                                                 context_->file_task_runner(),
476                                                 host_config_path_));
477     config_watcher_->Watch(this);
478   }
479 #endif  // !defined(REMOTING_MULTI_PROCESS)
480 
481 #if defined(OS_POSIX)
482   remoting::RegisterSignalHandler(
483       SIGTERM,
484       base::Bind(&HostProcess::SigTermHandler, base::Unretained(this)));
485 #endif  // defined(OS_POSIX)
486 }
487 
488 #if defined(OS_POSIX)
SigTermHandler(int signal_number)489 void HostProcess::SigTermHandler(int signal_number) {
490   DCHECK(signal_number == SIGTERM);
491   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
492   HOST_LOG << "Caught SIGTERM: Shutting down...";
493   ShutdownHost(kSuccessExitCode);
494 }
495 #endif  // OS_POSIX
496 
CreateAuthenticatorFactory()497 void HostProcess::CreateAuthenticatorFactory() {
498   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
499 
500   if (state_ != HOST_STARTED)
501     return;
502 
503   std::string local_certificate = key_pair_->GenerateCertificate();
504   if (local_certificate.empty()) {
505     LOG(ERROR) << "Failed to generate host certificate.";
506     ShutdownHost(kInitializationFailed);
507     return;
508   }
509 
510   scoped_refptr<protocol::PairingRegistry> pairing_registry = NULL;
511   if (allow_pairing_) {
512     pairing_registry = CreatePairingRegistry(context_->file_task_runner());
513   }
514 
515   scoped_ptr<protocol::AuthenticatorFactory> factory;
516 
517   if (token_url_.is_empty() && token_validation_url_.is_empty()) {
518     factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithSharedSecret(
519         use_service_account_, host_owner_, local_certificate, key_pair_,
520         host_secret_hash_, pairing_registry);
521 
522   } else if (token_url_.is_valid() && token_validation_url_.is_valid()) {
523     scoped_ptr<protocol::ThirdPartyHostAuthenticator::TokenValidatorFactory>
524         token_validator_factory(new TokenValidatorFactoryImpl(
525             token_url_, token_validation_url_, key_pair_,
526             context_->url_request_context_getter()));
527     factory = protocol::Me2MeHostAuthenticatorFactory::CreateWithThirdPartyAuth(
528         use_service_account_, host_owner_, local_certificate, key_pair_,
529         token_validator_factory.Pass());
530 
531   } else {
532     // TODO(rmsousa): If the policy is bad the host should not go online. It
533     // should keep running, but not connected, until the policies are fixed.
534     // Having it show up as online and then reject all clients is misleading.
535     LOG(ERROR) << "One of the third-party token URLs is empty or invalid. "
536                << "Host will reject all clients until policies are corrected. "
537                << "TokenUrl: " << token_url_ << ", "
538                << "TokenValidationUrl: " << token_validation_url_;
539     factory = protocol::Me2MeHostAuthenticatorFactory::CreateRejecting();
540   }
541 
542 #if defined(OS_POSIX)
543   // On Linux and Mac, perform a PAM authorization step after authentication.
544   factory.reset(new PamAuthorizationFactory(factory.Pass()));
545 #endif
546   host_->SetAuthenticatorFactory(factory.Pass());
547 
548   host_->set_pairing_registry(pairing_registry);
549 }
550 
551 // IPC::Listener implementation.
OnMessageReceived(const IPC::Message & message)552 bool HostProcess::OnMessageReceived(const IPC::Message& message) {
553   DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
554 
555 #if defined(REMOTING_MULTI_PROCESS)
556   bool handled = true;
557   IPC_BEGIN_MESSAGE_MAP(HostProcess, message)
558     IPC_MESSAGE_HANDLER(ChromotingDaemonMsg_Crash, OnCrash)
559     IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Configuration,
560                         OnConfigUpdated)
561     IPC_MESSAGE_FORWARD(
562         ChromotingDaemonNetworkMsg_DesktopAttached,
563         desktop_session_connector_,
564         DesktopSessionConnector::OnDesktopSessionAgentAttached)
565     IPC_MESSAGE_FORWARD(ChromotingDaemonNetworkMsg_TerminalDisconnected,
566                         desktop_session_connector_,
567                         DesktopSessionConnector::OnTerminalDisconnected)
568     IPC_MESSAGE_UNHANDLED(handled = false)
569   IPC_END_MESSAGE_MAP()
570 
571   CHECK(handled) << "Received unexpected IPC type: " << message.type();
572   return handled;
573 
574 #else  // !defined(REMOTING_MULTI_PROCESS)
575   return false;
576 #endif  // !defined(REMOTING_MULTI_PROCESS)
577 }
578 
OnChannelError()579 void HostProcess::OnChannelError() {
580   DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
581 
582   // Shutdown the host if the daemon process disconnects the IPC channel.
583   context_->network_task_runner()->PostTask(
584       FROM_HERE,
585       base::Bind(&HostProcess::ShutdownHost, this, kSuccessExitCode));
586 }
587 
StartOnUiThread()588 void HostProcess::StartOnUiThread() {
589   DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
590 
591   if (!InitWithCommandLine(CommandLine::ForCurrentProcess())) {
592     // Shutdown the host if the command line is invalid.
593     context_->network_task_runner()->PostTask(
594         FROM_HERE, base::Bind(&HostProcess::ShutdownHost, this,
595                               kUsageExitCode));
596     return;
597   }
598 
599 #if defined(OS_LINUX)
600   // If an audio pipe is specific on the command-line then initialize
601   // AudioCapturerLinux to capture from it.
602   base::FilePath audio_pipe_name = CommandLine::ForCurrentProcess()->
603       GetSwitchValuePath(kAudioPipeSwitchName);
604   if (!audio_pipe_name.empty()) {
605     remoting::AudioCapturerLinux::InitializePipeReader(
606         context_->audio_task_runner(), audio_pipe_name);
607   }
608 #endif  // defined(OS_LINUX)
609 
610   // Create a desktop environment factory appropriate to the build type &
611   // platform.
612 #if defined(OS_WIN)
613   IpcDesktopEnvironmentFactory* desktop_environment_factory =
614       new IpcDesktopEnvironmentFactory(
615           context_->audio_task_runner(),
616           context_->network_task_runner(),
617           context_->video_capture_task_runner(),
618           context_->network_task_runner(),
619           daemon_channel_.get());
620   desktop_session_connector_ = desktop_environment_factory;
621 #else  // !defined(OS_WIN)
622   DesktopEnvironmentFactory* desktop_environment_factory =
623       new Me2MeDesktopEnvironmentFactory(
624           context_->network_task_runner(),
625           context_->input_task_runner(),
626           context_->ui_task_runner());
627 #endif  // !defined(OS_WIN)
628 
629   desktop_environment_factory_.reset(desktop_environment_factory);
630 
631   context_->network_task_runner()->PostTask(
632       FROM_HERE,
633       base::Bind(&HostProcess::StartOnNetworkThread, this));
634 }
635 
ShutdownOnUiThread()636 void HostProcess::ShutdownOnUiThread() {
637   DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
638 
639   // Tear down resources that need to be torn down on the UI thread.
640   network_change_notifier_.reset();
641   daemon_channel_.reset();
642   desktop_environment_factory_.reset();
643 
644   // It is now safe for the HostProcess to be deleted.
645   self_ = NULL;
646 
647 #if defined(OS_LINUX)
648   // Cause the global AudioPipeReader to be freed, otherwise the audio
649   // thread will remain in-use and prevent the process from exiting.
650   // TODO(wez): DesktopEnvironmentFactory should own the pipe reader.
651   // See crbug.com/161373 and crbug.com/104544.
652   AudioCapturerLinux::InitializePipeReader(NULL, base::FilePath());
653 #endif
654 }
655 
656 // Overridden from HeartbeatSender::Listener
OnUnknownHostIdError()657 void HostProcess::OnUnknownHostIdError() {
658   LOG(ERROR) << "Host ID not found.";
659   ShutdownHost(kInvalidHostIdExitCode);
660 }
661 
OnHeartbeatSuccessful()662 void HostProcess::OnHeartbeatSuccessful() {
663   HOST_LOG << "Host ready to receive connections.";
664 #if defined(OS_POSIX)
665   if (signal_parent_) {
666     kill(getppid(), SIGUSR1);
667     signal_parent_ = false;
668   }
669 #endif
670 }
671 
OnHostDeleted()672 void HostProcess::OnHostDeleted() {
673   LOG(ERROR) << "Host was deleted from the directory.";
674   ShutdownHost(kInvalidHostIdExitCode);
675 }
676 
677 // Applies the host config, returning true if successful.
ApplyConfig(scoped_ptr<JsonHostConfig> config)678 bool HostProcess::ApplyConfig(scoped_ptr<JsonHostConfig> config) {
679   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
680 
681   if (!config->GetString(kHostIdConfigPath, &host_id_)) {
682     LOG(ERROR) << "host_id is not defined in the config.";
683     return false;
684   }
685 
686   std::string key_base64;
687   if (!config->GetString(kPrivateKeyConfigPath, &key_base64)) {
688     LOG(ERROR) << "Private key couldn't be read from the config file.";
689     return false;
690   }
691 
692   key_pair_ = RsaKeyPair::FromString(key_base64);
693   if (!key_pair_.get()) {
694     LOG(ERROR) << "Invalid private key in the config file.";
695     return false;
696   }
697 
698   std::string host_secret_hash_string;
699   if (!config->GetString(kHostSecretHashConfigPath,
700                          &host_secret_hash_string)) {
701     host_secret_hash_string = "plain:";
702   }
703 
704   if (!host_secret_hash_.Parse(host_secret_hash_string)) {
705     LOG(ERROR) << "Invalid host_secret_hash.";
706     return false;
707   }
708 
709   // Use an XMPP connection to the Talk network for session signalling.
710   if (!config->GetString(kXmppLoginConfigPath, &xmpp_server_config_.username) ||
711       !(config->GetString(kXmppAuthTokenConfigPath,
712                           &xmpp_server_config_.auth_token) ||
713         config->GetString(kOAuthRefreshTokenConfigPath,
714                           &oauth_refresh_token_))) {
715     LOG(ERROR) << "XMPP credentials are not defined in the config.";
716     return false;
717   }
718 
719   if (!oauth_refresh_token_.empty()) {
720     // SignalingConnector is responsible for getting OAuth token.
721     xmpp_server_config_.auth_token = "";
722     xmpp_server_config_.auth_service = "oauth2";
723   } else if (!config->GetString(kXmppAuthServiceConfigPath,
724                                 &xmpp_server_config_.auth_service)) {
725     // For the me2me host, we default to ClientLogin token for chromiumsync
726     // because earlier versions of the host had no HTTP stack with which to
727     // request an OAuth2 access token.
728     xmpp_server_config_.auth_service = kChromotingTokenDefaultServiceName;
729   }
730 
731   if (config->GetString(kHostOwnerConfigPath, &host_owner_)) {
732     // Service account configs have a host_owner, different from the xmpp_login.
733     use_service_account_ = true;
734   } else {
735     // User credential configs only have an xmpp_login, which is also the owner.
736     host_owner_ = xmpp_server_config_.username;
737     use_service_account_ = false;
738   }
739   return true;
740 }
741 
OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies)742 void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
743   // TODO(rmsousa): Consolidate all On*PolicyUpdate methods into this one.
744   // TODO(sergeyu): Currently polices are verified only when they are loaded.
745   // Separate policy loading from policy verifications - this will allow to
746   // check policies again later, e.g. when host config changes.
747 
748   if (!context_->network_task_runner()->BelongsToCurrentThread()) {
749     context_->network_task_runner()->PostTask(FROM_HERE, base::Bind(
750         &HostProcess::OnPolicyUpdate, this, base::Passed(&policies)));
751     return;
752   }
753 
754   bool restart_required = false;
755   bool bool_value;
756   std::string string_value;
757   if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
758                           &string_value)) {
759     restart_required |= OnHostDomainPolicyUpdate(string_value);
760   }
761   bool curtain_required = false;
762   if (policies->GetBoolean(
763           policy_hack::PolicyWatcher::kHostRequireCurtainPolicyName,
764           &curtain_required)) {
765     OnCurtainPolicyUpdate(curtain_required);
766   }
767   if (policies->GetBoolean(
768       policy_hack::PolicyWatcher::kHostMatchUsernamePolicyName,
769       &bool_value)) {
770     restart_required |= OnUsernamePolicyUpdate(curtain_required, bool_value);
771   }
772   if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
773                            &bool_value)) {
774     restart_required |= OnNatPolicyUpdate(bool_value);
775   }
776   if (policies->GetString(
777           policy_hack::PolicyWatcher::kHostTalkGadgetPrefixPolicyName,
778           &string_value)) {
779     restart_required |= OnHostTalkGadgetPrefixPolicyUpdate(string_value);
780   }
781   std::string token_url_string, token_validation_url_string;
782   if (policies->GetString(
783           policy_hack::PolicyWatcher::kHostTokenUrlPolicyName,
784           &token_url_string) &&
785       policies->GetString(
786           policy_hack::PolicyWatcher::kHostTokenValidationUrlPolicyName,
787           &token_validation_url_string)) {
788     restart_required |= OnHostTokenUrlPolicyUpdate(
789         GURL(token_url_string), GURL(token_validation_url_string));
790   }
791   if (policies->GetBoolean(
792           policy_hack::PolicyWatcher::kHostAllowClientPairing,
793           &bool_value)) {
794     restart_required |= OnPairingPolicyUpdate(bool_value);
795   }
796 
797   if (state_ == HOST_INITIALIZING) {
798     StartHost();
799   } else if (state_ == HOST_STARTED && restart_required) {
800     RestartHost();
801   }
802 }
803 
OnHostDomainPolicyUpdate(const std::string & host_domain)804 bool HostProcess::OnHostDomainPolicyUpdate(const std::string& host_domain) {
805   // Returns true if the host has to be restarted after this policy update.
806   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
807 
808   HOST_LOG << "Policy sets host domain: " << host_domain;
809 
810   if (!host_domain.empty() &&
811       !EndsWith(host_owner_, std::string("@") + host_domain, false)) {
812     ShutdownHost(kInvalidHostDomainExitCode);
813   }
814   return false;
815 }
816 
OnUsernamePolicyUpdate(bool curtain_required,bool host_username_match_required)817 bool HostProcess::OnUsernamePolicyUpdate(bool curtain_required,
818                                          bool host_username_match_required) {
819   // Returns false: never restart the host after this policy update.
820   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
821 
822   if (host_username_match_required) {
823     HOST_LOG << "Policy requires host username match.";
824     std::string username = GetUsername();
825     bool shutdown = username.empty() ||
826         !StartsWithASCII(host_owner_, username + std::string("@"),
827                          false);
828 
829 #if defined(OS_MACOSX)
830     // On Mac, we run as root at the login screen, so the username won't match.
831     // However, there's no need to enforce the policy at the login screen, as
832     // the client will have to reconnect if a login occurs.
833     if (shutdown && getuid() == 0) {
834       shutdown = false;
835     }
836 #endif
837 
838     // Curtain-mode on Windows presents the standard OS login prompt to the user
839     // for each connection, removing the need for an explicit user-name matching
840     // check.
841 #if defined(OS_WIN) && defined(REMOTING_RDP_SESSION)
842     if (curtain_required)
843       return false;
844 #endif  // defined(OS_WIN) && defined(REMOTING_RDP_SESSION)
845 
846     // Shutdown the host if the username does not match.
847     if (shutdown) {
848       LOG(ERROR) << "The host username does not match.";
849       ShutdownHost(kUsernameMismatchExitCode);
850     }
851   } else {
852     HOST_LOG << "Policy does not require host username match.";
853   }
854 
855   return false;
856 }
857 
OnNatPolicyUpdate(bool nat_traversal_enabled)858 bool HostProcess::OnNatPolicyUpdate(bool nat_traversal_enabled) {
859   // Returns true if the host has to be restarted after this policy update.
860   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
861 
862   if (allow_nat_traversal_ != nat_traversal_enabled) {
863     if (nat_traversal_enabled)
864       HOST_LOG << "Policy enables NAT traversal.";
865     else
866       HOST_LOG << "Policy disables NAT traversal.";
867     allow_nat_traversal_ = nat_traversal_enabled;
868     return true;
869   }
870   return false;
871 }
872 
OnCurtainPolicyUpdate(bool curtain_required)873 void HostProcess::OnCurtainPolicyUpdate(bool curtain_required) {
874   // Returns true if the host has to be restarted after this policy update.
875   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
876 
877 #if defined(OS_MACOSX)
878   if (curtain_required) {
879     // When curtain mode is in effect on Mac, the host process runs in the
880     // user's switched-out session, but launchd will also run an instance at
881     // the console login screen.  Even if no user is currently logged-on, we
882     // can't support remote-access to the login screen because the current host
883     // process model disconnects the client during login, which would leave
884     // the logged in session un-curtained on the console until they reconnect.
885     //
886     // TODO(jamiewalch): Fix this once we have implemented the multi-process
887     // daemon architecture (crbug.com/134894)
888     if (getuid() == 0) {
889       LOG(ERROR) << "Running the host in the console login session is yet not "
890                     "supported.";
891       ShutdownHost(kLoginScreenNotSupportedExitCode);
892       return;
893     }
894   }
895 #endif
896 
897   if (curtain_required_ != curtain_required) {
898     if (curtain_required)
899       HOST_LOG << "Policy requires curtain-mode.";
900     else
901       HOST_LOG << "Policy does not require curtain-mode.";
902     curtain_required_ = curtain_required;
903     if (host_)
904       host_->SetEnableCurtaining(curtain_required_);
905   }
906 }
907 
OnHostTalkGadgetPrefixPolicyUpdate(const std::string & talkgadget_prefix)908 bool HostProcess::OnHostTalkGadgetPrefixPolicyUpdate(
909     const std::string& talkgadget_prefix) {
910   // Returns true if the host has to be restarted after this policy update.
911   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
912 
913   if (talkgadget_prefix != talkgadget_prefix_) {
914     HOST_LOG << "Policy sets talkgadget prefix: " << talkgadget_prefix;
915     talkgadget_prefix_ = talkgadget_prefix;
916     return true;
917   }
918   return false;
919 }
920 
OnHostTokenUrlPolicyUpdate(const GURL & token_url,const GURL & token_validation_url)921 bool HostProcess::OnHostTokenUrlPolicyUpdate(
922     const GURL& token_url,
923     const GURL& token_validation_url) {
924   // Returns true if the host has to be restarted after this policy update.
925   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
926 
927   if (token_url_ != token_url ||
928       token_validation_url_ != token_validation_url) {
929     HOST_LOG << "Policy sets third-party token URLs: "
930               << "TokenUrl: " << token_url << ", "
931               << "TokenValidationUrl: " << token_validation_url;
932 
933     token_url_ = token_url;
934     token_validation_url_ = token_validation_url;
935     return true;
936   }
937 
938   return false;
939 }
940 
OnPairingPolicyUpdate(bool allow_pairing)941 bool HostProcess::OnPairingPolicyUpdate(bool allow_pairing) {
942   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
943 
944   if (allow_pairing_ == allow_pairing)
945     return false;
946 
947   if (allow_pairing)
948     HOST_LOG << "Policy enables client pairing.";
949   else
950     HOST_LOG << "Policy disables client pairing.";
951   allow_pairing_ = allow_pairing;
952   return true;
953 }
954 
StartHost()955 void HostProcess::StartHost() {
956   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
957   DCHECK(!host_);
958   DCHECK(!signal_strategy_.get());
959   DCHECK(state_ == HOST_INITIALIZING || state_ == HOST_STOPPING_TO_RESTART ||
960          state_ == HOST_STOPPED) << state_;
961   state_ = HOST_STARTED;
962 
963   signal_strategy_.reset(
964       new XmppSignalStrategy(net::ClientSocketFactory::GetDefaultFactory(),
965                              context_->url_request_context_getter(),
966                              xmpp_server_config_));
967 
968   scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker(
969       new DnsBlackholeChecker(context_->url_request_context_getter(),
970                               talkgadget_prefix_));
971 
972   // Create a NetworkChangeNotifier for use by the signaling connector.
973   network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
974 
975   signaling_connector_.reset(new SignalingConnector(
976       signal_strategy_.get(),
977       context_->url_request_context_getter(),
978       dns_blackhole_checker.Pass(),
979       base::Bind(&HostProcess::OnAuthFailed, this)));
980 
981   if (!oauth_refresh_token_.empty()) {
982     scoped_ptr<SignalingConnector::OAuthCredentials> oauth_credentials(
983         new SignalingConnector::OAuthCredentials(
984             xmpp_server_config_.username, oauth_refresh_token_,
985             use_service_account_));
986     signaling_connector_->EnableOAuth(oauth_credentials.Pass());
987   }
988 
989   NetworkSettings network_settings(
990       allow_nat_traversal_ ?
991       NetworkSettings::NAT_TRAVERSAL_ENABLED :
992       NetworkSettings::NAT_TRAVERSAL_DISABLED);
993   if (!allow_nat_traversal_) {
994     network_settings.min_port = NetworkSettings::kDefaultMinPort;
995     network_settings.max_port = NetworkSettings::kDefaultMaxPort;
996   }
997 
998   host_.reset(new ChromotingHost(
999       signal_strategy_.get(),
1000       desktop_environment_factory_.get(),
1001       CreateHostSessionManager(signal_strategy_.get(), network_settings,
1002                                context_->url_request_context_getter()),
1003       context_->audio_task_runner(),
1004       context_->input_task_runner(),
1005       context_->video_capture_task_runner(),
1006       context_->video_encode_task_runner(),
1007       context_->network_task_runner(),
1008       context_->ui_task_runner()));
1009 
1010   // TODO(simonmorris): Get the maximum session duration from a policy.
1011 #if defined(OS_LINUX)
1012   host_->SetMaximumSessionDuration(base::TimeDelta::FromHours(20));
1013 #endif
1014 
1015   heartbeat_sender_.reset(new HeartbeatSender(
1016       this, host_id_, signal_strategy_.get(), key_pair_,
1017       directory_bot_jid_));
1018 
1019   host_status_sender_.reset(new HostStatusSender(
1020       host_id_, signal_strategy_.get(), key_pair_, directory_bot_jid_));
1021 
1022   host_change_notification_listener_.reset(new HostChangeNotificationListener(
1023       this, host_id_, signal_strategy_.get(), directory_bot_jid_));
1024 
1025   log_to_server_.reset(
1026       new LogToServer(host_->AsWeakPtr(), ServerLogEntry::ME2ME,
1027                       signal_strategy_.get(), directory_bot_jid_));
1028 
1029   // Set up repoting the host status notifications.
1030 #if defined(REMOTING_MULTI_PROCESS)
1031   host_event_logger_.reset(
1032       new IpcHostEventLogger(host_->AsWeakPtr(), daemon_channel_.get()));
1033 #else  // !defined(REMOTING_MULTI_PROCESS)
1034   host_event_logger_ =
1035       HostEventLogger::Create(host_->AsWeakPtr(), kApplicationName);
1036 #endif  // !defined(REMOTING_MULTI_PROCESS)
1037 
1038   host_->SetEnableCurtaining(curtain_required_);
1039   host_->Start(host_owner_);
1040 
1041   CreateAuthenticatorFactory();
1042 }
1043 
OnAuthFailed()1044 void HostProcess::OnAuthFailed() {
1045   ShutdownHost(kInvalidOauthCredentialsExitCode);
1046 }
1047 
RestartHost()1048 void HostProcess::RestartHost() {
1049   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1050   DCHECK_EQ(state_, HOST_STARTED);
1051 
1052   state_ = HOST_STOPPING_TO_RESTART;
1053   ShutdownOnNetworkThread();
1054 }
1055 
ShutdownHost(HostExitCodes exit_code)1056 void HostProcess::ShutdownHost(HostExitCodes exit_code) {
1057   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1058 
1059   *exit_code_out_ = exit_code;
1060 
1061   switch (state_) {
1062     case HOST_INITIALIZING:
1063       state_ = HOST_STOPPING;
1064       ShutdownOnNetworkThread();
1065       break;
1066 
1067     case HOST_STARTED:
1068       state_ = HOST_STOPPING;
1069       host_status_sender_->SendOfflineStatus(exit_code);
1070       ScheduleHostShutdown();
1071       break;
1072 
1073     case HOST_STOPPING_TO_RESTART:
1074       state_ = HOST_STOPPING;
1075       break;
1076 
1077     case HOST_STOPPING:
1078     case HOST_STOPPED:
1079       // Host is already stopped or being stopped. No action is required.
1080       break;
1081   }
1082 }
1083 
1084 // TODO(weitaosu): shut down the host once we get an ACK for the offline status
1085 //                  XMPP message.
ScheduleHostShutdown()1086 void HostProcess::ScheduleHostShutdown() {
1087   // Delay the shutdown by 2 second to allow SendOfflineStatus to complete.
1088   context_->network_task_runner()->PostDelayedTask(
1089       FROM_HERE,
1090       base::Bind(&HostProcess::ShutdownOnNetworkThread, base::Unretained(this)),
1091       base::TimeDelta::FromSeconds(2));
1092 }
1093 
ShutdownOnNetworkThread()1094 void HostProcess::ShutdownOnNetworkThread() {
1095   DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
1096 
1097   host_.reset();
1098   host_event_logger_.reset();
1099   log_to_server_.reset();
1100   heartbeat_sender_.reset();
1101   host_status_sender_.reset();
1102   host_change_notification_listener_.reset();
1103   signaling_connector_.reset();
1104   signal_strategy_.reset();
1105   network_change_notifier_.reset();
1106 
1107   if (state_ == HOST_STOPPING_TO_RESTART) {
1108     StartHost();
1109   } else if (state_ == HOST_STOPPING) {
1110     state_ = HOST_STOPPED;
1111 
1112     if (policy_watcher_.get()) {
1113       base::WaitableEvent done_event(true, false);
1114       policy_watcher_->StopWatching(&done_event);
1115       done_event.Wait();
1116       policy_watcher_.reset();
1117     }
1118 
1119     config_watcher_.reset();
1120 
1121     // Complete the rest of shutdown on the main thread.
1122     context_->ui_task_runner()->PostTask(
1123         FROM_HERE,
1124         base::Bind(&HostProcess::ShutdownOnUiThread, this));
1125   } else {
1126     // This method is only called in STOPPING_TO_RESTART and STOPPING states.
1127     NOTREACHED();
1128   }
1129 }
1130 
OnCrash(const std::string & function_name,const std::string & file_name,const int & line_number)1131 void HostProcess::OnCrash(const std::string& function_name,
1132                           const std::string& file_name,
1133                           const int& line_number) {
1134   char message[1024];
1135   base::snprintf(message, sizeof(message),
1136                  "Requested by %s at %s, line %d.",
1137                  function_name.c_str(), file_name.c_str(), line_number);
1138   base::debug::Alias(message);
1139 
1140   // The daemon requested us to crash the process.
1141   CHECK(false) << message;
1142 }
1143 
HostProcessMain()1144 int HostProcessMain() {
1145 #if defined(TOOLKIT_GTK)
1146   // Required for any calls into GTK functions, such as the Disconnect and
1147   // Continue windows, though these should not be used for the Me2Me case
1148   // (crbug.com/104377).
1149   gfx::GtkInitFromCommandLine(*CommandLine::ForCurrentProcess());
1150 #endif  // TOOLKIT_GTK
1151 
1152   // Enable support for SSL server sockets, which must be done while still
1153   // single-threaded.
1154   net::EnableSSLServerSockets();
1155 
1156   // Ensures runtime specific CPU features are initialized.
1157   media::InitializeCPUSpecificMediaFeatures();
1158 
1159   // Create the main message loop and start helper threads.
1160   base::MessageLoop message_loop(base::MessageLoop::TYPE_UI);
1161   scoped_ptr<ChromotingHostContext> context =
1162       ChromotingHostContext::Create(new AutoThreadTaskRunner(
1163           message_loop.message_loop_proxy(), base::MessageLoop::QuitClosure()));
1164   if (!context)
1165     return kInitializationFailed;
1166 
1167   // Create & start the HostProcess using these threads.
1168   // TODO(wez): The HostProcess holds a reference to itself until Shutdown().
1169   // Remove this hack as part of the multi-process refactoring.
1170   int exit_code = kSuccessExitCode;
1171   new HostProcess(context.Pass(), &exit_code);
1172 
1173   // Run the main (also UI) message loop until the host no longer needs it.
1174   message_loop.Run();
1175 
1176   return exit_code;
1177 }
1178 
1179 }  // namespace remoting
1180 
1181 #if !defined(OS_WIN)
main(int argc,char ** argv)1182 int main(int argc, char** argv) {
1183   return remoting::HostMain(argc, argv);
1184 }
1185 #endif  // !defined(OS_WIN)
1186