Revert "Merged all Chromoting Host code into remoting_core.dll (Windows)."
[chromium-blink-merge.git] / remoting / host / remoting_me2me_host.cc
blob3d692d79a1e25a6c983e2cee1c41a6c4735543a9
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/file_path.h"
14 #include "base/file_util.h"
15 #include "base/memory/scoped_ptr.h"
16 #include "base/message_loop.h"
17 #include "base/scoped_native_library.h"
18 #include "base/single_thread_task_runner.h"
19 #include "base/string_number_conversions.h"
20 #include "base/string_util.h"
21 #include "base/stringize_macros.h"
22 #include "base/synchronization/waitable_event.h"
23 #include "base/threading/thread.h"
24 #include "base/utf_string_conversions.h"
25 #include "base/win/windows_version.h"
26 #include "build/build_config.h"
27 #include "crypto/nss_util.h"
28 #include "ipc/ipc_channel.h"
29 #include "ipc/ipc_channel_proxy.h"
30 #include "ipc/ipc_listener.h"
31 #include "media/video/capture/screen/screen_capturer.h"
32 #include "net/base/network_change_notifier.h"
33 #include "net/socket/ssl_server_socket.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/host/basic_desktop_environment.h"
38 #include "remoting/host/branding.h"
39 #include "remoting/host/chromoting_host.h"
40 #include "remoting/host/chromoting_host_context.h"
41 #include "remoting/host/chromoting_messages.h"
42 #include "remoting/host/config_file_watcher.h"
43 #include "remoting/host/curtain_mode.h"
44 #include "remoting/host/curtaining_host_observer.h"
45 #include "remoting/host/desktop_environment.h"
46 #include "remoting/host/desktop_resizer.h"
47 #include "remoting/host/desktop_session_connector.h"
48 #include "remoting/host/dns_blackhole_checker.h"
49 #include "remoting/host/event_executor.h"
50 #include "remoting/host/heartbeat_sender.h"
51 #include "remoting/host/host_config.h"
52 #include "remoting/host/host_event_logger.h"
53 #include "remoting/host/host_exit_codes.h"
54 #include "remoting/host/host_user_interface.h"
55 #include "remoting/host/ipc_constants.h"
56 #include "remoting/host/ipc_desktop_environment.h"
57 #include "remoting/host/json_host_config.h"
58 #include "remoting/host/log_to_server.h"
59 #include "remoting/host/logging.h"
60 #include "remoting/host/network_settings.h"
61 #include "remoting/host/policy_hack/policy_watcher.h"
62 #include "remoting/host/resizing_host_observer.h"
63 #include "remoting/host/session_manager_factory.h"
64 #include "remoting/host/signaling_connector.h"
65 #include "remoting/host/ui_strings.h"
66 #include "remoting/host/usage_stats_consent.h"
67 #include "remoting/jingle_glue/xmpp_signal_strategy.h"
68 #include "remoting/protocol/me2me_host_authenticator_factory.h"
70 #if defined(OS_POSIX)
71 #include <pwd.h>
72 #include <signal.h>
73 #include "base/file_descriptor_posix.h"
74 #include "remoting/host/pam_authorization_factory_posix.h"
75 #include "remoting/host/posix/signal_handler.h"
76 #endif // defined(OS_POSIX)
78 #if defined(OS_MACOSX)
79 #include "base/mac/scoped_cftyperef.h"
80 #include "base/mac/scoped_nsautorelease_pool.h"
81 #endif // defined(OS_MACOSX)
83 #if defined(OS_LINUX)
84 #include "remoting/host/audio_capturer_linux.h"
85 #endif // defined(OS_LINUX)
87 #if defined(OS_WIN)
88 #include <commctrl.h>
89 #include "base/win/scoped_handle.h"
90 #include "remoting/host/win/session_desktop_environment.h"
91 #endif // defined(OS_WIN)
93 #if defined(TOOLKIT_GTK)
94 #include "ui/gfx/gtk_util.h"
95 #endif // defined(TOOLKIT_GTK)
97 namespace {
99 // This is used for tagging system event logs.
100 const char kApplicationName[] = "chromoting";
102 // The command line switch used to get version of the daemon.
103 const char kVersionSwitchName[] = "version";
105 // The command line switch used to pass name of the pipe to capture audio on
106 // linux.
107 const char kAudioPipeSwitchName[] = "audio-pipe-name";
109 void QuitMessageLoop(MessageLoop* message_loop) {
110 message_loop->PostTask(FROM_HERE, MessageLoop::QuitClosure());
113 // Returns true if GetUsername() is implemented on this platform.
114 bool CanGetUsername() {
115 #if defined(OS_POSIX)
116 return true;
117 #else // !defined(OS_POSIX)
118 return false;
119 #endif // defined(OS_POSIX)
120 } // namespace
122 // Returns the username associated with this process, or the empty string on
123 // error.
124 std::string GetUsername() {
125 #if defined(OS_POSIX)
126 long buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
127 if (buf_size <= 0)
128 return "";
129 scoped_array<char> buf(new char[buf_size]);
130 struct passwd passwd;
131 struct passwd* passwd_result = NULL;
132 getpwuid_r(getuid(), &passwd, buf.get(), buf_size, &passwd_result);
133 if (!passwd_result)
134 return "";
135 return std::string(passwd_result->pw_name);
136 #else // !defined(OS_POSIX)
137 NOTREACHED();
138 return "";
139 #endif // defined(OS_POSIX)
142 } // namespace
144 namespace remoting {
146 class HostProcess
147 : public ConfigFileWatcher::Delegate,
148 public HeartbeatSender::Listener,
149 public IPC::Listener,
150 public base::RefCountedThreadSafe<HostProcess> {
151 public:
152 HostProcess(scoped_ptr<ChromotingHostContext> context,
153 int* exit_code_out);
155 // ConfigFileWatcher::Delegate interface.
156 virtual void OnConfigUpdated(const std::string& serialized_config) OVERRIDE;
157 virtual void OnConfigWatcherError() OVERRIDE;
159 // IPC::Listener implementation.
160 virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
161 virtual void OnChannelError() OVERRIDE;
163 // HeartbeatSender::Listener overrides.
164 virtual void OnUnknownHostIdError() OVERRIDE;
166 private:
167 enum HostState {
168 // Host process has just been started. Waiting for config and policies to be
169 // read from the disk.
170 HOST_INITIALIZING,
172 // Host is started and running.
173 HOST_STARTED,
175 // Host is being stopped and will need to be started again.
176 HOST_STOPPING_TO_RESTART,
178 // Host is being stopped.
179 HOST_STOPPING,
181 // Host has been stopped.
182 HOST_STOPPED,
184 // Allowed state transitions:
185 // INITIALIZING->STARTED
186 // INITIALIZING->STOPPED
187 // STARTED->STOPPING_TO_RESTART
188 // STARTED->STOPPING
189 // STOPPING_TO_RESTART->STARTED
190 // STOPPING_TO_RESTART->STOPPING
191 // STOPPING->STOPPED
192 // STOPPED->STARTED
194 // |host_| must be NULL in INITIALIZING and STOPPED states and not-NULL in
195 // all other states.
198 friend class base::RefCountedThreadSafe<HostProcess>;
199 virtual ~HostProcess();
201 void StartOnNetworkThread();
203 #if defined(OS_POSIX)
204 // Callback passed to RegisterSignalHandler() to handle SIGTERM events.
205 void SigTermHandler(int signal_number);
206 #endif
208 // Called to initialize resources on the UI thread.
209 void StartOnUiThread();
211 // Initializes IPC control channel and config file path from |cmd_line|.
212 // Called on the UI thread.
213 bool InitWithCommandLine(const CommandLine* cmd_line);
215 // Called on the UI thread to start monitoring the configuration file.
216 void StartWatchingConfigChanges();
218 // Called on the network thread to set the host's Authenticator factory.
219 void CreateAuthenticatorFactory();
221 // Asks the daemon to inject Secure Attention Sequence to the console.
222 void SendSasToConsole();
224 // Tear down resources that run on the UI thread.
225 void ShutdownOnUiThread();
227 // Applies the host config, returning true if successful.
228 bool ApplyConfig(scoped_ptr<JsonHostConfig> config);
230 void OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies);
231 bool OnHostDomainPolicyUpdate(const std::string& host_domain);
232 bool OnUsernamePolicyUpdate(bool username_match_required);
233 bool OnNatPolicyUpdate(bool nat_traversal_enabled);
234 bool OnCurtainPolicyUpdate(bool curtain_required);
235 bool OnHostTalkGadgetPrefixPolicyUpdate(const std::string& talkgadget_prefix);
237 void StartHost();
239 void OnAuthFailed();
241 void OnCurtainModeFailed();
243 void OnRemoteSessionSwitchedToConsole();
245 // Invoked when the user uses the Disconnect windows to terminate
246 // the sessions, or when the local session is activated in curtain mode.
247 void OnDisconnectRequested();
249 void RestartHost();
251 // Stops the host and shuts down the process with the specified |exit_code|.
252 void ShutdownHost(int exit_code);
254 void ShutdownOnNetworkThread();
256 // Crashes the process in response to a daemon's request. The daemon passes
257 // the location of the code that detected the fatal error resulted in this
258 // request.
259 void OnCrash(const std::string& function_name,
260 const std::string& file_name,
261 const int& line_number);
263 scoped_ptr<ChromotingHostContext> context_;
265 // Created on the UI thread but used from the network thread.
266 scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_;
268 // Accessed on the UI thread.
269 scoped_ptr<IPC::ChannelProxy> daemon_channel_;
270 FilePath host_config_path_;
271 scoped_ptr<DesktopEnvironmentFactory> desktop_environment_factory_;
273 // Accessed on the network thread.
274 HostState state_;
276 scoped_ptr<ConfigFileWatcher> config_watcher_;
278 std::string host_id_;
279 protocol::SharedSecretHash host_secret_hash_;
280 HostKeyPair key_pair_;
281 std::string oauth_refresh_token_;
282 std::string serialized_config_;
283 std::string xmpp_login_;
284 std::string xmpp_auth_token_;
285 std::string xmpp_auth_service_;
287 scoped_ptr<policy_hack::PolicyWatcher> policy_watcher_;
288 bool allow_nat_traversal_;
289 std::string talkgadget_prefix_;
291 scoped_ptr<CurtainMode> curtain_;
292 scoped_ptr<CurtainingHostObserver> curtaining_host_observer_;
293 bool curtain_required_;
295 scoped_ptr<DesktopResizer> desktop_resizer_;
296 scoped_ptr<ResizingHostObserver> resizing_host_observer_;
297 scoped_ptr<XmppSignalStrategy> signal_strategy_;
298 scoped_ptr<SignalingConnector> signaling_connector_;
299 scoped_ptr<HeartbeatSender> heartbeat_sender_;
300 scoped_ptr<LogToServer> log_to_server_;
301 scoped_ptr<HostEventLogger> host_event_logger_;
303 // Created on the UI thread and used on the network thread.
304 scoped_ptr<HostUserInterface> host_user_interface_;
306 scoped_refptr<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_;
318 HostProcess::HostProcess(scoped_ptr<ChromotingHostContext> context,
319 int* exit_code_out)
320 : context_(context.Pass()),
321 state_(HOST_INITIALIZING),
322 allow_nat_traversal_(true),
323 curtain_required_(false),
324 desktop_resizer_(DesktopResizer::Create()),
325 #if defined(REMOTING_MULTI_PROCESS)
326 desktop_session_connector_(NULL),
327 #endif // defined(REMOTING_MULTI_PROCESS)
328 ALLOW_THIS_IN_INITIALIZER_LIST(self_(this)),
329 exit_code_out_(exit_code_out) {
330 // Create a NetworkChangeNotifier for use by the signalling connector.
331 network_change_notifier_.reset(net::NetworkChangeNotifier::Create());
333 // Create the platform-specific curtain-mode implementation.
334 // TODO(wez): Create this on the network thread?
335 curtain_ = CurtainMode::Create(
336 base::Bind(&HostProcess::OnRemoteSessionSwitchedToConsole,
337 base::Unretained(this)),
338 base::Bind(&HostProcess::OnCurtainModeFailed,
339 base::Unretained(this)));
341 StartOnUiThread();
344 HostProcess::~HostProcess() {
345 // Verify that UI components have been torn down.
346 DCHECK(!config_watcher_);
347 DCHECK(!daemon_channel_);
348 DCHECK(!desktop_environment_factory_);
349 DCHECK(!host_user_interface_);
351 // We might be getting deleted on one of the threads the |host_context| owns,
352 // so we need to post it back to the caller thread to safely join & delete the
353 // threads it contains. This will go away when we move to AutoThread.
354 // |context_release()| will null |context_| before the method is invoked, so
355 // we need to pull out the task-runner on which to call DeleteSoon first.
356 scoped_refptr<base::SingleThreadTaskRunner> task_runner =
357 context_->ui_task_runner();
358 task_runner->DeleteSoon(FROM_HERE, context_.release());
361 bool HostProcess::InitWithCommandLine(const CommandLine* cmd_line) {
362 #if defined(REMOTING_MULTI_PROCESS)
363 // Parse the handle value and convert it to a handle/file descriptor.
364 std::string channel_name =
365 cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
367 int pipe_handle = 0;
368 if (channel_name.empty() ||
369 !base::StringToInt(channel_name, &pipe_handle)) {
370 LOG(ERROR) << "Invalid '" << kDaemonPipeSwitchName
371 << "' value: " << channel_name;
372 return false;
375 #if defined(OS_WIN)
376 base::win::ScopedHandle pipe(reinterpret_cast<HANDLE>(pipe_handle));
377 IPC::ChannelHandle channel_handle(pipe);
378 #elif defined(OS_POSIX)
379 base::FileDescriptor pipe(pipe_handle, true);
380 IPC::ChannelHandle channel_handle(channel_name, pipe);
381 #endif // defined(OS_POSIX)
383 // Connect to the daemon process.
384 daemon_channel_.reset(new IPC::ChannelProxy(
385 channel_handle,
386 IPC::Channel::MODE_CLIENT,
387 this,
388 context_->network_task_runner()));
389 #else // !defined(REMOTING_MULTI_PROCESS)
390 // Connect to the daemon process.
391 std::string channel_name =
392 cmd_line->GetSwitchValueASCII(kDaemonPipeSwitchName);
393 if (!channel_name.empty()) {
394 daemon_channel_.reset(new IPC::ChannelProxy(
395 channel_name, IPC::Channel::MODE_CLIENT, this,
396 context_->network_task_runner()));
399 FilePath default_config_dir = remoting::GetConfigDir();
400 host_config_path_ = default_config_dir.Append(kDefaultHostConfigFile);
401 if (cmd_line->HasSwitch(kHostConfigSwitchName)) {
402 host_config_path_ = cmd_line->GetSwitchValuePath(kHostConfigSwitchName);
404 #endif // !defined(REMOTING_MULTI_PROCESS)
406 return true;
409 void HostProcess::OnConfigUpdated(
410 const std::string& serialized_config) {
411 if (!context_->network_task_runner()->BelongsToCurrentThread()) {
412 context_->network_task_runner()->PostTask(FROM_HERE,
413 base::Bind(&HostProcess::OnConfigUpdated, this, serialized_config));
414 return;
417 // Filter out duplicates.
418 if (serialized_config_ == serialized_config)
419 return;
421 LOG(INFO) << "Processing new host configuration.";
423 serialized_config_ = serialized_config;
424 scoped_ptr<JsonHostConfig> config(new JsonHostConfig(FilePath()));
425 if (!config->SetSerializedData(serialized_config)) {
426 LOG(ERROR) << "Invalid configuration.";
427 ShutdownHost(kInvalidHostConfigurationExitCode);
428 return;
431 if (!ApplyConfig(config.Pass())) {
432 LOG(ERROR) << "Failed to apply the configuration.";
433 ShutdownHost(kInvalidHostConfigurationExitCode);
434 return;
437 if (state_ == HOST_INITIALIZING) {
438 // TODO(sergeyu): Currently OnPolicyUpdate() assumes that host config is
439 // already loaded so PolicyWatcher has to be started here. Separate policy
440 // loading from policy verifications and move |policy_watcher_|
441 // initialization to StartOnNetworkThread().
442 policy_watcher_.reset(
443 policy_hack::PolicyWatcher::Create(context_->file_task_runner()));
444 policy_watcher_->StartWatching(
445 base::Bind(&HostProcess::OnPolicyUpdate, base::Unretained(this)));
446 } else if (state_ == HOST_STARTED) {
447 // TODO(sergeyu): Here we assume that PIN is the only part of the config
448 // that may change while the service is running. Change ApplyConfig() to
449 // detect other changes in the config and restart host if necessary here.
450 CreateAuthenticatorFactory();
454 void HostProcess::OnConfigWatcherError() {
455 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
456 ShutdownHost(kInvalidHostConfigurationExitCode);
459 void HostProcess::StartOnNetworkThread() {
460 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
462 #if !defined(REMOTING_MULTI_PROCESS)
463 // Start watching the host configuration file.
464 config_watcher_.reset(new ConfigFileWatcher(context_->network_task_runner(),
465 context_->file_task_runner(),
466 this));
467 config_watcher_->Watch(host_config_path_);
468 #endif // !defined(REMOTING_MULTI_PROCESS)
470 #if defined(OS_POSIX)
471 remoting::RegisterSignalHandler(
472 SIGTERM,
473 base::Bind(&HostProcess::SigTermHandler, base::Unretained(this)));
474 #endif // defined(OS_POSIX)
477 #if defined(OS_POSIX)
478 void HostProcess::SigTermHandler(int signal_number) {
479 DCHECK(signal_number == SIGTERM);
480 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
481 LOG(INFO) << "Caught SIGTERM: Shutting down...";
482 ShutdownHost(kSuccessExitCode);
484 #endif // OS_POSIX
486 void HostProcess::CreateAuthenticatorFactory() {
487 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
489 if (state_ != HOST_STARTED)
490 return;
492 std::string local_certificate = key_pair_.GenerateCertificate();
493 if (local_certificate.empty()) {
494 LOG(ERROR) << "Failed to generate host certificate.";
495 ShutdownHost(kInitializationFailed);
496 return;
499 scoped_ptr<protocol::AuthenticatorFactory> factory(
500 new protocol::Me2MeHostAuthenticatorFactory(
501 local_certificate, *key_pair_.private_key(), host_secret_hash_));
502 #if defined(OS_POSIX)
503 // On Linux and Mac, perform a PAM authorization step after authentication.
504 factory.reset(new PamAuthorizationFactory(factory.Pass()));
505 #endif
506 host_->SetAuthenticatorFactory(factory.Pass());
509 // IPC::Listener implementation.
510 bool HostProcess::OnMessageReceived(const IPC::Message& message) {
511 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
513 #if defined(REMOTING_MULTI_PROCESS)
514 bool handled = true;
515 IPC_BEGIN_MESSAGE_MAP(HostProcess, message)
516 IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Crash,
517 OnCrash)
518 IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Configuration,
519 OnConfigUpdated)
520 IPC_MESSAGE_FORWARD(
521 ChromotingDaemonNetworkMsg_DesktopAttached,
522 desktop_session_connector_,
523 DesktopSessionConnector::OnDesktopSessionAgentAttached)
524 IPC_MESSAGE_FORWARD(ChromotingDaemonNetworkMsg_TerminalDisconnected,
525 desktop_session_connector_,
526 DesktopSessionConnector::OnTerminalDisconnected)
527 IPC_MESSAGE_UNHANDLED(handled = false)
528 IPC_END_MESSAGE_MAP()
529 return handled;
530 #else // !defined(REMOTING_MULTI_PROCESS)
531 return false;
532 #endif // !defined(REMOTING_MULTI_PROCESS)
535 void HostProcess::OnChannelError() {
536 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
538 // Shutdown the host if the daemon process disconnects the IPC channel.
539 context_->network_task_runner()->PostTask(
540 FROM_HERE,
541 base::Bind(&HostProcess::ShutdownHost, this, kSuccessExitCode));
544 void HostProcess::StartOnUiThread() {
545 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
547 if (!InitWithCommandLine(CommandLine::ForCurrentProcess())) {
548 OnConfigWatcherError();
549 return;
552 #if defined(OS_LINUX)
553 // TODO(sergeyu): Pass configuration parameters to the Linux-specific version
554 // of DesktopEnvironmentFactory when we have it.
555 media::ScreenCapturer::EnableXDamage(true);
557 // If an audio pipe is specific on the command-line then initialize
558 // AudioCapturerLinux to capture from it.
559 FilePath audio_pipe_name = CommandLine::ForCurrentProcess()->
560 GetSwitchValuePath(kAudioPipeSwitchName);
561 if (!audio_pipe_name.empty()) {
562 remoting::AudioCapturerLinux::InitializePipeReader(
563 context_->audio_task_runner(), audio_pipe_name);
565 #endif // defined(OS_LINUX)
567 // Create a desktop environment factory appropriate to the build type &
568 // platform.
569 #if defined(OS_WIN)
571 #if defined(REMOTING_MULTI_PROCESS)
572 IpcDesktopEnvironmentFactory* desktop_environment_factory =
573 new IpcDesktopEnvironmentFactory(
574 context_->network_task_runner(),
575 daemon_channel_.get());
576 desktop_session_connector_ = desktop_environment_factory;
577 #else // !defined(REMOTING_MULTI_PROCESS)
578 DesktopEnvironmentFactory* desktop_environment_factory =
579 new SessionDesktopEnvironmentFactory(
580 base::Bind(&HostProcess::SendSasToConsole, this));
581 #endif // !defined(REMOTING_MULTI_PROCESS)
583 #else // !defined(OS_WIN)
584 DesktopEnvironmentFactory* desktop_environment_factory =
585 new BasicDesktopEnvironmentFactory();
586 #endif // !defined(OS_WIN)
588 desktop_environment_factory_.reset(desktop_environment_factory);
590 // The host UI should be created on the UI thread.
591 bool want_user_interface = true;
592 #if defined(OS_LINUX) || defined(REMOTING_MULTI_PROCESS)
593 want_user_interface = false;
594 #elif defined(OS_MACOSX)
595 // Don't try to display any UI on top of the system's login screen as this
596 // is rejected by the Window Server on OS X 10.7.4, and prevents the
597 // capturer from working (http://crbug.com/140984).
599 // TODO(lambroslambrou): Use a better technique of detecting whether we're
600 // running in the LoginWindow context, and refactor this into a separate
601 // function to be used here and in CurtainMode::ActivateCurtain().
602 want_user_interface = getuid() != 0;
603 #endif // OS_MACOSX
605 if (want_user_interface) {
606 UiStrings ui_strings;
607 host_user_interface_.reset(
608 new HostUserInterface(context_->network_task_runner(),
609 context_->ui_task_runner(), ui_strings));
610 host_user_interface_->Init();
613 context_->network_task_runner()->PostTask(
614 FROM_HERE,
615 base::Bind(&HostProcess::StartOnNetworkThread, this));
618 void HostProcess::SendSasToConsole() {
619 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
621 if (daemon_channel_)
622 daemon_channel_->Send(new ChromotingNetworkDaemonMsg_SendSasToConsole());
625 void HostProcess::ShutdownOnUiThread() {
626 DCHECK(context_->ui_task_runner()->BelongsToCurrentThread());
628 // Tear down resources that need to be torn down on the UI thread.
629 network_change_notifier_.reset();
630 daemon_channel_.reset();
631 desktop_environment_factory_.reset();
632 host_user_interface_.reset();
634 // It is now safe for the HostProcess to be deleted.
635 self_ = NULL;
637 #if defined(OS_LINUX)
638 // Cause the global AudioPipeReader to be freed, otherwise the audio
639 // thread will remain in-use and prevent the process from exiting.
640 // TODO(wez): DesktopEnvironmentFactory should own the pipe reader.
641 // See crbug.com/161373 and crbug.com/104544.
642 AudioCapturerLinux::InitializePipeReader(NULL, FilePath());
643 #endif
646 // Overridden from HeartbeatSender::Listener
647 void HostProcess::OnUnknownHostIdError() {
648 LOG(ERROR) << "Host ID not found.";
649 ShutdownHost(kInvalidHostIdExitCode);
652 // Applies the host config, returning true if successful.
653 bool HostProcess::ApplyConfig(scoped_ptr<JsonHostConfig> config) {
654 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
656 if (!config->GetString(kHostIdConfigPath, &host_id_)) {
657 LOG(ERROR) << "host_id is not defined in the config.";
658 return false;
661 if (!key_pair_.Load(*config)) {
662 return false;
665 std::string host_secret_hash_string;
666 if (!config->GetString(kHostSecretHashConfigPath,
667 &host_secret_hash_string)) {
668 host_secret_hash_string = "plain:";
671 if (!host_secret_hash_.Parse(host_secret_hash_string)) {
672 LOG(ERROR) << "Invalid host_secret_hash.";
673 return false;
676 // Use an XMPP connection to the Talk network for session signalling.
677 if (!config->GetString(kXmppLoginConfigPath, &xmpp_login_) ||
678 !(config->GetString(kXmppAuthTokenConfigPath, &xmpp_auth_token_) ||
679 config->GetString(kOAuthRefreshTokenConfigPath,
680 &oauth_refresh_token_))) {
681 LOG(ERROR) << "XMPP credentials are not defined in the config.";
682 return false;
685 if (!oauth_refresh_token_.empty()) {
686 xmpp_auth_token_ = ""; // This will be set to the access token later.
687 xmpp_auth_service_ = "oauth2";
688 } else if (!config->GetString(kXmppAuthServiceConfigPath,
689 &xmpp_auth_service_)) {
690 // For the me2me host, we default to ClientLogin token for chromiumsync
691 // because earlier versions of the host had no HTTP stack with which to
692 // request an OAuth2 access token.
693 xmpp_auth_service_ = kChromotingTokenDefaultServiceName;
695 return true;
698 void HostProcess::OnPolicyUpdate(scoped_ptr<base::DictionaryValue> policies) {
699 // TODO(rmsousa): Consolidate all On*PolicyUpdate methods into this one.
700 // TODO(sergeyu): Currently polices are verified only when they are loaded.
701 // Separate policy loading from policy verifications - this will allow to
702 // check policies again later, e.g. when host config changes.
704 if (!context_->network_task_runner()->BelongsToCurrentThread()) {
705 context_->network_task_runner()->PostTask(FROM_HERE, base::Bind(
706 &HostProcess::OnPolicyUpdate, this, base::Passed(&policies)));
707 return;
710 bool restart_required = false;
711 bool bool_value;
712 std::string string_value;
713 if (policies->GetString(policy_hack::PolicyWatcher::kHostDomainPolicyName,
714 &string_value)) {
715 restart_required |= OnHostDomainPolicyUpdate(string_value);
717 if (policies->GetBoolean(
718 policy_hack::PolicyWatcher::kHostMatchUsernamePolicyName,
719 &bool_value)) {
720 restart_required |= OnUsernamePolicyUpdate(bool_value);
722 if (policies->GetBoolean(policy_hack::PolicyWatcher::kNatPolicyName,
723 &bool_value)) {
724 restart_required |= OnNatPolicyUpdate(bool_value);
726 if (policies->GetString(
727 policy_hack::PolicyWatcher::kHostTalkGadgetPrefixPolicyName,
728 &string_value)) {
729 restart_required |= OnHostTalkGadgetPrefixPolicyUpdate(string_value);
731 if (policies->GetBoolean(
732 policy_hack::PolicyWatcher::kHostRequireCurtainPolicyName,
733 &bool_value)) {
734 restart_required |= OnCurtainPolicyUpdate(bool_value);
737 if (state_ == HOST_INITIALIZING) {
738 StartHost();
739 } else if (state_ == HOST_STARTED && restart_required) {
740 RestartHost();
744 bool HostProcess::OnHostDomainPolicyUpdate(const std::string& host_domain) {
745 // Returns true if the host has to be restarted after this policy update.
746 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
748 LOG(INFO) << "Policy sets host domain: " << host_domain;
750 if (!host_domain.empty() &&
751 !EndsWith(xmpp_login_, std::string("@") + host_domain, false)) {
752 ShutdownHost(kInvalidHostDomainExitCode);
754 return false;
757 bool HostProcess::OnUsernamePolicyUpdate(bool host_username_match_required) {
758 // Returns false: never restart the host after this policy update.
759 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
761 if (host_username_match_required) {
762 LOG(INFO) << "Policy requires host username match.";
763 bool shutdown = !CanGetUsername() ||
764 !StartsWithASCII(xmpp_login_, GetUsername() + std::string("@"),
765 false);
767 #if defined(OS_MACOSX)
768 // On Mac, we run as root at the login screen, so the username won't match.
769 // However, there's no need to enforce the policy at the login screen, as
770 // the client will have to reconnect if a login occurs.
771 if (shutdown && getuid() == 0) {
772 shutdown = false;
774 #endif
776 if (shutdown) {
777 ShutdownHost(kUsernameMismatchExitCode);
779 } else {
780 LOG(INFO) << "Policy does not require host username match.";
783 return false;
786 bool HostProcess::OnNatPolicyUpdate(bool nat_traversal_enabled) {
787 // Returns true if the host has to be restarted after this policy update.
788 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
790 if (allow_nat_traversal_ != nat_traversal_enabled) {
791 if (nat_traversal_enabled)
792 LOG(INFO) << "Policy enables NAT traversal.";
793 else
794 LOG(INFO) << "Policy disables NAT traversal.";
795 allow_nat_traversal_ = nat_traversal_enabled;
796 return true;
798 return false;
801 bool HostProcess::OnCurtainPolicyUpdate(bool curtain_required) {
802 // Returns true if the host has to be restarted after this policy update.
803 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
805 #if defined(OS_MACOSX)
806 if (curtain_required) {
807 // When curtain mode is in effect on Mac, the host process runs in the
808 // user's switched-out session, but launchd will also run an instance at
809 // the console login screen. Even if no user is currently logged-on, we
810 // can't support remote-access to the login screen because the current host
811 // process model disconnects the client during login, which would leave
812 // the logged in session un-curtained on the console until they reconnect.
814 // TODO(jamiewalch): Fix this once we have implemented the multi-process
815 // daemon architecture (crbug.com/134894)
816 if (getuid() == 0) {
817 ShutdownHost(kLoginScreenNotSupportedExitCode);
818 return false;
821 #endif
823 if (curtain_required_ != curtain_required) {
824 if (curtain_required)
825 LOG(ERROR) << "Policy requires curtain-mode.";
826 else
827 LOG(ERROR) << "Policy does not require curtain-mode.";
828 curtain_required_ = curtain_required;
829 if (curtaining_host_observer_)
830 curtaining_host_observer_->SetEnableCurtaining(curtain_required_);
831 return true;
833 return false;
836 bool HostProcess::OnHostTalkGadgetPrefixPolicyUpdate(
837 const std::string& talkgadget_prefix) {
838 // Returns true if the host has to be restarted after this policy update.
839 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
841 if (talkgadget_prefix != talkgadget_prefix_) {
842 LOG(INFO) << "Policy sets talkgadget prefix: " << talkgadget_prefix;
843 talkgadget_prefix_ = talkgadget_prefix;
844 return true;
846 return false;
849 void HostProcess::StartHost() {
850 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
851 DCHECK(!host_);
852 DCHECK(!signal_strategy_.get());
853 DCHECK(state_ == HOST_INITIALIZING || state_ == HOST_STOPPING_TO_RESTART ||
854 state_ == HOST_STOPPED) << state_;
855 state_ = HOST_STARTED;
857 signal_strategy_.reset(
858 new XmppSignalStrategy(context_->url_request_context_getter(),
859 xmpp_login_, xmpp_auth_token_,
860 xmpp_auth_service_));
862 scoped_ptr<DnsBlackholeChecker> dns_blackhole_checker(
863 new DnsBlackholeChecker(context_->url_request_context_getter(),
864 talkgadget_prefix_));
866 signaling_connector_.reset(new SignalingConnector(
867 signal_strategy_.get(),
868 context_->url_request_context_getter(),
869 dns_blackhole_checker.Pass(),
870 base::Bind(&HostProcess::OnAuthFailed, this)));
872 if (!oauth_refresh_token_.empty()) {
873 scoped_ptr<SignalingConnector::OAuthCredentials> oauth_credentials(
874 new SignalingConnector::OAuthCredentials(
875 xmpp_login_, oauth_refresh_token_));
876 signaling_connector_->EnableOAuth(oauth_credentials.Pass());
879 NetworkSettings network_settings(
880 allow_nat_traversal_ ?
881 NetworkSettings::NAT_TRAVERSAL_ENABLED :
882 NetworkSettings::NAT_TRAVERSAL_DISABLED);
883 if (!allow_nat_traversal_) {
884 network_settings.min_port = NetworkSettings::kDefaultMinPort;
885 network_settings.max_port = NetworkSettings::kDefaultMaxPort;
888 host_ = new ChromotingHost(
889 signal_strategy_.get(),
890 desktop_environment_factory_.get(),
891 CreateHostSessionManager(network_settings,
892 context_->url_request_context_getter()),
893 context_->audio_task_runner(),
894 context_->input_task_runner(),
895 context_->video_capture_task_runner(),
896 context_->video_encode_task_runner(),
897 context_->network_task_runner(),
898 context_->ui_task_runner());
900 // TODO(simonmorris): Get the maximum session duration from a policy.
901 #if defined(OS_LINUX)
902 host_->SetMaximumSessionDuration(base::TimeDelta::FromHours(20));
903 #endif
905 heartbeat_sender_.reset(new HeartbeatSender(
906 this, host_id_, signal_strategy_.get(), &key_pair_));
908 log_to_server_.reset(
909 new LogToServer(host_, ServerLogEntry::ME2ME, signal_strategy_.get()));
910 host_event_logger_ = HostEventLogger::Create(host_, kApplicationName);
912 resizing_host_observer_.reset(
913 new ResizingHostObserver(desktop_resizer_.get(), host_));
915 // Create a host observer to enable/disable curtain mode as clients connect
916 // and disconnect.
917 curtaining_host_observer_.reset(new CurtainingHostObserver(
918 curtain_.get(), host_));
919 curtaining_host_observer_->SetEnableCurtaining(curtain_required_);
921 if (host_user_interface_.get()) {
922 host_user_interface_->Start(
923 host_, base::Bind(&HostProcess::OnDisconnectRequested, this));
926 host_->Start(xmpp_login_);
928 CreateAuthenticatorFactory();
931 void HostProcess::OnAuthFailed() {
932 ShutdownHost(kInvalidOauthCredentialsExitCode);
935 void HostProcess::OnCurtainModeFailed() {
936 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
937 DCHECK(host_);
938 LOG(ERROR) << "Curtain mode failed to activate. Closing connection.";
939 host_->RejectAuthenticatingClient();
942 void HostProcess::OnRemoteSessionSwitchedToConsole() {
943 LOG(INFO) << "The remote session switched was to the console."
944 " Closing connection.";
945 OnDisconnectRequested();
948 // Invoked when the user uses the Disconnect windows to terminate
949 // the sessions, or when the local session is activated in curtain mode.
950 void HostProcess::OnDisconnectRequested() {
951 if (!context_->network_task_runner()->BelongsToCurrentThread()) {
952 context_->network_task_runner()->PostTask(FROM_HERE,
953 base::Bind(&HostProcess::OnDisconnectRequested, this));
954 return;
956 if (host_) {
957 host_->DisconnectAllClients();
961 void HostProcess::RestartHost() {
962 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
963 DCHECK_EQ(state_, HOST_STARTED);
965 state_ = HOST_STOPPING_TO_RESTART;
966 host_->Shutdown(base::Bind(&HostProcess::ShutdownOnNetworkThread, this));
969 void HostProcess::ShutdownHost(int exit_code) {
970 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
972 *exit_code_out_ = exit_code;
974 switch (state_) {
975 case HOST_INITIALIZING:
976 state_ = HOST_STOPPING;
977 ShutdownOnNetworkThread();
978 break;
980 case HOST_STARTED:
981 state_ = HOST_STOPPING;
982 host_->Shutdown(base::Bind(&HostProcess::ShutdownOnNetworkThread, this));
983 break;
985 case HOST_STOPPING_TO_RESTART:
986 state_ = HOST_STOPPING;
987 break;
989 case HOST_STOPPING:
990 case HOST_STOPPED:
991 // Host is already stopped or being stopped. No action is required.
992 break;
996 void HostProcess::ShutdownOnNetworkThread() {
997 DCHECK(context_->network_task_runner()->BelongsToCurrentThread());
999 host_ = NULL;
1000 curtaining_host_observer_.reset();
1001 host_event_logger_.reset();
1002 log_to_server_.reset();
1003 heartbeat_sender_.reset();
1004 signaling_connector_.reset();
1005 signal_strategy_.reset();
1006 resizing_host_observer_.reset();
1008 if (state_ == HOST_STOPPING_TO_RESTART) {
1009 StartHost();
1010 } else if (state_ == HOST_STOPPING) {
1011 state_ = HOST_STOPPED;
1013 if (policy_watcher_.get()) {
1014 base::WaitableEvent done_event(true, false);
1015 policy_watcher_->StopWatching(&done_event);
1016 done_event.Wait();
1017 policy_watcher_.reset();
1020 config_watcher_.reset();
1022 // Complete the rest of shutdown on the main thread.
1023 context_->ui_task_runner()->PostTask(
1024 FROM_HERE,
1025 base::Bind(&HostProcess::ShutdownOnUiThread, this));
1026 } else {
1027 // This method is used as a callback for ChromotingHost::Shutdown() which is
1028 // called only in STOPPING_TO_RESTART and STOPPING states.
1029 NOTREACHED();
1033 void HostProcess::OnCrash(const std::string& function_name,
1034 const std::string& file_name,
1035 const int& line_number) {
1036 CHECK(false);
1039 } // namespace remoting
1041 int main(int argc, char** argv) {
1042 #if defined(OS_MACOSX)
1043 // Needed so we don't leak objects when threads are created.
1044 base::mac::ScopedNSAutoreleasePool pool;
1045 #endif
1047 CommandLine::Init(argc, argv);
1049 // Initialize Breakpad as early as possible. On Windows, this happens in
1050 // WinMain(), so it shouldn't also be done here. The command-line needs to be
1051 // initialized first, so that the preference for crash-reporting can be looked
1052 // up in the config file.
1053 #if defined(MAC_BREAKPAD)
1054 if (remoting::IsUsageStatsAllowed()) {
1055 remoting::InitializeCrashReporting();
1057 #endif // MAC_BREAKPAD
1059 // This object instance is required by Chrome code (for example,
1060 // LazyInstance, MessageLoop).
1061 base::AtExitManager exit_manager;
1063 if (CommandLine::ForCurrentProcess()->HasSwitch(kVersionSwitchName)) {
1064 printf("%s\n", STRINGIZE(VERSION));
1065 return 0;
1068 remoting::InitHostLogging();
1070 #if defined(TOOLKIT_GTK)
1071 // Required for any calls into GTK functions, such as the Disconnect and
1072 // Continue windows, though these should not be used for the Me2Me case
1073 // (crbug.com/104377).
1074 const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
1075 gfx::GtkInitFromCommandLine(*cmd_line);
1076 #endif // TOOLKIT_GTK
1078 // Enable support for SSL server sockets, which must be done while still
1079 // single-threaded.
1080 net::EnableSSLServerSockets();
1082 // Create the main message loop and start helper threads.
1083 MessageLoop message_loop(MessageLoop::TYPE_UI);
1084 scoped_ptr<remoting::ChromotingHostContext> context =
1085 remoting::ChromotingHostContext::Create(
1086 new remoting::AutoThreadTaskRunner(message_loop.message_loop_proxy(),
1087 MessageLoop::QuitClosure()));
1088 if (!context)
1089 return remoting::kInitializationFailed;
1091 // Create & start the HostProcess using these threads.
1092 // TODO(wez): The HostProcess holds a reference to itself until Shutdown().
1093 // Remove this hack as part of the multi-process refactoring.
1094 int exit_code = remoting::kSuccessExitCode;
1095 new remoting::HostProcess(context.Pass(), &exit_code);
1097 // Run the main (also UI) message loop until the host no longer needs it.
1098 message_loop.Run();
1100 return exit_code;
1103 #if defined(OS_WIN)
1104 HMODULE g_hModule = NULL;
1106 int CALLBACK WinMain(HINSTANCE instance,
1107 HINSTANCE previous_instance,
1108 LPSTR command_line,
1109 int show_command) {
1110 #if defined(OFFICIAL_BUILD)
1111 if (remoting::IsUsageStatsAllowed()) {
1112 remoting::InitializeCrashReporting();
1114 #endif // OFFICIAL_BUILD
1116 g_hModule = instance;
1118 // Register and initialize common controls.
1119 INITCOMMONCONTROLSEX info;
1120 info.dwSize = sizeof(info);
1121 info.dwICC = ICC_STANDARD_CLASSES;
1122 InitCommonControlsEx(&info);
1124 // Mark the process as DPI-aware, so Windows won't scale coordinates in APIs.
1125 // N.B. This API exists on Vista and above.
1126 if (base::win::GetVersion() >= base::win::VERSION_VISTA) {
1127 FilePath path(base::GetNativeLibraryName(UTF8ToUTF16("user32")));
1128 base::ScopedNativeLibrary user32(path);
1129 CHECK(user32.is_valid());
1131 typedef BOOL (WINAPI * SetProcessDPIAwareFn)();
1132 SetProcessDPIAwareFn set_process_dpi_aware =
1133 static_cast<SetProcessDPIAwareFn>(
1134 user32.GetFunctionPointer("SetProcessDPIAware"));
1135 set_process_dpi_aware();
1138 // CommandLine::Init() ignores the passed |argc| and |argv| on Windows getting
1139 // the command line from GetCommandLineW(), so we can safely pass NULL here.
1140 return main(0, NULL);
1143 #endif // defined(OS_WIN)