diff options
author | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-05 19:58:36 +0000 |
---|---|---|
committer | alexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-09-05 19:58:36 +0000 |
commit | df338b7db4dfebd184886fec55db969305c94f07 (patch) | |
tree | 8e0184e81556899615ffac2120efe0ea563a1cce | |
parent | e2206627c1f4f26425b7ba51a21e8d021956bea1 (diff) | |
download | chromium_src-df338b7db4dfebd184886fec55db969305c94f07.zip chromium_src-df338b7db4dfebd184886fec55db969305c94f07.tar.gz chromium_src-df338b7db4dfebd184886fec55db969305c94f07.tar.bz2 |
[Chromoting] The daemon process now starts the networking process and passes the host configuration (and updates) over the IPC channel.
This CL also switches to FilePathWatcher (to detect the configuration file changes) on all platforms.
BUG=123696,134694
Review URL: https://chromiumcodereview.appspot.com/10855249
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@154999 0039d316-1c4b-4281-b951-d872f2087c98
22 files changed, 781 insertions, 334 deletions
diff --git a/remoting/base/stoppable.h b/remoting/base/stoppable.h index 4d58a59..dddfcdae 100644 --- a/remoting/base/stoppable.h +++ b/remoting/base/stoppable.h @@ -21,8 +21,8 @@ class Stoppable { public: // Constructs an object and stores the callback to be posted to |task_runner| // once the object has been shutdown completely. - explicit Stoppable(scoped_refptr<base::SingleThreadTaskRunner> task_runner, - const base::Closure& stopped_callback); + Stoppable(scoped_refptr<base::SingleThreadTaskRunner> task_runner, + const base::Closure& stopped_callback); virtual ~Stoppable(); // Initiates shutdown. It can be called by both the owner of the object and diff --git a/remoting/host/chromoting_host_context.cc b/remoting/host/chromoting_host_context.cc index 3aa26af..37ba9f3 100644 --- a/remoting/host/chromoting_host_context.cc +++ b/remoting/host/chromoting_host_context.cc @@ -69,10 +69,10 @@ bool ChromotingHostContext::Start() { file_task_runner_ = new AutoThreadTaskRunner(file_thread_.message_loop_proxy(), ui_task_runner_); - network_task_runner_ = new AutoThreadTaskRunner(network_thread_.message_loop_proxy(), ui_task_runner_); + url_request_context_getter_ = new URLRequestContextGetter( ui_task_runner(), network_task_runner(), static_cast<MessageLoopForIO*>(file_thread_.message_loop())); diff --git a/remoting/host/chromoting_host_context.h b/remoting/host/chromoting_host_context.h index 497b8e3..2e3c8e7 100644 --- a/remoting/host/chromoting_host_context.h +++ b/remoting/host/chromoting_host_context.h @@ -7,9 +7,12 @@ #include "base/gtest_prod_util.h" #include "base/memory/ref_counted.h" -#include "base/single_thread_task_runner.h" #include "base/threading/thread.h" +namespace base { +class SingleThreadTaskRunner; +} // namespace base + namespace net { class URLRequestContextGetter; } // namespace net diff --git a/remoting/host/chromoting_messages.h b/remoting/host/chromoting_messages.h index b744428..f997f47 100644 --- a/remoting/host/chromoting_messages.h +++ b/remoting/host/chromoting_messages.h @@ -8,3 +8,9 @@ #include "ipc/ipc_message_macros.h" #define IPC_MESSAGE_START ChromotingMsgStart + +//----------------------------------------------------------------------------- +// Daemon to Network messages + +// Delivers the host configuration (and updates) to the network process. +IPC_MESSAGE_CONTROL1(ChromotingDaemonNetworkMsg_Configuration, std::string) diff --git a/remoting/host/composite_host_config.cc b/remoting/host/composite_host_config.cc deleted file mode 100644 index 01f456f..0000000 --- a/remoting/host/composite_host_config.cc +++ /dev/null @@ -1,50 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "remoting/host/composite_host_config.h" - -#include "remoting/host/json_host_config.h" - -namespace remoting { - -CompositeHostConfig::CompositeHostConfig() { -} - -CompositeHostConfig::~CompositeHostConfig() { -} - -void CompositeHostConfig::AddConfigPath(const FilePath& path) { - configs_.push_back(new JsonHostConfig(path)); -} - -bool CompositeHostConfig::Read() { - bool result = true; - for (ScopedVector<JsonHostConfig>::iterator it = configs_.begin(); - it != configs_.end(); ++it) { - result = result && (*it)->Read(); - } - return result; -} - -bool CompositeHostConfig::GetString(const std::string& path, - std::string* out_value) const { - for (ScopedVector<JsonHostConfig>::const_iterator it = configs_.begin(); - it != configs_.end(); ++it) { - if ((*it)->GetString(path, out_value)) - return true; - } - return false; -} - -bool CompositeHostConfig::GetBoolean(const std::string& path, - bool* out_value) const { - for (ScopedVector<JsonHostConfig>::const_iterator it = configs_.begin(); - it != configs_.end(); ++it) { - if ((*it)->GetBoolean(path, out_value)) - return true; - } - return false; -} - -} // namespace remoting diff --git a/remoting/host/composite_host_config.h b/remoting/host/composite_host_config.h deleted file mode 100644 index 7bb9f31..0000000 --- a/remoting/host/composite_host_config.h +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright (c) 2012 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef REMOTING_HOST_COMPOSITE_HOST_CONFIG_H_ -#define REMOTING_HOST_COMPOSITE_HOST_CONFIG_H_ - -#include "remoting/host/host_config.h" - -#include "base/compiler_specific.h" -#include "base/memory/scoped_vector.h" - -class FilePath; - -namespace remoting { - -class JsonHostConfig; - -// CompositeConfig reads multiple configuration files and merges them together. -class CompositeHostConfig : public HostConfig { - public: - CompositeHostConfig(); - virtual ~CompositeHostConfig(); - - // Add configuration file specified stored at |path|. When the same parameter - // is present in more than one file priority is given to those that are added - // first. - void AddConfigPath(const FilePath& path); - - // Reads all configuration files. Returns false if it fails to load any of the - // files. - bool Read(); - - // HostConfig interface. - virtual bool GetString(const std::string& path, - std::string* out_value) const OVERRIDE; - virtual bool GetBoolean(const std::string& path, - bool* out_value) const OVERRIDE; - - private: - ScopedVector<JsonHostConfig> configs_; - - DISALLOW_COPY_AND_ASSIGN(CompositeHostConfig); -}; - -} // namespace remoting - -#endif // REMOTING_HOST_COMPOSITE_HOST_CONFIG_H_ diff --git a/remoting/host/config_file_watcher.cc b/remoting/host/config_file_watcher.cc new file mode 100644 index 0000000..584bf90 --- /dev/null +++ b/remoting/host/config_file_watcher.cc @@ -0,0 +1,186 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "remoting/host/config_file_watcher.h" + +#include "base/bind.h" +#include "base/bind_helpers.h" +#include "base/files/file_path_watcher.h" +#include "base/file_util.h" +#include "base/memory/scoped_ptr.h" +#include "base/memory/weak_ptr.h" +#include "base/single_thread_task_runner.h" +#include "base/timer.h" + +namespace remoting { + +// The name of the command-line switch used to specify the host configuration +// file to use. +const char kHostConfigSwitchName[] = "host-config"; + +const FilePath::CharType kDefaultHostConfigFile[] = + FILE_PATH_LITERAL("host.json"); + +class ConfigFileWatcherImpl + : public base::RefCountedThreadSafe<ConfigFileWatcherImpl> { + public: + // Creates a configuration file watcher that lives on the |io_task_runner| + // thread but posts config file updates on on |main_task_runner|. + ConfigFileWatcherImpl( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, + ConfigFileWatcher::Delegate* delegate); + + // Starts watching |config_path|. + void Watch(const FilePath& config_path); + + // Stops watching the configuration file. + void StopWatching(); + + private: + friend class base::RefCountedThreadSafe<ConfigFileWatcherImpl>; + virtual ~ConfigFileWatcherImpl(); + + void FinishStopping(); + + // Called every time the host configuration file is updated. + void OnConfigUpdated(const FilePath& path, bool error); + + // Reads the configuration file and passes it to the delegate. + void ReloadConfig(); + + FilePath config_path_; + + scoped_ptr<base::DelayTimer<ConfigFileWatcherImpl> > config_updated_timer_; + + // Monitors the host configuration file. + scoped_ptr<base::files::FilePathWatcher> config_watcher_; + + base::WeakPtrFactory<ConfigFileWatcher::Delegate> delegate_weak_factory_; + base::WeakPtr<ConfigFileWatcher::Delegate> delegate_; + + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; + + DISALLOW_COPY_AND_ASSIGN(ConfigFileWatcherImpl); +}; + +ConfigFileWatcher::Delegate::~Delegate() { +} + +ConfigFileWatcher::ConfigFileWatcher( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, + Delegate* delegate) + : impl_(new ConfigFileWatcherImpl(main_task_runner, + io_task_runner, delegate)) { +} + +ConfigFileWatcher::~ConfigFileWatcher() { + impl_->StopWatching(); + impl_ = NULL; +} + +void ConfigFileWatcher::Watch(const FilePath& config_path) { + impl_->Watch(config_path); +} + +ConfigFileWatcherImpl::ConfigFileWatcherImpl( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, + ConfigFileWatcher::Delegate* delegate) + : delegate_weak_factory_(delegate), + delegate_(delegate_weak_factory_.GetWeakPtr()), + main_task_runner_(main_task_runner), + io_task_runner_(io_task_runner) { + DCHECK(main_task_runner_->BelongsToCurrentThread()); +} + +void ConfigFileWatcherImpl::Watch(const FilePath& config_path) { + if (!io_task_runner_->BelongsToCurrentThread()) { + io_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ConfigFileWatcherImpl::Watch, this, config_path)); + return; + } + + DCHECK(config_path_.empty()); + DCHECK(config_updated_timer_.get() == NULL); + DCHECK(config_watcher_.get() == NULL); + + // Create the timer that will be used for delayed-reading the configuration + // file. + config_updated_timer_.reset(new base::DelayTimer<ConfigFileWatcherImpl>( + FROM_HERE, base::TimeDelta::FromSeconds(2), this, + &ConfigFileWatcherImpl::ReloadConfig)); + + // Start watching the configuration file. + config_watcher_.reset(new base::files::FilePathWatcher()); + config_path_ = config_path; + if (!config_watcher_->Watch( + config_path_, + base::Bind(&ConfigFileWatcherImpl::OnConfigUpdated, this))) { + LOG(ERROR) << "Couldn't watch file '" << config_path_.value() << "'"; + main_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ConfigFileWatcher::Delegate::OnConfigWatcherError, + delegate_)); + return; + } + + // Force reloading of the configuration file at least once. + ReloadConfig(); +} + +void ConfigFileWatcherImpl::StopWatching() { + DCHECK(main_task_runner_->BelongsToCurrentThread()); + + delegate_weak_factory_.InvalidateWeakPtrs(); + io_task_runner_->PostTask( + FROM_HERE, base::Bind(&ConfigFileWatcherImpl::FinishStopping, this)); +} + +ConfigFileWatcherImpl::~ConfigFileWatcherImpl() { + DCHECK(config_updated_timer_.get() == NULL); + DCHECK(config_watcher_.get() == NULL); +} + +void ConfigFileWatcherImpl::FinishStopping() { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + config_updated_timer_.reset(NULL); + config_watcher_.reset(NULL); +} + +void ConfigFileWatcherImpl::OnConfigUpdated(const FilePath& path, bool error) { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + // Call ReloadConfig() after a short delay, so that we will not try to read + // the updated configuration file before it has been completely written. + // If the writer moves the new configuration file into place atomically, + // this delay may not be necessary. + if (!error && config_path_ == path) + config_updated_timer_->Reset(); +} + +void ConfigFileWatcherImpl::ReloadConfig() { + DCHECK(io_task_runner_->BelongsToCurrentThread()); + + std::string config; + if (!file_util::ReadFileToString(config_path_, &config)) { + LOG(ERROR) << "Failed to read '" << config_path_.value() << "'"; + main_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ConfigFileWatcher::Delegate::OnConfigWatcherError, + delegate_)); + return; + } + + main_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ConfigFileWatcher::Delegate::OnConfigUpdated, delegate_, + config)); +} + +} // namespace remoting diff --git a/remoting/host/config_file_watcher.h b/remoting/host/config_file_watcher.h new file mode 100644 index 0000000..83e971a --- /dev/null +++ b/remoting/host/config_file_watcher.h @@ -0,0 +1,57 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_HOST_DAEMON_CONFIG_WATCHER_H_ +#define REMOTING_HOST_DAEMON_CONFIG_WATCHER_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/file_path.h" +#include "base/memory/ref_counted.h" + +namespace base { +class SingleThreadTaskRunner; +} // namespace base + +namespace remoting { + +extern const char kHostConfigSwitchName[]; +extern const FilePath::CharType kDefaultHostConfigFile[]; + +class ConfigFileWatcherImpl; + +class ConfigFileWatcher { + public: + class Delegate { + public: + virtual ~Delegate(); + + // Called once after starting watching the configuration file and every time + // the file changes. + virtual void OnConfigUpdated(const std::string& serialized_config) = 0; + + // Called when the configuration file watcher encountered an error. + virtual void OnConfigWatcherError() = 0; + }; + + // Creates a configuration file watcher that lives at the |io_task_runner| + // thread but posts config file updates on on |main_task_runner|. + ConfigFileWatcher( + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, + Delegate* delegate); + virtual ~ConfigFileWatcher(); + + // Starts watching |config_path|. + void Watch(const FilePath& config_path); + + private: + scoped_refptr<ConfigFileWatcherImpl> impl_; + + DISALLOW_COPY_AND_ASSIGN(ConfigFileWatcher); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_DAEMON_CONFIG_WATCHER_H_ diff --git a/remoting/host/daemon_process.cc b/remoting/host/daemon_process.cc index 2a08284..607fca7 100644 --- a/remoting/host/daemon_process.cc +++ b/remoting/host/daemon_process.cc @@ -6,22 +6,41 @@ #include "base/bind.h" #include "base/bind_helpers.h" +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/file_util.h" #include "base/single_thread_task_runner.h" -#include "base/threading/thread.h" +#include "remoting/host/branding.h" +#include "remoting/host/chromoting_messages.h" -namespace { +namespace remoting { -const char kIpcThreadName[] = "Daemon process IPC"; +DaemonProcess::~DaemonProcess() { + CHECK(!config_watcher_.get()); +} -} // namespace +void DaemonProcess::OnConfigUpdated(const std::string& serialized_config) { + if (serialized_config_ != serialized_config) { + serialized_config_ = serialized_config; + Send(new ChromotingDaemonNetworkMsg_Configuration(serialized_config_)); + } +} -namespace remoting { +void DaemonProcess::OnConfigWatcherError() { + Stop(); +} -DaemonProcess::~DaemonProcess() { +void DaemonProcess::OnChannelConnected() { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + + // Send the configuration to the network process. + Send(new ChromotingDaemonNetworkMsg_Configuration(serialized_config_)); } bool DaemonProcess::OnMessageReceived(const IPC::Message& message) { - return true; + DCHECK(main_task_runner()->BelongsToCurrentThread()); + + return false; } DaemonProcess::DaemonProcess( @@ -34,22 +53,34 @@ DaemonProcess::DaemonProcess( // Initialize on the same thread that will be used for shutting down. main_task_runner_->PostTask( FROM_HERE, - base::Bind(&DaemonProcess::Init, base::Unretained(this))); + base::Bind(&DaemonProcess::Initialize, base::Unretained(this))); } -void DaemonProcess::Init() { - DCHECK(main_task_runner_->BelongsToCurrentThread()); +void DaemonProcess::Initialize() { + DCHECK(main_task_runner()->BelongsToCurrentThread()); - if (!LaunchNetworkProcess()) { - LOG(ERROR) << "Failed to launch the networking process."; - Stop(); - return; + // Get the name of the host configuration file. + FilePath default_config_dir = remoting::GetConfigDir(); + FilePath config_path = default_config_dir.Append(kDefaultHostConfigFile); + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(kHostConfigSwitchName)) { + config_path = command_line->GetSwitchValuePath(kHostConfigSwitchName); } + + // Start watching the host configuration file. + config_watcher_.reset(new ConfigFileWatcher(main_task_runner(), + io_task_runner(), + this)); + config_watcher_->Watch(config_path); + + // Launch the process. + LaunchNetworkProcess(); } void DaemonProcess::DoStop() { - DCHECK(main_task_runner_->BelongsToCurrentThread()); + DCHECK(main_task_runner()->BelongsToCurrentThread()); + config_watcher_.reset(); CompleteStopping(); } diff --git a/remoting/host/daemon_process.h b/remoting/host/daemon_process.h index ed5f4cb..a52be9e 100644 --- a/remoting/host/daemon_process.h +++ b/remoting/host/daemon_process.h @@ -12,22 +12,24 @@ #include "ipc/ipc_channel.h" #include "ipc/ipc_channel_proxy.h" #include "remoting/base/stoppable.h" +#include "remoting/host/config_file_watcher.h" +#include "remoting/host/worker_process_ipc_delegate.h" + +class FilePath; namespace base { class SingleThreadTaskRunner; -class Thread; } // namespace base -namespace IPC { -class Message; -} // namespace IPC - namespace remoting { // This class implements core of the daemon process. It manages the networking // process running at lower privileges and maintains the list of virtual // terminals. -class DaemonProcess : public Stoppable, public IPC::Listener { +class DaemonProcess + : public Stoppable, + public ConfigFileWatcher::Delegate, + public WorkerProcessIpcDelegate { public: virtual ~DaemonProcess(); @@ -37,33 +39,52 @@ class DaemonProcess : public Stoppable, public IPC::Listener { scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, const base::Closure& stopped_callback); - // IPC::Listener implementation. + // ConfigFileWatcher::Delegate + virtual void OnConfigUpdated(const std::string& serialized_config) OVERRIDE; + virtual void OnConfigWatcherError() OVERRIDE; + + // WorkerProcessIpcDelegate implementation. + virtual void OnChannelConnected() OVERRIDE; virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + // Sends an IPC message to the network process. The message will be dropped + // unless the network process is connected over the IPC channel. + virtual void Send(IPC::Message* message) = 0; + protected: DaemonProcess(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, const base::Closure& stopped_callback); // Reads the host configuration and launches the networking process. - void Init(); + void Initialize(); // Stoppable implementation. virtual void DoStop() OVERRIDE; // Launches the network process and establishes an IPC channel with it. - virtual bool LaunchNetworkProcess() = 0; + virtual void LaunchNetworkProcess() = 0; + + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner() { + return main_task_runner_; + } + + scoped_refptr<base::SingleThreadTaskRunner> io_task_runner() { + return io_task_runner_; + } private: + scoped_ptr<ConfigFileWatcher> config_watcher_; + + // The configuration file contents. + std::string serialized_config_; + // The main task runner. Typically it is the UI message loop. scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; // Handles IPC and background I/O tasks. scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_; - // The IPC channel connecting the daemon process to the networking process. - scoped_ptr<IPC::ChannelProxy> network_process_channel_; - DISALLOW_COPY_AND_ASSIGN(DaemonProcess); }; diff --git a/remoting/host/daemon_process_win.cc b/remoting/host/daemon_process_win.cc index 2f7706d..d8588d5 100644 --- a/remoting/host/daemon_process_win.cc +++ b/remoting/host/daemon_process_win.cc @@ -4,23 +4,96 @@ #include "remoting/host/daemon_process.h" +#include "base/base_switches.h" +#include "base/bind.h" +#include "base/bind_helpers.h" #include "base/logging.h" +#include "base/path_service.h" #include "base/single_thread_task_runner.h" +#include "base/time.h" +#include "base/timer.h" +#include "base/utf_string_conversions.h" #include "base/win/scoped_handle.h" +#include "remoting/host/constants.h" +#include "remoting/host/win/launch_process_with_token.h" +#include "remoting/host/win/worker_process_launcher.h" + +using base::win::ScopedHandle; +using base::TimeDelta; + +namespace { + +// The minimum and maximum delays between attempts to launch the networking +// process. +const int kMaxLaunchDelaySeconds = 60; +const int kMinLaunchDelaySeconds = 1; + +const FilePath::CharType kMe2meHostBinaryName[] = + FILE_PATH_LITERAL("remoting_me2me_host.exe"); + +// The IPC channel name is passed to the networking process in the command line. +const char kDaemonPipeSwitchName[] = "daemon-pipe"; + +// The command line parameters that should be copied from the service's command +// line to the network process. +const char* kCopiedSwitchNames[] = { + "host-config", switches::kV, switches::kVModule }; + +// The security descriptor of the daemon IPC endpoint. It gives full access +// to LocalSystem and denies access by anyone else. +const char kDaemonPipeSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; + +} // namespace namespace remoting { -class DaemonProcessWin : public DaemonProcess { +class DaemonProcessWin : public DaemonProcess, + public WorkerProcessLauncher::Delegate { public: DaemonProcessWin(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, const base::Closure& stopped_callback); virtual ~DaemonProcessWin(); + virtual void OnChannelConnected() OVERRIDE; + + // Sends an IPC message to the worker process. This method can be called only + // after successful Start() and until Stop() is called or an error occurred. + virtual void Send(IPC::Message* message) OVERRIDE; + + // WorkerProcessLauncher::Delegate implementation. + virtual bool DoLaunchProcess( + const std::string& channel_name, + ScopedHandle* process_exit_event_out) OVERRIDE; + virtual void DoKillProcess(DWORD exit_code) OVERRIDE; + + protected: + // Stoppable implementation. + virtual void DoStop() OVERRIDE; + // DaemonProcess implementation. - virtual bool LaunchNetworkProcess() OVERRIDE; + virtual void LaunchNetworkProcess() OVERRIDE; private: + // Called when the launcher reports the worker process has stopped. + void OnLauncherStopped(); + + // True if the network process is connected to the daemon. + bool connected_; + + // Time of the last launch attempt. + base::Time launch_time_; + + // Current backoff delay. + base::TimeDelta launch_backoff_; + + // Timer used to schedule the next attempt to launch the process. + base::OneShotTimer<DaemonProcessWin> timer_; + + scoped_ptr<WorkerProcessLauncher> launcher_; + + ScopedHandle network_process_; + DISALLOW_COPY_AND_ASSIGN(DaemonProcessWin); }; @@ -28,15 +101,176 @@ DaemonProcessWin::DaemonProcessWin( scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> io_task_runner, const base::Closure& stopped_callback) - : DaemonProcess(main_task_runner, io_task_runner, stopped_callback) { + : DaemonProcess(main_task_runner, io_task_runner, stopped_callback), + connected_(false) { } DaemonProcessWin::~DaemonProcessWin() { + // Make sure that the object is completely stopped. The same check exists + // in Stoppable::~Stoppable() but this one helps us to fail early and + // predictably. + CHECK_EQ(stoppable_state(), Stoppable::kStopped); } -bool DaemonProcessWin::LaunchNetworkProcess() { - NOTIMPLEMENTED(); - return false; +void DaemonProcessWin::LaunchNetworkProcess() { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + DCHECK(launcher_.get() == NULL); + DCHECK(!network_process_.IsValid()); + DCHECK(!timer_.IsRunning()); + + launch_time_ = base::Time::Now(); + launcher_.reset(new WorkerProcessLauncher( + this, this, + base::Bind(&DaemonProcessWin::OnLauncherStopped, base::Unretained(this)), + main_task_runner(), + io_task_runner())); + launcher_->Start(kDaemonPipeSecurityDescriptor); +} + +void DaemonProcessWin::OnChannelConnected() { + connected_ = true; + DaemonProcess::OnChannelConnected(); +} + +void DaemonProcessWin::Send(IPC::Message* message) { + if (connected_) { + launcher_->Send(message); + } else { + delete message; + } +} + +bool DaemonProcessWin::DoLaunchProcess( + const std::string& channel_name, + ScopedHandle* process_exit_event_out) { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + DCHECK(!network_process_.IsValid()); + + // Construct the host binary name. + FilePath dir_path; + if (!PathService::Get(base::DIR_EXE, &dir_path)) { + LOG(ERROR) << "Failed to get the executable file name."; + return false; + } + FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); + + // Create the host process command line, passing the name of the IPC channel + // to use and copying known switches from the service's command line. + CommandLine command_line(host_binary); + command_line.AppendSwitchNative(kDaemonPipeSwitchName, + UTF8ToWide(channel_name)); + command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), + kCopiedSwitchNames, + _countof(kCopiedSwitchNames)); + + ScopedHandle token; + if (!OpenProcessToken(GetCurrentProcess(), + MAXIMUM_ALLOWED, + token.Receive())) { + LOG_GETLASTERROR(FATAL) << "Failed to open process token"; + return false; + } + + // Try to launch the process and attach an object watcher to the returned + // handle so that we get notified when the process terminates. + // TODO(alexeypa): Pass a restricted process token. + // See http://crbug.com/134694. + ScopedHandle worker_thread; + if (!LaunchProcessWithToken(host_binary, + command_line.GetCommandLineString(), + token, + 0, + &network_process_, + &worker_thread)) { + return false; + } + + ScopedHandle process_exit_event; + if (!DuplicateHandle(GetCurrentProcess(), + network_process_, + GetCurrentProcess(), + process_exit_event.Receive(), + SYNCHRONIZE, + FALSE, + 0)) { + LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; + DoKillProcess(CONTROL_C_EXIT); + return false; + } + + *process_exit_event_out = process_exit_event.Pass(); + return true; +} + +void DaemonProcessWin::DoKillProcess(DWORD exit_code) { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + CHECK(network_process_.IsValid()); + + TerminateProcess(network_process_, exit_code); +} + +void DaemonProcessWin::DoStop() { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + + timer_.Stop(); + + if (launcher_.get() != NULL) { + launcher_->Stop(); + } + + // Early exit if we're still waiting for |launcher_| to stop. + if (launcher_.get() != NULL) { + return; + } + + DaemonProcess::DoStop(); +} + +void DaemonProcessWin::OnLauncherStopped() { + DCHECK(main_task_runner()->BelongsToCurrentThread()); + CHECK(network_process_.IsValid()); + + DWORD exit_code = CONTROL_C_EXIT; + if (!::GetExitCodeProcess(network_process_, &exit_code)) { + LOG_GETLASTERROR(INFO) + << "Failed to query the exit code of the worker process"; + exit_code = CONTROL_C_EXIT; + } + + network_process_.Close(); + connected_ = false; + launcher_.reset(NULL); + + // Do not relaunch the network process if the caller has asked us to stop. + if (stoppable_state() != Stoppable::kRunning) { + Stop(); + return; + } + + // Stop trying to restart the worker process if its process exited due to + // misconfiguration. + if (kMinPermanentErrorExitCode <= exit_code && + exit_code <= kMaxPermanentErrorExitCode) { + Stop(); + return; + } + + // Expand the backoff interval if the process has died quickly or reset it + // if it was up longer than the maximum backoff delay. + base::TimeDelta delta = base::Time::Now() - launch_time_; + if (delta < base::TimeDelta() || + delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) { + launch_backoff_ = base::TimeDelta(); + } else { + launch_backoff_ = std::max( + launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); + launch_backoff_ = std::min( + launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); + } + + // Try to launch the worker process. + timer_.Start(FROM_HERE, launch_backoff_, + this, &DaemonProcessWin::LaunchNetworkProcess); } scoped_ptr<DaemonProcess> DaemonProcess::Create( diff --git a/remoting/host/json_host_config.cc b/remoting/host/json_host_config.cc index 1f22adf..a73e574 100644 --- a/remoting/host/json_host_config.cc +++ b/remoting/host/json_host_config.cc @@ -30,16 +30,7 @@ bool JsonHostConfig::Read() { return false; } - scoped_ptr<Value> value( - base::JSONReader::Read(file_content, base::JSON_ALLOW_TRAILING_COMMAS)); - if (value.get() == NULL || !value->IsType(Value::TYPE_DICTIONARY)) { - LOG(WARNING) << "Failed to parse " << filename_.value(); - return false; - } - - DictionaryValue* dictionary = static_cast<DictionaryValue*>(value.release()); - values_.reset(dictionary); - return true; + return SetSerializedData(file_content); } bool JsonHostConfig::Save() { @@ -58,4 +49,17 @@ std::string JsonHostConfig::GetSerializedData() { return data; } +bool JsonHostConfig::SetSerializedData(const std::string& config) { + scoped_ptr<Value> value( + base::JSONReader::Read(config, base::JSON_ALLOW_TRAILING_COMMAS)); + if (value.get() == NULL || !value->IsType(Value::TYPE_DICTIONARY)) { + LOG(WARNING) << "Failed to parse " << filename_.value(); + return false; + } + + DictionaryValue* dictionary = static_cast<DictionaryValue*>(value.release()); + values_.reset(dictionary); + return true; +} + } // namespace remoting diff --git a/remoting/host/json_host_config.h b/remoting/host/json_host_config.h index 014e148..c0148cc 100644 --- a/remoting/host/json_host_config.h +++ b/remoting/host/json_host_config.h @@ -29,6 +29,10 @@ class JsonHostConfig : public InMemoryHostConfig { std::string GetSerializedData(); + // Sets the configuration from serialized JSON data. Returns false if the data + // could not be parsed. + bool SetSerializedData(const std::string& config); + private: FilePath filename_; diff --git a/remoting/host/me2me_preference_pane.mm b/remoting/host/me2me_preference_pane.mm index bd82396..dcb3593 100644 --- a/remoting/host/me2me_preference_pane.mm +++ b/remoting/host/me2me_preference_pane.mm @@ -524,17 +524,8 @@ std::string JsonHostConfig::GetSerializedData() const { have_new_config_ = NO; - // If the service is running, send a signal to cause it to reload its - // configuration, otherwise start the service. - if (is_service_running_) { - pid_t job_pid = base::mac::PIDForJob(remoting::kServiceName); - if (job_pid > 0) { - kill(job_pid, SIGHUP); - } else { - NSLog(@"Failed to obtain PID of service %s", remoting::kServiceName); - [self showError]; - } - } else { + // Ensure the service is started. + if (!is_service_running_) { [self sendJobControlMessage:LAUNCH_KEY_STARTJOB]; } diff --git a/remoting/host/remoting_me2me_host.cc b/remoting/host/remoting_me2me_host.cc index 192cfe2..65daa12 100644 --- a/remoting/host/remoting_me2me_host.cc +++ b/remoting/host/remoting_me2me_host.cc @@ -12,7 +12,6 @@ #include "base/command_line.h" #include "base/file_path.h" #include "base/file_util.h" -#include "base/files/file_path_watcher.h" #include "base/logging.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" @@ -34,8 +33,9 @@ #include "remoting/host/branding.h" #include "remoting/host/chromoting_host.h" #include "remoting/host/chromoting_host_context.h" -#include "remoting/host/composite_host_config.h" +#include "remoting/host/chromoting_messages.h" #include "remoting/host/constants.h" +#include "remoting/host/config_file_watcher.h" #include "remoting/host/desktop_environment.h" #include "remoting/host/dns_blackhole_checker.h" #include "remoting/host/event_executor.h" @@ -43,6 +43,7 @@ #include "remoting/host/host_config.h" #include "remoting/host/host_event_logger.h" #include "remoting/host/host_user_interface.h" +#include "remoting/host/json_host_config.h" #include "remoting/host/log_to_server.h" #include "remoting/host/network_settings.h" #include "remoting/host/policy_hack/policy_watcher.h" @@ -53,16 +54,16 @@ #include "remoting/jingle_glue/xmpp_signal_strategy.h" #include "remoting/protocol/me2me_host_authenticator_factory.h" -#if defined(OS_POSIX) -#include "remoting/host/posix/sighup_listener.h" -#endif // defined(OS_POSIX) - #if defined(OS_MACOSX) #include "base/mac/scoped_cftyperef.h" #include "base/mac/scoped_nsautorelease_pool.h" #include "remoting/host/curtain_mode_mac.h" #endif // defined(OS_MACOSX) +#if defined(OS_POSIX) +#include <signal.h> +#endif // defined(OS_POSIX) + // N.B. OS_WIN is defined by including src/base headers. #if defined(OS_WIN) #include <commctrl.h> @@ -77,16 +78,8 @@ namespace { // This is used for tagging system event logs. const char kApplicationName[] = "chromoting"; -// The command line switch specifying the name of the Chromoting IPC channel. -const char kDaemonIpcSwitchName[] = "chromoting-ipc"; - -// These are used for parsing the config-file locations from the command line, -// and for defining the default locations if the switches are not present. -const char kAuthConfigSwitchName[] = "auth-config"; -const char kHostConfigSwitchName[] = "host-config"; - -const FilePath::CharType kDefaultHostConfigFile[] = - FILE_PATH_LITERAL("host.json"); +// The command line switch specifying the name of the daemon IPC endpoint. +const char kDaemonIpcSwitchName[] = "daemon-pipe"; const char kUnofficialOAuth2ClientId[] = "440925447803-2pi3v45bff6tp1rde2f7q6lgbor3o5uj.apps.googleusercontent.com"; @@ -106,10 +99,12 @@ namespace remoting { class HostProcess : public HeartbeatSender::Listener, - public IPC::Listener { + public IPC::Listener, + public ConfigFileWatcher::Delegate { public: HostProcess(scoped_ptr<ChromotingHostContext> context) : context_(context.Pass()), + config_(FilePath()), #ifdef OFFICIAL_BUILD oauth_use_official_client_id_(true), #else @@ -127,9 +122,6 @@ class HostProcess #endif { network_change_notifier_.reset(net::NetworkChangeNotifier::Create()); - config_updated_timer_.reset(new base::DelayTimer<HostProcess>( - FROM_HERE, base::TimeDelta::FromSeconds(2), this, - &HostProcess::ConfigUpdatedDelayed)); } bool InitWithCommandLine(const CommandLine* cmd_line) { @@ -148,85 +140,93 @@ class HostProcess context_->network_task_runner())); } +#if !defined(REMOTING_MULTI_PROCESS) FilePath default_config_dir = remoting::GetConfigDir(); - if (cmd_line->HasSwitch(kAuthConfigSwitchName)) { - FilePath path = cmd_line->GetSwitchValuePath(kAuthConfigSwitchName); - config_.AddConfigPath(path); - } - host_config_path_ = default_config_dir.Append(kDefaultHostConfigFile); if (cmd_line->HasSwitch(kHostConfigSwitchName)) { host_config_path_ = cmd_line->GetSwitchValuePath(kHostConfigSwitchName); } - config_.AddConfigPath(host_config_path_); +#endif // !defined(REMOTING_MULTI_PROCESS) return true; } - void ConfigUpdated() { + virtual void OnConfigUpdated(const std::string& serialized_config) OVERRIDE { DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); - // Call ConfigUpdatedDelayed after a short delay, so that this object won't - // try to read the updated configuration file before it has been - // completely written. - // If the writer moves the new configuration file into place atomically, - // this delay may not be necessary. - config_updated_timer_->Reset(); - } + LOG(INFO) << "Processing new host configuration."; - void ConfigUpdatedDelayed() { - DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); + if (!config_.SetSerializedData(serialized_config)) { + LOG(ERROR) << "Invalid configuration."; + OnConfigWatcherError(); + return; + } + + if (!ApplyConfig()) { + LOG(ERROR) << "Failed to apply the configuration."; + OnConfigWatcherError(); + return; + } - if (LoadConfig()) { + // Start watching the policy (and eventually start the host) if this is + // the first configuration update. Otherwise, post a task to create new + // authenticator factory in case PIN has changed. + if (policy_watcher_.get() == NULL) { +#if defined(OS_MACOSX) || defined(OS_WIN) + bool want_user_interface = true; + +#if defined(OS_MACOSX) + // Don't try to display any UI on top of the system's login screen as this + // is rejected by the Window Server on OS X 10.7.4, and prevents the + // capturer from working (http://crbug.com/140984). + base::mac::ScopedCFTypeRef<CFDictionaryRef> session( + CGSessionCopyCurrentDictionary()); + const void* logged_in = CFDictionaryGetValue(session, + kCGSessionLoginDoneKey); + if (logged_in != kCFBooleanTrue) { + want_user_interface = false; + } +#endif // OS_MACOSX + + if (want_user_interface) { + host_user_interface_.reset(new HostUserInterface(context_.get())); + } +#endif // OS_MACOSX || OS_WIN + + StartWatchingPolicy(); + } else { // PostTask to create new authenticator factory in case PIN has changed. context_->network_task_runner()->PostTask( FROM_HERE, base::Bind(&HostProcess::CreateAuthenticatorFactory, base::Unretained(this))); - } else { - LOG(ERROR) << "Invalid configuration."; } } -#if defined(OS_WIN) - class ConfigChangedDelegate : public base::files::FilePathWatcher::Delegate { - public: - ConfigChangedDelegate( - scoped_refptr<base::SingleThreadTaskRunner> task_runner, - const base::Closure& callback) - : task_runner_(task_runner), - callback_(callback) { - } - - void OnFilePathChanged(const FilePath& path) OVERRIDE { - task_runner_->PostTask(FROM_HERE, callback_); - } - - void OnFilePathError(const FilePath& path) OVERRIDE { - } + virtual void OnConfigWatcherError() OVERRIDE { + DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); - private: - scoped_refptr<base::SingleThreadTaskRunner> task_runner_; - base::Closure callback_; + context_->network_task_runner()->PostTask( + FROM_HERE, + base::Bind(&HostProcess::Shutdown, base::Unretained(this), + kInvalidHostConfigurationExitCode)); + } - DISALLOW_COPY_AND_ASSIGN(ConfigChangedDelegate); - }; -#endif // defined(OS_WIN) + void StartWatchingConfigChanges() { +#if !defined(REMOTING_MULTI_PROCESS) - void ListenForConfigChanges() { #if defined(OS_POSIX) - remoting::RegisterHupSignalHandler( - base::Bind(&HostProcess::ConfigUpdatedDelayed, base::Unretained(this))); -#elif defined(OS_WIN) - scoped_refptr<base::files::FilePathWatcher::Delegate> delegate( - new ConfigChangedDelegate( - context_->ui_task_runner(), - base::Bind(&HostProcess::ConfigUpdated, base::Unretained(this)))); - config_watcher_.reset(new base::files::FilePathWatcher()); - if (!config_watcher_->Watch(host_config_path_, delegate)) { - LOG(ERROR) << "Couldn't watch file " << host_config_path_.value(); - } -#endif // defined (OS_WIN) + // Ignore SIGHUP sent by the daemon controller since we use + // |ConfigFileWatcher| instead. + signal(SIGHUP, SIG_IGN); +#endif // defined(OS_POSIX) + + // Start watching the host configuration file. + config_watcher_.reset(new ConfigFileWatcher(context_->ui_task_runner(), + context_->file_task_runner(), + this)); + config_watcher_->Watch(host_config_path_); +#endif // !defined(REMOTING_MULTI_PROCESS) } void CreateAuthenticatorFactory() { @@ -240,50 +240,30 @@ class HostProcess // IPC::Listener implementation. virtual bool OnMessageReceived(const IPC::Message& message) { + DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); + +#if defined(REMOTING_MULTI_PROCESS) + bool handled = true; + IPC_BEGIN_MESSAGE_MAP(HostProcess, message) + IPC_MESSAGE_HANDLER(ChromotingDaemonNetworkMsg_Configuration, + OnConfigUpdated) + IPC_MESSAGE_UNHANDLED(handled = false) + IPC_END_MESSAGE_MAP() + return handled; +#else // !defined(REMOTING_MULTI_PROCESS) return false; +#endif // !defined(REMOTING_MULTI_PROCESS) } void StartHostProcess() { DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); - if (!InitWithCommandLine(CommandLine::ForCurrentProcess()) || - !LoadConfig()) { - context_->network_task_runner()->PostTask( - FROM_HERE, - base::Bind(&HostProcess::Shutdown, base::Unretained(this), - kInvalidHostConfigurationExitCode)); + if (!InitWithCommandLine(CommandLine::ForCurrentProcess())) { + OnConfigWatcherError(); return; } -#if defined(OS_MACOSX) || defined(OS_WIN) - bool want_user_interface = true; - -#if defined(OS_MACOSX) - // Don't try to display any UI on top of the system's login screen as this - // is rejected by the Window Server on OS X 10.7.4, and prevents the - // capturer from working (http://crbug.com/140984). - base::mac::ScopedCFTypeRef<CFDictionaryRef> session( - CGSessionCopyCurrentDictionary()); - const void* logged_in = CFDictionaryGetValue(session, - kCGSessionLoginDoneKey); - if (logged_in != kCFBooleanTrue) { - want_user_interface = false; - } -#endif // OS_MACOSX - - if (want_user_interface) { - host_user_interface_.reset(new HostUserInterface(context_.get())); - } -#endif // OS_MACOSX || OS_WIN - - StartWatchingPolicy(); - -#if defined(OS_MACOSX) || defined(OS_WIN) - context_->file_task_runner()->PostTask( - FROM_HERE, - base::Bind(&HostProcess::ListenForConfigChanges, - base::Unretained(this))); -#endif + StartWatchingConfigChanges(); } int get_exit_code() const { return exit_code_; } @@ -292,6 +272,10 @@ class HostProcess void ShutdownHostProcess() { DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); +#if !defined(REMOTING_MULTI_PROCESS) + config_watcher_.reset(); +#endif // !defined(REMOTING_MULTI_PROCESS) + daemon_channel_.reset(); #if defined(OS_MACOSX) || defined(OS_WIN) @@ -321,19 +305,10 @@ class HostProcess base::Bind(&HostProcess::OnPolicyUpdate, base::Unretained(this))); } - // Read host config, returning true if successful. - bool LoadConfig() { + // Applies the host config, returning true if successful. + bool ApplyConfig() { DCHECK(context_->ui_task_runner()->BelongsToCurrentThread()); - // TODO(sergeyu): There is a potential race condition: this function is - // called on the main thread while the class members it mutates are used on - // the network thread. Fix it. http://crbug.com/140986 . - - if (!config_.Read()) { - LOG(ERROR) << "Failed to read config file."; - return false; - } - if (!config_.GetString(kHostIdConfigPath, &host_id_)) { LOG(ERROR) << "host_id is not defined in the config."; return false; @@ -666,8 +641,11 @@ class HostProcess scoped_ptr<IPC::ChannelProxy> daemon_channel_; scoped_ptr<net::NetworkChangeNotifier> network_change_notifier_; + JsonHostConfig config_; +#if !defined(REMOTING_MULTI_PROCESS) FilePath host_config_path_; - CompositeHostConfig config_; + scoped_ptr<ConfigFileWatcher> config_watcher_; +#endif // !defined(REMOTING_MULTI_PROCESS) std::string host_id_; HostKeyPair key_pair_; @@ -683,9 +661,6 @@ class HostProcess bool allow_nat_traversal_; std::string talkgadget_prefix_; - scoped_ptr<base::files::FilePathWatcher> config_watcher_; - scoped_ptr<base::DelayTimer<HostProcess> > config_updated_timer_; - bool restarting_; bool shutting_down_; @@ -763,13 +738,9 @@ int main(int argc, char** argv) { if (!context->Start()) return remoting::kHostInitializationFailed; - // Create the host process instance and run the rest of the initialization on - // the main message loop. + // Create the host process instance and enter the main message loop. remoting::HostProcess me2me_host(context.Pass()); - message_loop.PostTask( - FROM_HERE, - base::Bind(&remoting::HostProcess::StartHostProcess, - base::Unretained(&me2me_host))); + me2me_host.StartHostProcess(); message_loop.Run(); return me2me_host.get_exit_code(); } diff --git a/remoting/host/win/host_service.cc b/remoting/host/win/host_service.cc index 92dda1d..b882d52 100644 --- a/remoting/host/win/host_service.cc +++ b/remoting/host/win/host_service.cc @@ -80,8 +80,7 @@ const wchar_t kUsageMessage[] = // The command line parameters that should be copied from the service's command // line when launching an elevated child. const char* kCopiedSwitchNames[] = { - "auth-config", "host-config", "chromoting-ipc", switches::kV, - switches::kVModule }; + "host-config", "daemon-pipe", switches::kV, switches::kVModule }; // Exit codes: const int kSuccessExitCode = 0; diff --git a/remoting/host/win/worker_process_launcher.cc b/remoting/host/win/worker_process_launcher.cc index 9f5f35a..834b8bb 100644 --- a/remoting/host/win/worker_process_launcher.cc +++ b/remoting/host/win/worker_process_launcher.cc @@ -21,6 +21,7 @@ #include "base/win/scoped_handle.h" #include "ipc/ipc_channel_proxy.h" #include "ipc/ipc_message.h" +#include "remoting/host/worker_process_ipc_delegate.h" using base::win::ScopedHandle; @@ -38,12 +39,14 @@ WorkerProcessLauncher::Delegate::~Delegate() { } WorkerProcessLauncher::WorkerProcessLauncher( - Delegate* delegate, + Delegate* launcher_delegate, + WorkerProcessIpcDelegate* worker_delegate, const base::Closure& stopped_callback, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner) : Stoppable(main_task_runner, stopped_callback), - delegate_(delegate), + launcher_delegate_(launcher_delegate), + worker_delegate_(worker_delegate), main_task_runner_(main_task_runner), ipc_task_runner_(ipc_task_runner) { } @@ -70,12 +73,13 @@ void WorkerProcessLauncher::Start(const std::string& pipe_sddl) { // Launch the process and attach an object watcher to the returned process // handle so that we get notified if the process terminates. - if (delegate_->DoLaunchProcess(channel_name, &process_exit_event_)) { + if (launcher_delegate_->DoLaunchProcess(channel_name, + &process_exit_event_)) { if (process_watcher_.StartWatching(process_exit_event_, this)) { return; } - delegate_->DoKillProcess(CONTROL_C_EXIT); + launcher_delegate_->DoKillProcess(CONTROL_C_EXIT); } } @@ -85,7 +89,11 @@ void WorkerProcessLauncher::Start(const std::string& pipe_sddl) { void WorkerProcessLauncher::Send(IPC::Message* message) { DCHECK(main_task_runner_->BelongsToCurrentThread()); - ipc_channel_->Send(message); + if (ipc_channel_.get()) { + ipc_channel_->Send(message); + } else { + delete message; + } } void WorkerProcessLauncher::OnObjectSignaled(HANDLE object) { @@ -101,7 +109,7 @@ bool WorkerProcessLauncher::OnMessageReceived(const IPC::Message& message) { DCHECK(pipe_.IsValid()); DCHECK(process_exit_event_.IsValid()); - return delegate_->OnMessageReceived(message); + return worker_delegate_->OnMessageReceived(message); } void WorkerProcessLauncher::OnChannelConnected(int32 peer_pid) { @@ -118,7 +126,7 @@ void WorkerProcessLauncher::OnChannelConnected(int32 peer_pid) { // If we'd like to be able to launch low-privileged workers and let them // connect back, the pipe handle should be passed to the worker instead of // the pipe name. - delegate_->OnChannelConnected(); + worker_delegate_->OnChannelConnected(); } void WorkerProcessLauncher::OnChannelError() { @@ -144,7 +152,7 @@ void WorkerProcessLauncher::DoStop() { // Kill the process if it has been started already. if (process_watcher_.GetWatchedObject() != NULL) { - delegate_->DoKillProcess(CONTROL_C_EXIT); + launcher_delegate_->DoKillProcess(CONTROL_C_EXIT); return; } diff --git a/remoting/host/win/worker_process_launcher.h b/remoting/host/win/worker_process_launcher.h index 1d47022..d53f860 100644 --- a/remoting/host/win/worker_process_launcher.h +++ b/remoting/host/win/worker_process_launcher.h @@ -29,6 +29,8 @@ class Message; namespace remoting { +class WorkerProcessIpcDelegate; + // Launches a worker process that is controlled via an IPC channel. All // interaction with the spawned process is through the IPC::Listener and Send() // method. In case of error the channel is closed and the worker process is @@ -53,25 +55,18 @@ class WorkerProcessLauncher // Terminates the worker process with the given exit code. virtual void DoKillProcess(DWORD exit_code) = 0; - - // Notifies that a client has been connected to the channel. - virtual void OnChannelConnected() = 0; - - // Processes messages sent by the client. - virtual bool OnMessageReceived(const IPC::Message& message) = 0; }; - // Creates the launcher. - // |delegate| will be able to receive messages sent over the channel once - // the worker has been started and until it is stopped by Stop() or an error - // occurs. + // Creates the launcher that will use |launcher_delegate| to manage the worker + // process and |worker_delegate| to handle IPCs. // // |stopped_callback| and |main_task_runner| are passed to the underlying // |Stoppable| implementation. The caller should call all the methods on this // class on the |main_task_runner| thread. |ipc_task_runner| is used to // perform background IPC I/O. WorkerProcessLauncher( - Delegate* delegate, + Delegate* launcher_delegate, + WorkerProcessIpcDelegate* worker_delegate, const base::Closure& stopped_callback, scoped_refptr<base::SingleThreadTaskRunner> main_task_runner, scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner); @@ -80,8 +75,9 @@ class WorkerProcessLauncher // Starts the worker process. void Start(const std::string& pipe_sddl); - // Sends an IPC message to the worker process. This method can be called only - // after successful Start() and until Stop() is called or an error occurred. + // Sends an IPC message to the worker process. The message will be silently + // dropped if Send() is called before Start() or after stutdown has been + // initiated. void Send(IPC::Message* message); // base::win::ObjectWatcher::Delegate implementation. @@ -105,7 +101,8 @@ class WorkerProcessLauncher // Generates random channel ID. std::string GenerateRandomChannelId(); - Delegate* delegate_; + Delegate* launcher_delegate_; + WorkerProcessIpcDelegate* worker_delegate_; // The main service message loop. scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; diff --git a/remoting/host/win/wts_session_process_launcher.cc b/remoting/host/win/wts_session_process_launcher.cc index cf5a908..d659f15 100644 --- a/remoting/host/win/wts_session_process_launcher.cc +++ b/remoting/host/win/wts_session_process_launcher.cc @@ -20,7 +20,6 @@ #include "base/logging.h" #include "base/single_thread_task_runner.h" #include "base/path_service.h" -#include "base/process_util.h" #include "base/rand_util.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" @@ -49,19 +48,19 @@ const FilePath::CharType kMe2meHostBinaryName[] = const FilePath::CharType kMe2meServiceBinaryName[] = FILE_PATH_LITERAL("remoting_service.exe"); -// The IPC channel name is passed to the host in the command line. -const char kChromotingIpcSwitchName[] = "chromoting-ipc"; +// The command line switch specifying the name of the daemon IPC endpoint. +const char kDaemonIpcSwitchName[] = "daemon-pipe"; const char kElevateSwitchName[] = "elevate"; // The command line parameters that should be copied from the service's command // line to the host process. const char* kCopiedSwitchNames[] = { - "auth-config", "host-config", switches::kV, switches::kVModule }; + "host-config", switches::kV, switches::kVModule }; -// The security descriptor of the Chromoting IPC channel. It gives full access +// The security descriptor of the daemon IPC endpoint. It gives full access // to LocalSystem and denies access by anyone else. -const char kChromotingChannelSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; +const char kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; } // namespace @@ -94,8 +93,8 @@ WtsSessionProcessLauncher::WtsSessionProcessLauncher( WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { // Make sure that the object is completely stopped. The same check exists - // in Stoppable::~Stoppable() but this one allows us to examine the state of - // the object before destruction. + // in Stoppable::~Stoppable() but this one helps us to fail early and + // predictably. CHECK_EQ(stoppable_state(), Stoppable::kStopped); monitor_->RemoveWtsConsoleObserver(this); @@ -141,7 +140,7 @@ bool WtsSessionProcessLauncher::DoLaunchProcess( // to use and copying known switches from the service's command line. CommandLine command_line(service_binary); command_line.AppendSwitchPath(kElevateSwitchName, host_binary); - command_line.AppendSwitchNative(kChromotingIpcSwitchName, + command_line.AppendSwitchNative(kDaemonIpcSwitchName, UTF8ToWide(channel_name)); command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), kCopiedSwitchNames, @@ -375,12 +374,12 @@ void WtsSessionProcessLauncher::LaunchProcess() { launch_time_ = base::Time::Now(); launcher_.reset(new WorkerProcessLauncher( - this, + this, this, base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, base::Unretained(this)), main_message_loop_, ipc_message_loop_)); - launcher_->Start(kChromotingChannelSecurityDescriptor); + launcher_->Start(kDaemonIpcSecurityDescriptor); } void WtsSessionProcessLauncher::OnLauncherStopped() { diff --git a/remoting/host/win/wts_session_process_launcher.h b/remoting/host/win/wts_session_process_launcher.h index e522748..097e588 100644 --- a/remoting/host/win/wts_session_process_launcher.h +++ b/remoting/host/win/wts_session_process_launcher.h @@ -9,6 +9,8 @@ #include "base/basictypes.h" #include "base/compiler_specific.h" +#include "base/file_path.h" +#include "base/file_util.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/message_loop.h" @@ -20,16 +22,12 @@ #include "remoting/base/stoppable.h" #include "remoting/host/win/worker_process_launcher.h" #include "remoting/host/win/wts_console_observer.h" +#include "remoting/host/worker_process_ipc_delegate.h" namespace base { class SingleThreadTaskRunner; } // namespace base -namespace IPC { -class ChannelProxy; -class Message; -} // namespace IPC - namespace remoting { class WtsConsoleMonitor; @@ -37,6 +35,7 @@ class WtsConsoleMonitor; class WtsSessionProcessLauncher : public base::MessagePumpForIO::IOHandler, public Stoppable, + public WorkerProcessIpcDelegate, public WorkerProcessLauncher::Delegate, public WtsConsoleObserver { public: @@ -57,13 +56,15 @@ class WtsSessionProcessLauncher DWORD bytes_transferred, DWORD error) OVERRIDE; + // WorkerProcessIpcDelegate implementation. + virtual void OnChannelConnected() OVERRIDE; + virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; + // WorkerProcessLauncher::Delegate implementation. virtual bool DoLaunchProcess( const std::string& channel_name, base::win::ScopedHandle* process_exit_event_out) OVERRIDE; virtual void DoKillProcess(DWORD exit_code) OVERRIDE; - virtual void OnChannelConnected() OVERRIDE; - virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE; // WtsConsoleObserver implementation. virtual void OnSessionAttached(uint32 session_id) OVERRIDE; diff --git a/remoting/host/worker_process_ipc_delegate.h b/remoting/host/worker_process_ipc_delegate.h new file mode 100644 index 0000000..6a8ff58 --- /dev/null +++ b/remoting/host/worker_process_ipc_delegate.h @@ -0,0 +1,32 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef REMOTING_HOST_WORKER_PROCESS_IPC_DELEGATE_H_ +#define REMOTING_HOST_WORKER_PROCESS_IPC_DELEGATE_H_ + +#include "base/basictypes.h" +#include "base/compiler_specific.h" + +namespace IPC { +class Message; +} // namespace IPC + +namespace remoting { + +// An interface representing the object receiving IPC messages from a worker +// process. +class WorkerProcessIpcDelegate { + public: + virtual ~WorkerProcessIpcDelegate() {} + + // Notifies that a client has been connected to the channel. + virtual void OnChannelConnected() = 0; + + // Processes messages sent by the client. + virtual bool OnMessageReceived(const IPC::Message& message) = 0; +}; + +} // namespace remoting + +#endif // REMOTING_HOST_WORKER_PROCESS_IPC_DELEGATE_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 4b7feb0..7949102 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -593,6 +593,8 @@ 'host/branding.h', 'host/chromoting_messages.cc', 'host/chromoting_messages.h', + 'host/config_file_watcher.cc', + 'host/config_file_watcher.h', 'host/constants.h', 'host/constants_win.cc', 'host/daemon_process.cc', @@ -612,6 +614,7 @@ 'host/win/wts_console_observer.h', 'host/win/wts_session_process_launcher.cc', 'host/win/wts_session_process_launcher.h', + 'host/worker_process_ipc_delegate.h', ], 'msvs_settings': { 'VCLinkerTool': { @@ -1209,8 +1212,6 @@ 'host/clipboard_linux.cc', 'host/clipboard_mac.mm', 'host/clipboard_win.cc', - 'host/composite_host_config.cc', - 'host/composite_host_config.h', 'host/constants.h', 'host/constants_mac.cc', 'host/constants_mac.h', @@ -1446,13 +1447,13 @@ 'sources': [ 'host/branding.cc', 'host/branding.h', - 'host/posix/sighup_listener.cc', - 'host/posix/sighup_listener.h', + 'host/config_file_watcher.cc', + 'host/config_file_watcher.h', + 'host/curtain_mode_mac.h', + 'host/curtain_mode_mac.cc', 'host/remoting_me2me_host.cc', 'host/usage_stats_consent.h', 'host/usage_stats_consent_win.cc', - 'host/curtain_mode_mac.h', - 'host/curtain_mode_mac.cc', ], 'conditions': [ ['os_posix != 1', { |