summaryrefslogtreecommitdiffstats
path: root/remoting
diff options
context:
space:
mode:
authoralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-10 19:00:06 +0000
committeralexeypa@chromium.org <alexeypa@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-10 19:00:06 +0000
commit9733976130e9df953c41469be9cbfcbdd4667887 (patch)
treeb37e7ab428f5edb9e14108cbe589f1051ddf465e /remoting
parent51dc5a56a725a1aa13d8d87d9be27238d644445d (diff)
downloadchromium_src-9733976130e9df953c41469be9cbfcbdd4667887.zip
chromium_src-9733976130e9df953c41469be9cbfcbdd4667887.tar.gz
chromium_src-9733976130e9df953c41469be9cbfcbdd4667887.tar.bz2
[Chromoting] Reimplement the worker process launcher to address issues with job control and launching of elevated processes.
This CL makes three changes: 1. We create a new job object every time we launch a worker into a new session, under Vista and above. This addresses the limitation that a job object used with processes from one session cannot later be re-used to manage processes from a different session. 2. We launch and manage worker processes directly on XP/W2K3, without a job object. We don't need a job object since these platforms don't support/need elevation anyway. This addresses the limitation that processes created via CreateRemoteSessionProcess() cannot be assigned to a job object. 3. We allow WorkerProcessLauncher instances to be dropped synchronously from the caller's perspective; the class takes care of asynchronous tear-down tasks transparently. This addresses a race condition in tear-down of old worker processes and creation of new ones arising from delayed session change notifications. The new layout assigns the following responsibilities: - WorkerProcessLauncher is used to manage a worker process, taking care of setting up the IPC channel, and re-launching it w/ exponential back-off. It supports fire-and-forget tear-down. - WorkerProcessLauncher defers launch & termination to a caller-supplied Delegate, responsible for launching the process as the caller requires. - Our current "single-process" service launches the host process via WtsConsoleSessionProcessDriver, which tracks which session is currently attached to the console and uses WtsSessionProcessDelegate instances to have WorkerProcessLauncher re-launch the host into the new console session whenever it is switched. WtsSessionProcessDelegate takes care of OS-version-specific process management tasks. - Our experimental "multi-process" service launches the host's network process, undependent of session lifetimes, by passing an UnprivilegedProcessDelegate to WorkerProcessLauncher. This launches the host process in the same session as the caller. BUG=153005, 148781, 149098 TEST=remoting_unittests.WorkerProcessLauncherTest Review URL: https://chromiumcodereview.appspot.com/11040065 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@161140 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'remoting')
-rw-r--r--remoting/host/daemon_process.cc6
-rw-r--r--remoting/host/daemon_process.h1
-rw-r--r--remoting/host/daemon_process_win.cc205
-rw-r--r--remoting/host/win/host_service.cc8
-rw-r--r--remoting/host/win/host_service.h6
-rw-r--r--remoting/host/win/launch_process_with_token.cc5
-rw-r--r--remoting/host/win/unprivileged_process_delegate.cc117
-rw-r--r--remoting/host/win/unprivileged_process_delegate.h55
-rw-r--r--remoting/host/win/worker_process_launcher.cc411
-rw-r--r--remoting/host/win/worker_process_launcher.h105
-rw-r--r--remoting/host/win/worker_process_launcher_unittest.cc335
-rw-r--r--remoting/host/win/wts_console_session_process_driver.cc125
-rw-r--r--remoting/host/win/wts_console_session_process_driver.h76
-rw-r--r--remoting/host/win/wts_session_process_delegate.cc457
-rw-r--r--remoting/host/win/wts_session_process_delegate.h51
-rw-r--r--remoting/host/win/wts_session_process_launcher.cc713
-rw-r--r--remoting/host/win/wts_session_process_launcher.h113
-rw-r--r--remoting/host/worker_process_ipc_delegate.h3
-rw-r--r--remoting/remoting.gyp15
19 files changed, 1615 insertions, 1192 deletions
diff --git a/remoting/host/daemon_process.cc b/remoting/host/daemon_process.cc
index 607fca7..720ed4b 100644
--- a/remoting/host/daemon_process.cc
+++ b/remoting/host/daemon_process.cc
@@ -43,6 +43,12 @@ bool DaemonProcess::OnMessageReceived(const IPC::Message& message) {
return false;
}
+void DaemonProcess::OnPermanentError() {
+ DCHECK(main_task_runner()->BelongsToCurrentThread());
+
+ Stop();
+}
+
DaemonProcess::DaemonProcess(
scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
diff --git a/remoting/host/daemon_process.h b/remoting/host/daemon_process.h
index a52be9e..b7bcc55 100644
--- a/remoting/host/daemon_process.h
+++ b/remoting/host/daemon_process.h
@@ -46,6 +46,7 @@ class DaemonProcess
// WorkerProcessIpcDelegate implementation.
virtual void OnChannelConnected() OVERRIDE;
virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnPermanentError() OVERRIDE;
// Sends an IPC message to the network process. The message will be dropped
// unless the network process is connected over the IPC channel.
diff --git a/remoting/host/daemon_process_win.cc b/remoting/host/daemon_process_win.cc
index b299ed1..c29ebb6 100644
--- a/remoting/host/daemon_process_win.cc
+++ b/remoting/host/daemon_process_win.cc
@@ -8,6 +8,8 @@
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/single_thread_task_runner.h"
#include "base/time.h"
@@ -16,6 +18,7 @@
#include "base/win/scoped_handle.h"
#include "remoting/host/host_exit_codes.h"
#include "remoting/host/win/launch_process_with_token.h"
+#include "remoting/host/win/unprivileged_process_delegate.h"
#include "remoting/host/win/worker_process_launcher.h"
using base::win::ScopedHandle;
@@ -23,22 +26,9 @@ 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_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)";
@@ -47,26 +37,17 @@ const char kDaemonPipeSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)";
namespace remoting {
-class DaemonProcessWin : public DaemonProcess,
- public WorkerProcessLauncher::Delegate {
+class DaemonProcessWin : public DaemonProcess {
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;
@@ -75,25 +56,8 @@ class DaemonProcessWin : public DaemonProcess,
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);
};
@@ -101,8 +65,7 @@ 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),
- connected_(false) {
+ : DaemonProcess(main_task_runner, io_task_runner, stopped_callback) {
}
DaemonProcessWin::~DaemonProcessWin() {
@@ -115,162 +78,38 @@ DaemonProcessWin::~DaemonProcessWin() {
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;
+ Stop();
+ return;
}
- *process_exit_event_out = process_exit_event.Pass();
- return true;
+ scoped_ptr<UnprivilegedProcessDelegate> delegate(
+ new UnprivilegedProcessDelegate(main_task_runner(), io_task_runner(),
+ dir_path.Append(kMe2meHostBinaryName)));
+ launcher_.reset(new WorkerProcessLauncher(main_task_runner(),
+ io_task_runner(),
+ delegate.Pass(),
+ this,
+ kDaemonPipeSecurityDescriptor));
}
-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.
+void DaemonProcessWin::Send(IPC::Message* message) {
if (launcher_.get() != NULL) {
- return;
+ launcher_->Send(message);
+ } else {
+ delete message;
}
-
- DaemonProcess::DoStop();
}
-void DaemonProcessWin::OnLauncherStopped() {
+void DaemonProcessWin::DoStop() {
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);
+ launcher_.reset();
+ DaemonProcess::DoStop();
}
scoped_ptr<DaemonProcess> DaemonProcess::Create(
diff --git a/remoting/host/win/host_service.cc b/remoting/host/win/host_service.cc
index b882d52..d8ff0d1 100644
--- a/remoting/host/win/host_service.cc
+++ b/remoting/host/win/host_service.cc
@@ -39,7 +39,7 @@
#include "remoting/host/win/wts_console_observer.h"
#if !defined(REMOTING_MULTI_PROCESS)
-#include "remoting/host/win/wts_session_process_launcher.h"
+#include "remoting/host/win/wts_console_session_process_driver.h"
#endif // !defined(REMOTING_MULTI_PROCESS)
using base::StringPrintf;
@@ -213,8 +213,8 @@ void HostService::CreateLauncher(
#else // !defined(REMOTING_MULTI_PROCESS)
- // Create the session process launcher.
- child_.reset(new WtsSessionProcessLauncher(
+ // Create the console session process driver.
+ child_.reset(new WtsConsoleSessionProcessDriver(
base::Bind(&HostService::OnChildStopped, base::Unretained(this)),
this,
main_task_runner_,
@@ -249,7 +249,7 @@ int HostService::Elevate() {
CommandLine command_line(CommandLine::NO_PROGRAM);
command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
kCopiedSwitchNames,
- _countof(kCopiedSwitchNames));
+ arraysize(kCopiedSwitchNames));
CommandLine::StringType parameters = command_line.GetCommandLineString();
// Launch the child process requesting elevation.
diff --git a/remoting/host/win/host_service.h b/remoting/host/win/host_service.h
index 80bbc90..ce8b479 100644
--- a/remoting/host/win/host_service.h
+++ b/remoting/host/win/host_service.h
@@ -26,12 +26,6 @@ class AutoThreadTaskRunner;
class Stoppable;
class WtsConsoleObserver;
-#if defined(REMOTING_MULTI_PROCESS)
-class DaemonProcess;
-#else // !defined(REMOTING_MULTI_PROCESS)
-class WtsSessionProcessLauncher;
-#endif // !defined(REMOTING_MULTI_PROCESS)
-
class HostService : public WtsConsoleMonitor {
public:
static HostService* GetInstance();
diff --git a/remoting/host/win/launch_process_with_token.cc b/remoting/host/win/launch_process_with_token.cc
index 191f896..db2f40a 100644
--- a/remoting/host/win/launch_process_with_token.cc
+++ b/remoting/host/win/launch_process_with_token.cc
@@ -32,11 +32,6 @@ const WINSTATIONINFOCLASS kCreateProcessPipeNameClass =
const int kPipeBusyWaitTimeoutMs = 2000;
const int kPipeConnectMaxAttempts = 3;
-// The minimum and maximum delays between attempts to inject host process into
-// a session.
-const int kMaxLaunchDelaySeconds = 60;
-const int kMinLaunchDelaySeconds = 1;
-
// Name of the default session desktop.
const char kDefaultDesktopName[] = "winsta0\\default";
diff --git a/remoting/host/win/unprivileged_process_delegate.cc b/remoting/host/win/unprivileged_process_delegate.cc
new file mode 100644
index 0000000..74d539d
--- /dev/null
+++ b/remoting/host/win/unprivileged_process_delegate.cc
@@ -0,0 +1,117 @@
+// 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.
+//
+// This file implements the Windows service controlling Me2Me host processes
+// running within user sessions.
+
+#include "remoting/host/win/unprivileged_process_delegate.h"
+
+#include "base/base_switches.h"
+#include "base/command_line.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/utf_string_conversions.h"
+#include "base/win/scoped_handle.h"
+#include "remoting/host/win/launch_process_with_token.h"
+
+using base::win::ScopedHandle;
+
+namespace {
+
+// The command line switch specifying the name of the daemon IPC endpoint.
+const char kDaemonIpcSwitchName[] = "daemon-pipe";
+
+// The command line parameters that should be copied from the service's command
+// line to the host process.
+const char* kCopiedSwitchNames[] = {
+ "host-config", switches::kV, switches::kVModule };
+
+} // namespace
+
+namespace remoting {
+
+UnprivilegedProcessDelegate::UnprivilegedProcessDelegate(
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ const FilePath& binary_path)
+ : main_task_runner_(main_task_runner),
+ io_task_runner_(io_task_runner),
+ binary_path_(binary_path) {
+}
+
+UnprivilegedProcessDelegate::~UnprivilegedProcessDelegate() {
+ KillProcess(CONTROL_C_EXIT);
+}
+
+DWORD UnprivilegedProcessDelegate::GetExitCode() {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ DWORD exit_code = CONTROL_C_EXIT;
+ if (worker_process_.IsValid()) {
+ if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
+ LOG_GETLASTERROR(INFO)
+ << "Failed to query the exit code of the worker process";
+ exit_code = CONTROL_C_EXIT;
+ }
+ }
+
+ return exit_code;
+}
+
+void UnprivilegedProcessDelegate::KillProcess(DWORD exit_code) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ if (worker_process_.IsValid()) {
+ TerminateProcess(worker_process_, exit_code);
+ }
+}
+
+bool UnprivilegedProcessDelegate::LaunchProcess(
+ const std::string& channel_name,
+ ScopedHandle* process_exit_event_out) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ // Create the command line passing the name of the IPC channel to use and
+ // copying known switches from the caller's command line.
+ CommandLine command_line(binary_path_);
+ command_line.AppendSwitchNative(kDaemonIpcSwitchName,
+ UTF8ToWide(channel_name));
+ command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
+ kCopiedSwitchNames,
+ arraysize(kCopiedSwitchNames));
+
+ // Try to launch the process.
+ // TODO(alexeypa): Pass a restricted process token.
+ // See http://crbug.com/134694.
+ ScopedHandle worker_thread;
+ worker_process_.Close();
+ if (!LaunchProcessWithToken(command_line.GetProgram(),
+ command_line.GetCommandLineString(),
+ NULL,
+ 0,
+ &worker_process_,
+ &worker_thread)) {
+ return false;
+ }
+
+ // Return a handle that the caller can wait on to get notified when
+ // the process terminates.
+ ScopedHandle process_exit_event;
+ if (!DuplicateHandle(GetCurrentProcess(),
+ worker_process_,
+ GetCurrentProcess(),
+ process_exit_event.Receive(),
+ SYNCHRONIZE,
+ FALSE,
+ 0)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
+ KillProcess(CONTROL_C_EXIT);
+ return false;
+ }
+
+ *process_exit_event_out = process_exit_event.Pass();
+ return true;
+}
+
+} // namespace remoting
diff --git a/remoting/host/win/unprivileged_process_delegate.h b/remoting/host/win/unprivileged_process_delegate.h
new file mode 100644
index 0000000..0108371
--- /dev/null
+++ b/remoting/host/win/unprivileged_process_delegate.h
@@ -0,0 +1,55 @@
+// 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_WIN_UNPRIVILEGED_PROCESS_DELEGATE_H_
+#define REMOTING_HOST_WIN_UNPRIVILEGED_PROCESS_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/file_path.h"
+#include "base/memory/ref_counted.h"
+#include "remoting/host/win/worker_process_launcher.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace remoting {
+
+// Implements logic for launching and monitoring a worker process under a less
+// privileged user account.
+class UnprivilegedProcessDelegate : public WorkerProcessLauncher::Delegate {
+ public:
+ UnprivilegedProcessDelegate(
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ const FilePath& binary_path);
+ virtual ~UnprivilegedProcessDelegate();
+
+ // WorkerProcessLauncher::Delegate implementation.
+ virtual DWORD GetExitCode() OVERRIDE;
+ virtual void KillProcess(DWORD exit_code) OVERRIDE;
+ virtual bool LaunchProcess(
+ const std::string& channel_name,
+ base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
+
+ private:
+ // The task runner all public methods of this class should be called on.
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+ // The task runner serving job object notifications.
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+ // Path to the worker process binary.
+ FilePath binary_path_;
+
+ // The handle of the worker process, if launched.
+ base::win::ScopedHandle worker_process_;
+
+ DISALLOW_COPY_AND_ASSIGN(UnprivilegedProcessDelegate);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_WIN_UNPRIVILEGED_PROCESS_DELEGATE_H_
diff --git a/remoting/host/win/worker_process_launcher.cc b/remoting/host/win/worker_process_launcher.cc
index 834b8bb..b06b4a3 100644
--- a/remoting/host/win/worker_process_launcher.cc
+++ b/remoting/host/win/worker_process_launcher.cc
@@ -4,11 +4,9 @@
#include "remoting/host/win/worker_process_launcher.h"
-#include <windows.h>
#include <sddl.h>
#include <limits>
-#include "base/base_switches.h"
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/logging.h"
@@ -17,77 +15,219 @@
#include "base/rand_util.h"
#include "base/stringprintf.h"
#include "base/time.h"
+#include "base/timer.h"
#include "base/utf_string_conversions.h"
+#include "base/win/object_watcher.h"
#include "base/win/scoped_handle.h"
+#include "ipc/ipc_channel.h"
#include "ipc/ipc_channel_proxy.h"
#include "ipc/ipc_message.h"
+#include "net/base/backoff_entry.h"
+#include "remoting/host/host_exit_codes.h"
#include "remoting/host/worker_process_ipc_delegate.h"
using base::win::ScopedHandle;
-
-namespace {
+using base::TimeDelta;
// Match the pipe name prefix used by Chrome IPC channels so that the client
// could use Chrome IPC APIs instead of connecting manually.
const char kChromePipeNamePrefix[] = "\\\\.\\pipe\\chrome.";
-} // namespace
+// The minimum and maximum delays between attempts to inject host process into
+// a session.
+const int kMaxLaunchDelaySeconds = 60;
+const int kMinLaunchDelaySeconds = 1;
+
+const net::BackoffEntry::Policy kDefaultBackoffPolicy = {
+ // Number of initial errors (in sequence) to ignore before applying
+ // exponential back-off rules.
+ 0,
+
+ // Initial delay for exponential back-off in ms.
+ 1000,
+
+ // Factor by which the waiting time will be multiplied.
+ 2,
+
+ // Fuzzing percentage. ex: 10% will spread requests randomly
+ // between 90%-100% of the calculated time.
+ 0,
+
+ // Maximum amount of time we are willing to delay our request in ms.
+ 60000,
+
+ // Time to keep an entry from being discarded even when it
+ // has no significant state, -1 to never discard.
+ -1,
+
+ // Don't use initial delay unless the last request was an error.
+ false,
+};
+
namespace remoting {
+// 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
+// terminated.
+class WorkerProcessLauncher::Core
+ : public base::RefCountedThreadSafe<WorkerProcessLauncher::Core>,
+ public base::win::ObjectWatcher::Delegate,
+ public IPC::Listener {
+ public:
+ // Creates the launcher that will use |launcher_delegate| to manage the worker
+ // process and |worker_delegate| to handle IPCs. The caller must ensure that
+ // |worker_delegate| remains valid until Stoppable::Stop() method has been
+ // called.
+ //
+ // The caller should call all the methods on this class on
+ // the |caller_task_runner| thread. Methods of both delegate interfaces are
+ // called on the |caller_task_runner| thread as well. |io_task_runner| is used
+ // to perform background IPC I/O.
+ Core(scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ scoped_ptr<WorkerProcessLauncher::Delegate> launcher_delegate,
+ WorkerProcessIpcDelegate* worker_delegate,
+ const std::string& pipe_security_descriptor);
+
+ // Launches the worker process.
+ void Start();
+
+ // Stops the worker process asynchronously. The caller can drop the reference
+ // to |this| as soon as Stop() returns.
+ void Stop();
+
+ // 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.
+ virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
+
+ // IPC::Listener implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
+ virtual void OnChannelError() OVERRIDE;
+
+ private:
+ friend class base::RefCountedThreadSafe<Core>;
+ virtual ~Core();
+
+ // Creates the server end of the Chromoting IPC channel.
+ bool CreatePipeForIpcChannel(const std::string& channel_name,
+ const std::string& pipe_security_descriptor,
+ base::win::ScopedHandle* pipe_out);
+
+ // Generates random channel ID.
+ std::string GenerateRandomChannelId();
+
+ // Attempts to launch the worker process. Schedules next launch attempt if
+ // creation of the process fails.
+ void LaunchWorker();
+
+ // Records a successfull launch attempt.
+ void RecordSuccessfullLaunch();
+
+ // Stops the worker process asynchronously and schedules next launch attempt
+ // unless Stop() has been called already.
+ void StopWorker();
+
+ // All public methods are called on the |caller_task_runner| thread.
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
+
+ // The task runner is used perform background IPC I/O.
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+ // Implements specifics of launching a worker process.
+ scoped_ptr<WorkerProcessLauncher::Delegate> launcher_delegate_;
+
+ // Handles IPC messages sent by the worker process.
+ WorkerProcessIpcDelegate* worker_delegate_;
+
+ // The IPC channel connecting to the launched process.
+ scoped_ptr<IPC::ChannelProxy> ipc_channel_;
+
+ // The timer used to delay termination of the process in the case of an IPC
+ // error.
+ scoped_ptr<base::OneShotTimer<Core> > ipc_error_timer_;
+
+ // Launch backoff state.
+ net::BackoffEntry launch_backoff_;
+
+ // Timer used to delay recording a successfull launch.
+ scoped_ptr<base::OneShotTimer<Core> > launch_success_timer_;
+
+ // Timer used to schedule the next attempt to launch the process.
+ scoped_ptr<base::OneShotTimer<Core> > launch_timer_;
+
+ // Used to determine when the launched process terminates.
+ base::win::ObjectWatcher process_watcher_;
+
+ // A waiting handle that becomes signalled once the launched process has
+ // been terminated.
+ base::win::ScopedHandle process_exit_event_;
+
+ // The server end of the pipe.
+ base::win::ScopedHandle pipe_;
+
+ // The security descriptor (as SDDL) of the server end of the pipe.
+ std::string pipe_security_descriptor_;
+
+ // Self reference to keep the object alive while the worker process is being
+ // terminated.
+ scoped_refptr<Core> self_;
+
+ // True when Stop() has been called.
+ bool stopping_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
WorkerProcessLauncher::Delegate::~Delegate() {
}
-WorkerProcessLauncher::WorkerProcessLauncher(
- Delegate* launcher_delegate,
+WorkerProcessLauncher::Core::Core(
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ scoped_ptr<WorkerProcessLauncher::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),
- launcher_delegate_(launcher_delegate),
+ const std::string& pipe_security_descriptor)
+ : caller_task_runner_(caller_task_runner),
+ io_task_runner_(io_task_runner),
+ launcher_delegate_(launcher_delegate.Pass()),
worker_delegate_(worker_delegate),
- main_task_runner_(main_task_runner),
- ipc_task_runner_(ipc_task_runner) {
+ launch_backoff_(&kDefaultBackoffPolicy),
+ pipe_security_descriptor_(pipe_security_descriptor),
+ stopping_(false) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ // base::OneShotTimer must be destroyed on the same thread it was created on.
+ ipc_error_timer_.reset(new base::OneShotTimer<Core>());
+ launch_success_timer_.reset(new base::OneShotTimer<Core>());
+ launch_timer_.reset(new base::OneShotTimer<Core>());
}
-WorkerProcessLauncher::~WorkerProcessLauncher() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-}
+void WorkerProcessLauncher::Core::Start() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ DCHECK(!stopping_);
-void WorkerProcessLauncher::Start(const std::string& pipe_sddl) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- DCHECK(ipc_channel_.get() == NULL);
- DCHECK(!pipe_.IsValid());
- DCHECK(!process_exit_event_.IsValid());
- DCHECK(process_watcher_.GetWatchedObject() == NULL);
+ LaunchWorker();
+}
- std::string channel_name = GenerateRandomChannelId();
- if (CreatePipeForIpcChannel(channel_name, pipe_sddl, &pipe_)) {
- // Wrap the pipe into an IPC channel.
- ipc_channel_.reset(new IPC::ChannelProxy(
- IPC::ChannelHandle(pipe_),
- IPC::Channel::MODE_SERVER,
- this,
- ipc_task_runner_));
+void WorkerProcessLauncher::Core::Stop() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
- // Launch the process and attach an object watcher to the returned process
- // handle so that we get notified if the process terminates.
- if (launcher_delegate_->DoLaunchProcess(channel_name,
- &process_exit_event_)) {
- if (process_watcher_.StartWatching(process_exit_event_, this)) {
- return;
- }
-
- launcher_delegate_->DoKillProcess(CONTROL_C_EXIT);
- }
+ if (!stopping_) {
+ stopping_ = true;
+ worker_delegate_ = NULL;
+ StopWorker();
}
-
- Stop();
}
-void WorkerProcessLauncher::Send(IPC::Message* message) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+void WorkerProcessLauncher::Core::Send(IPC::Message* message) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (ipc_channel_.get()) {
ipc_channel_->Send(message);
@@ -96,27 +236,24 @@ void WorkerProcessLauncher::Send(IPC::Message* message) {
}
}
-void WorkerProcessLauncher::OnObjectSignaled(HANDLE object) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+void WorkerProcessLauncher::Core::OnObjectSignaled(HANDLE object) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(process_watcher_.GetWatchedObject() == NULL);
- Stop();
+ StopWorker();
}
-bool WorkerProcessLauncher::OnMessageReceived(const IPC::Message& message) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+bool WorkerProcessLauncher::Core::OnMessageReceived(
+ const IPC::Message& message) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(ipc_channel_.get() != NULL);
- DCHECK(pipe_.IsValid());
- DCHECK(process_exit_event_.IsValid());
return worker_delegate_->OnMessageReceived(message);
}
-void WorkerProcessLauncher::OnChannelConnected(int32 peer_pid) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+void WorkerProcessLauncher::Core::OnChannelConnected(int32 peer_pid) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(ipc_channel_.get() != NULL);
- DCHECK(pipe_.IsValid());
- DCHECK(process_exit_event_.IsValid());
// |peer_pid| is send by the client and cannot be trusted.
// GetNamedPipeClientProcessId() is not available on XP. The pipe's security
@@ -129,47 +266,27 @@ void WorkerProcessLauncher::OnChannelConnected(int32 peer_pid) {
worker_delegate_->OnChannelConnected();
}
-void WorkerProcessLauncher::OnChannelError() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+void WorkerProcessLauncher::Core::OnChannelError() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(ipc_channel_.get() != NULL);
- DCHECK(pipe_.IsValid());
- DCHECK(process_exit_event_.IsValid());
// Schedule a delayed termination of the worker process. Usually, the pipe is
// disconnected when the worker process is about to exit. Waiting a little bit
// here allows the worker to exit completely and so, notify
- // |process_watcher_|. As the result DoKillProcess() will not be called and
+ // |process_watcher_|. As the result KillProcess() will not be called and
// the original exit code reported by the worker process will be retrieved.
- ipc_error_timer_.Start(FROM_HERE, base::TimeDelta::FromSeconds(5),
- this, &WorkerProcessLauncher::Stop);
+ ipc_error_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(5),
+ this, &Core::StopWorker);
}
-void WorkerProcessLauncher::DoStop() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- ipc_channel_.reset();
- pipe_.Close();
-
- // Kill the process if it has been started already.
- if (process_watcher_.GetWatchedObject() != NULL) {
- launcher_delegate_->DoKillProcess(CONTROL_C_EXIT);
- return;
- }
-
- ipc_error_timer_.Stop();
-
- DCHECK(ipc_channel_.get() == NULL);
- DCHECK(!pipe_.IsValid());
- DCHECK(process_watcher_.GetWatchedObject() == NULL);
-
- process_exit_event_.Close();
- CompleteStopping();
+WorkerProcessLauncher::Core::~Core() {
+ DCHECK(stopping_);
}
// Creates the server end of the Chromoting IPC channel.
-bool WorkerProcessLauncher::CreatePipeForIpcChannel(
+bool WorkerProcessLauncher::Core::CreatePipeForIpcChannel(
const std::string& channel_name,
- const std::string& pipe_sddl,
+ const std::string& pipe_security_descriptor,
ScopedHandle* pipe_out) {
// Create security descriptor for the channel.
SECURITY_ATTRIBUTES security_attributes;
@@ -178,7 +295,7 @@ bool WorkerProcessLauncher::CreatePipeForIpcChannel(
ULONG security_descriptor_length = 0;
if (!ConvertStringSecurityDescriptorToSecurityDescriptor(
- UTF8ToUTF16(pipe_sddl).c_str(),
+ UTF8ToUTF16(pipe_security_descriptor).c_str(),
SDDL_REVISION_1,
reinterpret_cast<PSECURITY_DESCRIPTOR*>(
&security_attributes.lpSecurityDescriptor),
@@ -218,10 +335,130 @@ bool WorkerProcessLauncher::CreatePipeForIpcChannel(
}
// N.B. Copied from src/content/common/child_process_host_impl.cc
-std::string WorkerProcessLauncher::GenerateRandomChannelId() {
+std::string WorkerProcessLauncher::Core::GenerateRandomChannelId() {
return base::StringPrintf("%d.%p.%d",
base::GetCurrentProcId(), this,
base::RandInt(0, std::numeric_limits<int>::max()));
}
+void WorkerProcessLauncher::Core::LaunchWorker() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ DCHECK(ipc_channel_.get() == NULL);
+ DCHECK(!launch_success_timer_->IsRunning());
+ DCHECK(!launch_timer_->IsRunning());
+ DCHECK(!pipe_.IsValid());
+ DCHECK(!process_exit_event_.IsValid());
+ DCHECK(process_watcher_.GetWatchedObject() == NULL);
+
+ std::string channel_name = GenerateRandomChannelId();
+ if (CreatePipeForIpcChannel(channel_name, pipe_security_descriptor_,
+ &pipe_)) {
+ // Wrap the pipe into an IPC channel.
+ ipc_channel_.reset(new IPC::ChannelProxy(
+ IPC::ChannelHandle(pipe_),
+ IPC::Channel::MODE_SERVER,
+ this,
+ io_task_runner_));
+
+ // Launch the process and attach an object watcher to the returned process
+ // handle so that we get notified if the process terminates.
+ if (launcher_delegate_->LaunchProcess(channel_name, &process_exit_event_)) {
+ if (process_watcher_.StartWatching(process_exit_event_, this)) {
+ // Record a successful launch once the process has been running for at
+ // least two seconds.
+ launch_success_timer_->Start(FROM_HERE, base::TimeDelta::FromSeconds(2),
+ this, &Core::RecordSuccessfullLaunch);
+ return;
+ }
+
+ launcher_delegate_->KillProcess(CONTROL_C_EXIT);
+ }
+ }
+
+ launch_backoff_.InformOfRequest(false);
+ StopWorker();
+}
+
+void WorkerProcessLauncher::Core::RecordSuccessfullLaunch() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ launch_backoff_.InformOfRequest(true);
+}
+
+void WorkerProcessLauncher::Core::StopWorker() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ // Record a launch failure if the process exited too soon.
+ if (launch_success_timer_->IsRunning()) {
+ launch_success_timer_->Stop();
+ launch_backoff_.InformOfRequest(false);
+ }
+
+ // Keep |this| alive until the worker process is terminated.
+ self_ = this;
+
+ ipc_channel_.reset();
+ pipe_.Close();
+
+ // Kill the process if it has been started already.
+ if (process_watcher_.GetWatchedObject() != NULL) {
+ launcher_delegate_->KillProcess(CONTROL_C_EXIT);
+ return;
+ }
+
+ ipc_error_timer_->Stop();
+
+ DCHECK(ipc_channel_.get() == NULL);
+ DCHECK(!pipe_.IsValid());
+ DCHECK(process_watcher_.GetWatchedObject() == NULL);
+
+ process_exit_event_.Close();
+
+ // Do not relaunch the worker process if the caller has asked us to stop.
+ if (stopping_) {
+ ipc_error_timer_.reset();
+ launch_timer_.reset();
+ self_ = NULL;
+ return;
+ }
+
+ self_ = NULL;
+
+ // Stop trying to restart the worker process if it exited due to
+ // misconfiguration.
+ DWORD exit_code = launcher_delegate_->GetExitCode();
+ if (kMinPermanentErrorExitCode <= exit_code &&
+ exit_code <= kMaxPermanentErrorExitCode) {
+ // |delegate_| must be valid because Stop() hasn't been called yet and
+ // |running_| is true. |worker_delegate_| is valid here because Stop()
+ // hasn't been called yet (|stopping_| is false).
+ worker_delegate_->OnPermanentError();
+ return;
+ }
+
+ // Schedule the next attempt to launch the worker process.
+ launch_timer_->Start(FROM_HERE, launch_backoff_.GetTimeUntilRelease(),
+ this, &Core::LaunchWorker);
+}
+
+WorkerProcessLauncher::WorkerProcessLauncher(
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ scoped_ptr<Delegate> launcher_delegate,
+ WorkerProcessIpcDelegate* worker_delegate,
+ const std::string& pipe_security_descriptor) {
+ core_ = new Core(caller_task_runner, io_task_runner, launcher_delegate.Pass(),
+ worker_delegate, pipe_security_descriptor);
+ core_->Start();
+}
+
+WorkerProcessLauncher::~WorkerProcessLauncher() {
+ core_->Stop();
+ core_ = NULL;
+}
+
+void WorkerProcessLauncher::Send(IPC::Message* message) {
+ core_->Send(message);
+}
+
} // namespace remoting
diff --git a/remoting/host/win/worker_process_launcher.h b/remoting/host/win/worker_process_launcher.h
index d53f860..fb05910 100644
--- a/remoting/host/win/worker_process_launcher.h
+++ b/remoting/host/win/worker_process_launcher.h
@@ -5,25 +5,17 @@
#ifndef REMOTING_HOST_WIN_WORKER_PROCESS_LAUNCHER_H_
#define REMOTING_HOST_WIN_WORKER_PROCESS_LAUNCHER_H_
-#include <windows.h>
-
#include "base/basictypes.h"
#include "base/compiler_specific.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
-#include "base/process.h"
-#include "base/timer.h"
#include "base/win/scoped_handle.h"
-#include "base/win/object_watcher.h"
-#include "ipc/ipc_channel.h"
-#include "remoting/base/stoppable.h"
namespace base {
class SingleThreadTaskRunner;
} // namespace base
namespace IPC {
-class ChannelProxy;
class Message;
} // namespace IPC
@@ -32,100 +24,55 @@ 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
-// terminated.
-//
-// WorkerProcessLauncher object is good for one process launch attempt only.
-class WorkerProcessLauncher
- : public Stoppable,
- public base::win::ObjectWatcher::Delegate,
- public IPC::Listener {
+// interaction with the spawned process is through WorkerProcessIpcDelegate and
+// Send() method. In case of error the channel is closed and the worker process
+// is terminated.
+class WorkerProcessLauncher {
public:
class Delegate {
public:
virtual ~Delegate();
+ // Returns the exit code of the worker process.
+ virtual DWORD GetExitCode() = 0;
+
+ // Terminates the worker process with the given exit code.
+ virtual void KillProcess(DWORD exit_code) = 0;
+
// Starts the worker process and passes |channel_name| to it.
// |process_exit_event_out| receives a handle that becomes signalled once
// the launched process has been terminated.
- virtual bool DoLaunchProcess(
+ virtual bool LaunchProcess(
const std::string& channel_name,
base::win::ScopedHandle* process_exit_event_out) = 0;
-
- // Terminates the worker process with the given exit code.
- virtual void DoKillProcess(DWORD exit_code) = 0;
};
// Creates the launcher that will use |launcher_delegate| to manage the worker
- // process and |worker_delegate| to handle IPCs.
+ // process and |worker_delegate| to handle IPCs. The caller must ensure that
+ // |worker_delegate| remains valid until Stoppable::Stop() method has been
+ // called.
//
- // |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.
+ // The caller should call all the methods on this class on
+ // the |caller_task_runner| thread. Methods of both delegate interfaces are
+ // called on the |caller_task_runner| thread as well. |io_task_runner| is used
+ // to perform background IPC I/O.
WorkerProcessLauncher(
- Delegate* launcher_delegate,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ scoped_ptr<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);
- virtual ~WorkerProcessLauncher();
-
- // Starts the worker process.
- void Start(const std::string& pipe_sddl);
+ const std::string& pipe_security_descriptor);
+ ~WorkerProcessLauncher();
// 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.
- virtual void OnObjectSignaled(HANDLE object) OVERRIDE;
-
- // IPC::Listener implementation.
- virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
- virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
- virtual void OnChannelError() OVERRIDE;
-
- protected:
- // Stoppable implementation.
- virtual void DoStop() OVERRIDE;
-
private:
- // Creates the server end of the Chromoting IPC channel.
- bool CreatePipeForIpcChannel(const std::string& channel_name,
- const std::string& pipe_sddl,
- base::win::ScopedHandle* pipe_out);
-
- // Generates random channel ID.
- std::string GenerateRandomChannelId();
-
- Delegate* launcher_delegate_;
- WorkerProcessIpcDelegate* worker_delegate_;
-
- // The main service message loop.
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
-
- // Message loop used by the IPC channel.
- scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
-
- // Used to determine when the launched process terminates.
- base::win::ObjectWatcher process_watcher_;
-
- // A waiting handle that becomes signalled once the launched process has
- // been terminated.
- base::win::ScopedHandle process_exit_event_;
-
- // The IPC channel connecting to the launched process.
- scoped_ptr<IPC::ChannelProxy> ipc_channel_;
-
- // The timer used to delay termination of the process in the case of an IPC
- // error.
- base::OneShotTimer<WorkerProcessLauncher> ipc_error_timer_;
-
- // The server end of the pipe.
- base::win::ScopedHandle pipe_;
+ // The actual implementation resides in WorkerProcessLauncher::Core class.
+ class Core;
+ scoped_refptr<Core> core_;
DISALLOW_COPY_AND_ASSIGN(WorkerProcessLauncher);
};
diff --git a/remoting/host/win/worker_process_launcher_unittest.cc b/remoting/host/win/worker_process_launcher_unittest.cc
new file mode 100644
index 0000000..7c60eee
--- /dev/null
+++ b/remoting/host/win/worker_process_launcher_unittest.cc
@@ -0,0 +1,335 @@
+// 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 "base/bind.h"
+#include "base/memory/ref_counted.h"
+#include "base/message_loop.h"
+#include "base/win/scoped_handle.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_listener.h"
+#include "ipc/ipc_message.h"
+#include "remoting/base/auto_thread_task_runner.h"
+#include "remoting/host/host_exit_codes.h"
+#include "remoting/host/win/worker_process_launcher.h"
+#include "remoting/host/worker_process_ipc_delegate.h"
+#include "testing/gmock_mutant.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using base::win::ScopedHandle;
+using testing::_;
+using testing::AnyNumber;
+using testing::CreateFunctor;
+using testing::DoAll;
+using testing::Expectation;
+using testing::Invoke;
+using testing::Return;
+using testing::ReturnPointee;
+
+namespace remoting {
+
+namespace {
+
+const char kIpcSecurityDescriptor[] = "D:(A;;GA;;;AU)";
+
+class MockProcessLauncherDelegate
+ : public WorkerProcessLauncher::Delegate {
+ public:
+ MockProcessLauncherDelegate() {}
+ virtual ~MockProcessLauncherDelegate() {}
+
+ // WorkerProcessLauncher::Delegate implementation
+ MOCK_METHOD0(GetExitCode, DWORD());
+ MOCK_METHOD1(KillProcess, void(DWORD));
+ MOCK_METHOD2(LaunchProcess, bool(const std::string&, ScopedHandle*));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockProcessLauncherDelegate);
+};
+
+class MockIpcDelegate
+ : public WorkerProcessIpcDelegate {
+ public:
+ MockIpcDelegate() {}
+ virtual ~MockIpcDelegate() {}
+
+ // WorkerProcessIpcDelegate implementation
+ MOCK_METHOD0(OnChannelConnected, void());
+ MOCK_METHOD1(OnMessageReceived, bool(const IPC::Message&));
+ MOCK_METHOD0(OnPermanentError, void());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockIpcDelegate);
+};
+
+} // namespace
+
+class WorkerProcessLauncherTest
+ : public testing::Test,
+ public IPC::Listener {
+ public:
+ WorkerProcessLauncherTest();
+ virtual ~WorkerProcessLauncherTest();
+
+ virtual void SetUp() OVERRIDE;
+ virtual void TearDown() OVERRIDE;
+
+ // IPC::Listener implementation
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+
+ // WorkerProcessLauncher::Delegate mocks
+ void KillProcess(DWORD exit_code);
+ bool LaunchProcess(const std::string& channel_name,
+ ScopedHandle* process_exit_event_out);
+ bool LaunchProcessAndConnect(const std::string& channel_name,
+ ScopedHandle* process_exit_event_out);
+
+ void ConnectTo(const std::string& channel_name);
+ void Disconnect();
+
+ // Starts the worker.
+ void StartWorker();
+
+ // Stops the worker.
+ void StopWorker();
+
+ // Quits |message_loop_|.
+ void QuitMainMessageLoop();
+
+ protected:
+ MessageLoop message_loop_;
+ scoped_refptr<AutoThreadTaskRunner> task_runner_;
+
+ // Exit code of the worker process.
+ DWORD exit_code_;
+
+ MockIpcDelegate ipc_delegate_;
+ scoped_ptr<MockProcessLauncherDelegate> launcher_delegate_;
+
+ // Client end of the IPC channel.
+ scoped_ptr<IPC::ChannelProxy> ipc_channel_;
+
+ // The worker process launcher.
+ scoped_ptr<WorkerProcessLauncher> launcher_;
+
+ // The event signalling termination of the worker process.
+ ScopedHandle process_exit_event_;
+};
+
+
+WorkerProcessLauncherTest::WorkerProcessLauncherTest()
+ : message_loop_(MessageLoop::TYPE_IO) {
+}
+
+WorkerProcessLauncherTest::~WorkerProcessLauncherTest() {
+}
+
+void WorkerProcessLauncherTest::SetUp() {
+ task_runner_ = new AutoThreadTaskRunner(
+ message_loop_.message_loop_proxy(),
+ base::Bind(&WorkerProcessLauncherTest::QuitMainMessageLoop,
+ base::Unretained(this)));
+
+ exit_code_ = STILL_ACTIVE;
+
+ // Set up process launcher delegate
+ launcher_delegate_.reset(new MockProcessLauncherDelegate());
+ EXPECT_CALL(*launcher_delegate_, GetExitCode())
+ .Times(AnyNumber())
+ .WillRepeatedly(ReturnPointee(&exit_code_));
+ EXPECT_CALL(*launcher_delegate_, KillProcess(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Invoke(this, &WorkerProcessLauncherTest::KillProcess));
+
+ // Set up IPC delegate.
+ EXPECT_CALL(ipc_delegate_, OnMessageReceived(_))
+ .Times(AnyNumber())
+ .WillRepeatedly(Return(false));
+}
+
+void WorkerProcessLauncherTest::TearDown() {
+}
+
+bool WorkerProcessLauncherTest::OnMessageReceived(const IPC::Message& message) {
+ return false;
+}
+
+void WorkerProcessLauncherTest::KillProcess(DWORD exit_code) {
+ exit_code_ = exit_code;
+ BOOL result = SetEvent(process_exit_event_);
+ EXPECT_TRUE(result);
+}
+
+bool WorkerProcessLauncherTest::LaunchProcess(
+ const std::string& channel_name,
+ ScopedHandle* process_exit_event_out) {
+ return LaunchProcessAndConnect("", process_exit_event_out);
+}
+
+bool WorkerProcessLauncherTest::LaunchProcessAndConnect(
+ const std::string& channel_name,
+ ScopedHandle* process_exit_event_out) {
+ process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
+ if (!process_exit_event_.IsValid())
+ return false;
+
+ if (!channel_name.empty()) {
+ task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&WorkerProcessLauncherTest::ConnectTo,
+ base::Unretained(this),
+ channel_name));
+ }
+
+ exit_code_ = STILL_ACTIVE;
+ return DuplicateHandle(GetCurrentProcess(),
+ process_exit_event_,
+ GetCurrentProcess(),
+ process_exit_event_out->Receive(),
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS) != FALSE;
+}
+
+void WorkerProcessLauncherTest::ConnectTo(const std::string& channel_name) {
+ ipc_channel_.reset(new IPC::ChannelProxy(
+ IPC::ChannelHandle(channel_name),
+ IPC::Channel::MODE_CLIENT,
+ this,
+ task_runner_));
+}
+
+void WorkerProcessLauncherTest::Disconnect() {
+ ipc_channel_.reset();
+}
+
+void WorkerProcessLauncherTest::StartWorker() {
+ launcher_.reset(new WorkerProcessLauncher(task_runner_,
+ task_runner_,
+ launcher_delegate_.Pass(),
+ &ipc_delegate_,
+ kIpcSecurityDescriptor));
+}
+
+void WorkerProcessLauncherTest::StopWorker() {
+ launcher_.reset();
+ Disconnect();
+ task_runner_ = NULL;
+}
+
+void WorkerProcessLauncherTest::QuitMainMessageLoop() {
+ message_loop_.PostTask(FROM_HERE, MessageLoop::QuitClosure());
+}
+
+TEST_F(WorkerProcessLauncherTest, Start) {
+ EXPECT_CALL(*launcher_delegate_, LaunchProcess(_, _))
+ .Times(1)
+ .WillRepeatedly(Invoke(this, &WorkerProcessLauncherTest::LaunchProcess));
+
+ EXPECT_CALL(ipc_delegate_, OnChannelConnected())
+ .Times(0);
+ EXPECT_CALL(ipc_delegate_, OnPermanentError())
+ .Times(0);
+
+ StartWorker();
+ StopWorker();
+ message_loop_.Run();
+
+ EXPECT_EQ(exit_code_, CONTROL_C_EXIT);
+}
+
+// Starts and connects to the worker process. Expect OnChannelConnected to be
+// called.
+TEST_F(WorkerProcessLauncherTest, StartAndConnect) {
+ EXPECT_CALL(*launcher_delegate_, LaunchProcess(_, _))
+ .Times(1)
+ .WillRepeatedly(Invoke(
+ this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
+
+ EXPECT_CALL(ipc_delegate_, OnChannelConnected())
+ .Times(1)
+ .WillOnce(Invoke(this, &WorkerProcessLauncherTest::StopWorker));
+ EXPECT_CALL(ipc_delegate_, OnPermanentError())
+ .Times(0);
+
+ StartWorker();
+ message_loop_.Run();
+
+ EXPECT_EQ(exit_code_, CONTROL_C_EXIT);
+}
+
+// Kills the worker process after the 1st connect and expects it to be
+// restarted.
+TEST_F(WorkerProcessLauncherTest, Restart) {
+ EXPECT_CALL(*launcher_delegate_, LaunchProcess(_, _))
+ .Times(2)
+ .WillRepeatedly(Invoke(
+ this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
+ Expectation first_connect =
+ EXPECT_CALL(ipc_delegate_, OnChannelConnected())
+ .Times(2)
+ .WillOnce(Invoke(CreateFunctor(
+ this, &WorkerProcessLauncherTest::KillProcess, CONTROL_C_EXIT)))
+ .WillOnce(Invoke(this, &WorkerProcessLauncherTest::StopWorker));
+
+ EXPECT_CALL(ipc_delegate_, OnPermanentError())
+ .Times(0);
+
+ StartWorker();
+ message_loop_.Run();
+
+ EXPECT_EQ(exit_code_, CONTROL_C_EXIT);
+}
+
+// Drops the IPC channel to the worker process after the 1st connect and expects
+// the worker process to be restarted.
+TEST_F(WorkerProcessLauncherTest, DropIpcChannel) {
+ EXPECT_CALL(*launcher_delegate_, LaunchProcess(_, _))
+ .Times(2)
+ .WillRepeatedly(Invoke(
+ this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
+
+ Expectation first_connect =
+ EXPECT_CALL(ipc_delegate_, OnChannelConnected())
+ .Times(2)
+ .WillOnce(Invoke(this, &WorkerProcessLauncherTest::Disconnect))
+ .WillOnce(Invoke(this, &WorkerProcessLauncherTest::StopWorker));
+
+ EXPECT_CALL(ipc_delegate_, OnPermanentError())
+ .Times(0);
+
+ StartWorker();
+ message_loop_.Run();
+
+ EXPECT_EQ(exit_code_, CONTROL_C_EXIT);
+}
+
+// Returns a permanent error exit code and expects OnPermanentError() to be
+// invoked.
+TEST_F(WorkerProcessLauncherTest, PermanentError) {
+ EXPECT_CALL(*launcher_delegate_, LaunchProcess(_, _))
+ .Times(1)
+ .WillRepeatedly(Invoke(
+ this, &WorkerProcessLauncherTest::LaunchProcessAndConnect));
+
+ base::Closure terminate_permanently =
+ base::Bind(&WorkerProcessLauncherTest::KillProcess,
+ base::Unretained(this),
+ kInvalidHostConfigurationExitCode);
+ EXPECT_CALL(ipc_delegate_, OnChannelConnected())
+ .Times(1)
+ .WillRepeatedly(Invoke(&terminate_permanently, &base::Closure::Run));
+
+ EXPECT_CALL(ipc_delegate_, OnPermanentError())
+ .Times(1)
+ .WillOnce(Invoke(this, &WorkerProcessLauncherTest::StopWorker));
+
+ StartWorker();
+ message_loop_.Run();
+
+ EXPECT_EQ(exit_code_, kInvalidHostConfigurationExitCode);
+}
+
+} // namespace remoting
diff --git a/remoting/host/win/wts_console_session_process_driver.cc b/remoting/host/win/wts_console_session_process_driver.cc
new file mode 100644
index 0000000..5bd7cd5
--- /dev/null
+++ b/remoting/host/win/wts_console_session_process_driver.cc
@@ -0,0 +1,125 @@
+// 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/win/wts_console_session_process_driver.h"
+
+#include <sddl.h>
+#include <limits>
+
+#include "base/base_switches.h"
+#include "base/bind.h"
+#include "base/bind_helpers.h"
+#include "base/file_path.h"
+#include "base/logging.h"
+#include "base/single_thread_task_runner.h"
+#include "base/path_service.h"
+#include "ipc/ipc_message.h"
+#include "remoting/host/win/worker_process_launcher.h"
+#include "remoting/host/win/wts_console_monitor.h"
+#include "remoting/host/win/wts_session_process_delegate.h"
+
+namespace {
+
+const FilePath::CharType kMe2meHostBinaryName[] =
+ FILE_PATH_LITERAL("remoting_host.exe");
+
+// The security descriptor of the daemon IPC endpoint. It gives full access
+// to LocalSystem and denies access by anyone else.
+const char kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)";
+
+} // namespace
+
+namespace remoting {
+
+WtsConsoleSessionProcessDriver::WtsConsoleSessionProcessDriver(
+ const base::Closure& stopped_callback,
+ WtsConsoleMonitor* monitor,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
+ : Stoppable(caller_task_runner, stopped_callback),
+ caller_task_runner_(caller_task_runner),
+ io_task_runner_(io_task_runner),
+ monitor_(monitor) {
+ monitor_->AddWtsConsoleObserver(this);
+}
+
+WtsConsoleSessionProcessDriver::~WtsConsoleSessionProcessDriver() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ // 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);
+
+ monitor_->RemoveWtsConsoleObserver(this);
+
+ CHECK(launcher_.get() == NULL);
+}
+
+void WtsConsoleSessionProcessDriver::OnChannelConnected() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+}
+
+bool WtsConsoleSessionProcessDriver::OnMessageReceived(
+ const IPC::Message& message) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ return false;
+}
+
+void WtsConsoleSessionProcessDriver::OnPermanentError() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ Stop();
+}
+
+void WtsConsoleSessionProcessDriver::OnSessionAttached(uint32 session_id) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ if (stoppable_state() != Stoppable::kRunning) {
+ return;
+ }
+
+ DCHECK(launcher_.get() == NULL);
+
+ // 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.";
+ Stop();
+ return;
+ }
+ FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
+
+ // Create a Delegate capable of launching an elevated process in the session.
+ scoped_ptr<WtsSessionProcessDelegate> delegate(
+ new WtsSessionProcessDelegate(caller_task_runner_,
+ io_task_runner_,
+ host_binary,
+ session_id,
+ true));
+
+ // Use the Delegate to launch the host process.
+ launcher_.reset(new WorkerProcessLauncher(caller_task_runner_,
+ io_task_runner_,
+ delegate.Pass(),
+ this,
+ kDaemonIpcSecurityDescriptor));
+}
+
+void WtsConsoleSessionProcessDriver::OnSessionDetached() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ DCHECK(launcher_.get() != NULL);
+
+ launcher_.reset();
+}
+
+void WtsConsoleSessionProcessDriver::DoStop() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ launcher_.reset();
+ CompleteStopping();
+}
+
+} // namespace remoting
diff --git a/remoting/host/win/wts_console_session_process_driver.h b/remoting/host/win/wts_console_session_process_driver.h
new file mode 100644
index 0000000..90a8f3b
--- /dev/null
+++ b/remoting/host/win/wts_console_session_process_driver.h
@@ -0,0 +1,76 @@
+// 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_WIN_WTS_CONSOLE_SESSION_PROCESS_DRIVER_H_
+#define REMOTING_HOST_WIN_WTS_CONSOLE_SESSION_PROCESS_DRIVER_H_
+
+#include "base/callback_forward.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "base/memory/scoped_ptr.h"
+#include "remoting/base/stoppable.h"
+#include "remoting/host/win/wts_console_observer.h"
+#include "remoting/host/worker_process_ipc_delegate.h"
+
+namespace base {
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace remoting {
+
+class WorkerProcessLauncher;
+class WtsConsoleMonitor;
+
+// Launches the host in the session attached to the console. When a new session
+// attaches to the console relaunches the host in it.
+class WtsConsoleSessionProcessDriver
+ : public Stoppable,
+ public WorkerProcessIpcDelegate,
+ public WtsConsoleObserver {
+ public:
+ // Constructs a WtsConsoleSessionProcessDriver object. |stopped_callback| will
+ // be invoked to notify the caller that the object is completely stopped. All
+ // public methods of this class must be invoked on the |caller_task_runner|
+ // thread. |monitor| will only be accessed from the |caller_task_runner|
+ // thread. |io_task_runner| must be an I/O message loop.
+ WtsConsoleSessionProcessDriver(
+ const base::Closure& stopped_callback,
+ WtsConsoleMonitor* monitor,
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
+
+ virtual ~WtsConsoleSessionProcessDriver();
+
+ // WorkerProcessIpcDelegate implementation.
+ virtual void OnChannelConnected() OVERRIDE;
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnPermanentError() OVERRIDE;
+
+ // WtsConsoleObserver implementation.
+ virtual void OnSessionAttached(uint32 session_id) OVERRIDE;
+ virtual void OnSessionDetached() OVERRIDE;
+
+ protected:
+ // Stoppable implementation.
+ virtual void DoStop() OVERRIDE;
+
+ private:
+ // Task runner on which public methods of this class must be called.
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
+
+ // Message loop used by the IPC channel.
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+ // Launches and monitors the worker process.
+ scoped_ptr<WorkerProcessLauncher> launcher_;
+
+ // Used to unsubscribe from session attach and detach events.
+ WtsConsoleMonitor* monitor_;
+
+ DISALLOW_COPY_AND_ASSIGN(WtsConsoleSessionProcessDriver);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_WIN_WTS_CONSOLE_SESSION_PROCESS_DRIVER_H_
diff --git a/remoting/host/win/wts_session_process_delegate.cc b/remoting/host/win/wts_session_process_delegate.cc
new file mode 100644
index 0000000..54fc92c
--- /dev/null
+++ b/remoting/host/win/wts_session_process_delegate.cc
@@ -0,0 +1,457 @@
+// 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.
+//
+// This file implements the Windows service controlling Me2Me host processes
+// running within user sessions.
+
+#include "remoting/host/win/wts_session_process_delegate.h"
+
+#include <sddl.h>
+#include <limits>
+
+#include "base/base_switches.h"
+#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/logging.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/message_loop.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 "base/win/windows_version.h"
+#include "ipc/ipc_channel.h"
+#include "ipc/ipc_channel_proxy.h"
+#include "ipc/ipc_message.h"
+#include "remoting/host/host_exit_codes.h"
+#include "remoting/host/win/launch_process_with_token.h"
+#include "remoting/host/win/worker_process_launcher.h"
+#include "remoting/host/win/wts_console_monitor.h"
+#include "remoting/host/worker_process_ipc_delegate.h"
+
+using base::TimeDelta;
+using base::win::ScopedHandle;
+
+const FilePath::CharType kDaemonBinaryName[] =
+ FILE_PATH_LITERAL("remoting_daemon.exe");
+
+// 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[] = {
+ "host-config", switches::kV, switches::kVModule };
+
+namespace remoting {
+
+// A private class actually implementing the functionality provided by
+// |WtsSessionProcessDelegate|. This class is ref-counted and implements
+// asynchronous fire-and-forget shutdown.
+class WtsSessionProcessDelegate::Core
+ : public base::RefCountedThreadSafe<WtsSessionProcessDelegate::Core>,
+ public base::MessagePumpForIO::IOHandler,
+ public WorkerProcessLauncher::Delegate {
+ public:
+ // The caller must ensure that |delegate| remains valid at least until
+ // Stop() method has been called.
+ Core(scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ const FilePath& binary_path,
+ bool launch_elevated);
+
+ // base::MessagePumpForIO::IOHandler implementation.
+ virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
+ DWORD bytes_transferred,
+ DWORD error) OVERRIDE;
+
+ // WorkerProcessLauncher::Delegate implementation.
+ virtual DWORD GetExitCode() OVERRIDE;
+ virtual void KillProcess(DWORD exit_code) OVERRIDE;
+ virtual bool LaunchProcess(
+ const std::string& channel_name,
+ base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
+
+ // Initializes the object returning true on success.
+ bool Initialize(uint32 session_id);
+
+ // Stops the object asynchronously.
+ void Stop();
+
+ private:
+ friend class base::RefCountedThreadSafe<Core>;
+ virtual ~Core();
+
+ // Drains the completion port queue to make sure that all job object
+ // notifications have been received.
+ void DrainJobNotifications();
+
+ // Notified that the completion port queue has been drained.
+ void DrainJobNotificationsCompleted();
+
+ // Creates and initializes the job object that will sandbox the launched child
+ // processes.
+ void InitializeJob();
+
+ // Notified that the job object initialization is complete.
+ void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job);
+
+ // Called to process incoming job object notifications.
+ void OnJobNotification(DWORD message, DWORD pid);
+
+ // The task runner all public methods of this class should be called on.
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+ // The task runner serving job object notifications.
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
+
+ // Path to the worker process binary.
+ FilePath binary_path_;
+
+ // The job object used to control the lifetime of child processes.
+ base::win::ScopedHandle job_;
+
+ // True if the worker process should be launched elevated.
+ bool launch_elevated_;
+
+ // A handle that becomes signalled once all processes associated with the job
+ // have been terminated.
+ base::win::ScopedHandle process_exit_event_;
+
+ // The token to be used to launch a process in a different session.
+ base::win::ScopedHandle session_token_;
+
+ // True if Stop() has been called.
+ bool stopping_;
+
+ // The handle of the worker process, if launched.
+ base::win::ScopedHandle worker_process_;
+
+ DISALLOW_COPY_AND_ASSIGN(Core);
+};
+
+WtsSessionProcessDelegate::Core::Core(
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ const FilePath& binary_path,
+ bool launch_elevated)
+ : main_task_runner_(main_task_runner),
+ io_task_runner_(io_task_runner),
+ binary_path_(binary_path),
+ launch_elevated_(launch_elevated),
+ stopping_(false) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+}
+
+void WtsSessionProcessDelegate::Core::OnIOCompleted(
+ base::MessagePumpForIO::IOContext* context,
+ DWORD bytes_transferred,
+ DWORD error) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+ // |bytes_transferred| is used in job object notifications to supply
+ // the message ID; |context| carries process ID.
+ main_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &Core::OnJobNotification, this, bytes_transferred,
+ reinterpret_cast<DWORD>(context)));
+}
+
+DWORD WtsSessionProcessDelegate::Core::GetExitCode() {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ DWORD exit_code = CONTROL_C_EXIT;
+ if (worker_process_.IsValid()) {
+ if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
+ LOG_GETLASTERROR(INFO)
+ << "Failed to query the exit code of the worker process";
+ exit_code = CONTROL_C_EXIT;
+ }
+ }
+
+ return exit_code;
+}
+
+void WtsSessionProcessDelegate::Core::KillProcess(DWORD exit_code) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ if (launch_elevated_) {
+ if (job_.IsValid()) {
+ TerminateJobObject(job_, exit_code);
+ }
+ } else {
+ if (worker_process_.IsValid()) {
+ TerminateProcess(worker_process_, exit_code);
+ }
+ }
+}
+
+bool WtsSessionProcessDelegate::Core::LaunchProcess(
+ const std::string& channel_name,
+ ScopedHandle* process_exit_event_out) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ CommandLine command_line(CommandLine::NO_PROGRAM);
+ if (launch_elevated_) {
+ // The job object is not ready. Retry starting the host process later.
+ if (!job_.IsValid()) {
+ return false;
+ }
+
+ // Construct the helper 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 daemon_binary = dir_path.Append(kDaemonBinaryName);
+
+ // Create the command line passing the name of the IPC channel to use and
+ // copying known switches from the caller's command line.
+ command_line.SetProgram(daemon_binary);
+ command_line.AppendSwitchPath(kElevateSwitchName, binary_path_);
+
+ CHECK(ResetEvent(process_exit_event_));
+ } else {
+ command_line.SetProgram(binary_path_);
+ }
+
+ // Create the command line passing the name of the IPC channel to use and
+ // copying known switches from the caller's command line.
+ command_line.AppendSwitchNative(kDaemonIpcSwitchName,
+ UTF8ToWide(channel_name));
+ command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
+ kCopiedSwitchNames,
+ arraysize(kCopiedSwitchNames));
+
+ // Try to launch the process.
+ ScopedHandle worker_process;
+ ScopedHandle worker_thread;
+ if (!LaunchProcessWithToken(command_line.GetProgram(),
+ command_line.GetCommandLineString(),
+ session_token_,
+ CREATE_SUSPENDED | CREATE_BREAKAWAY_FROM_JOB,
+ &worker_process,
+ &worker_thread)) {
+ return false;
+ }
+
+ HANDLE local_process_exit_event;
+ if (launch_elevated_) {
+ if (!AssignProcessToJobObject(job_, worker_process)) {
+ LOG_GETLASTERROR(ERROR)
+ << "Failed to assign the worker to the job object";
+ TerminateProcess(worker_process, CONTROL_C_EXIT);
+ return false;
+ }
+
+ local_process_exit_event = process_exit_event_;
+ } else {
+ worker_process_ = worker_process.Pass();
+ local_process_exit_event = worker_process_;
+ }
+
+ if (!ResumeThread(worker_thread)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
+ KillProcess(CONTROL_C_EXIT);
+ return false;
+ }
+
+ // Return a handle that the caller can wait on to get notified when
+ // the process terminates.
+ ScopedHandle process_exit_event;
+ if (!DuplicateHandle(GetCurrentProcess(),
+ local_process_exit_event,
+ GetCurrentProcess(),
+ process_exit_event.Receive(),
+ SYNCHRONIZE,
+ FALSE,
+ 0)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
+ KillProcess(CONTROL_C_EXIT);
+ return false;
+ }
+
+ *process_exit_event_out = process_exit_event.Pass();
+ return true;
+}
+
+bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) {
+ if (base::win::GetVersion() == base::win::VERSION_XP)
+ launch_elevated_ = false;
+
+ if (launch_elevated_) {
+ process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
+ if (!process_exit_event_.IsValid()) {
+ LOG(ERROR) << "Failed to create a nameless event";
+ return false;
+ }
+
+ // To receive job object notifications the job object is registered with
+ // the completion port represented by |io_task_runner|. The registration has
+ // to be done on the I/O thread because
+ // MessageLoopForIO::RegisterJobObject() can only be called via
+ // MessageLoopForIO::current().
+ io_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&Core::InitializeJob, this));
+ }
+
+ // Create a session token for the launched process.
+ return CreateSessionToken(session_id, &session_token_);
+}
+
+void WtsSessionProcessDelegate::Core::Stop() {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ if (!stopping_) {
+ stopping_ = true;
+
+ // Drain the completion queue to make sure all job object notifications have
+ // been received.
+ DrainJobNotificationsCompleted();
+ }
+}
+
+WtsSessionProcessDelegate::Core::~Core() {
+}
+
+void WtsSessionProcessDelegate::Core::DrainJobNotifications() {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+ // DrainJobNotifications() is posted after the job object is destroyed, so
+ // by this time all notifications from the job object have been processed
+ // already. Let the main thread know that the queue has been drained.
+ main_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &Core::DrainJobNotificationsCompleted, this));
+}
+
+void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ if (job_.IsValid()) {
+ job_.Close();
+
+ // Drain the completion queue to make sure all job object notification have
+ // been received.
+ io_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &Core::DrainJobNotifications, this));
+ }
+}
+
+void WtsSessionProcessDelegate::Core::InitializeJob() {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
+
+ ScopedHandle job;
+ job.Set(CreateJobObject(NULL, NULL));
+ if (!job.IsValid()) {
+ LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
+ return;
+ }
+
+ // Limit the number of active processes in the job to two (the process
+ // performing elevation and the host) and make sure that all processes will be
+ // killed once the job object is destroyed.
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
+ memset(&info, 0, sizeof(info));
+ info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ info.BasicLimitInformation.ActiveProcessLimit = 2;
+ if (!SetInformationJobObject(job,
+ JobObjectExtendedLimitInformation,
+ &info,
+ sizeof(info))) {
+ LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
+ return;
+ }
+
+ // Register to receive job notifications via the I/O thread's completion port.
+ if (!MessageLoopForIO::current()->RegisterJobObject(job, this)) {
+ LOG_GETLASTERROR(ERROR)
+ << "Failed to associate the job object with a completion port";
+ return;
+ }
+
+ // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
+ // pointer.
+ scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
+ *job_wrapper = job.Pass();
+
+ // Let the main thread know that initialization is complete.
+ main_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &Core::InitializeJobCompleted, this, base::Passed(&job_wrapper)));
+}
+
+void WtsSessionProcessDelegate::Core::InitializeJobCompleted(
+ scoped_ptr<ScopedHandle> job) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+ DCHECK(!job_.IsValid());
+
+ job_ = job->Pass();
+}
+
+void WtsSessionProcessDelegate::Core::OnJobNotification(DWORD message,
+ DWORD pid) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ switch (message) {
+ case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
+ CHECK(SetEvent(process_exit_event_));
+ break;
+
+ case JOB_OBJECT_MSG_NEW_PROCESS:
+ // We report the exit code of the worker process to be |CONTROL_C_EXIT|
+ // if we cannot get the actual exit code. So here we can safely ignore
+ // the error returned by OpenProcess().
+ worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
+ break;
+ }
+}
+
+WtsSessionProcessDelegate::WtsSessionProcessDelegate(
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ const FilePath& binary_path,
+ uint32 session_id,
+ bool launch_elevated) {
+ core_ = new Core(main_task_runner, io_task_runner, binary_path,
+ launch_elevated);
+ if (!core_->Initialize(session_id)) {
+ core_->Stop();
+ core_ = NULL;
+ }
+}
+
+WtsSessionProcessDelegate::~WtsSessionProcessDelegate() {
+ core_->Stop();
+}
+
+DWORD WtsSessionProcessDelegate::GetExitCode() {
+ if (!core_)
+ return CONTROL_C_EXIT;
+
+ return core_->GetExitCode();
+}
+
+void WtsSessionProcessDelegate::KillProcess(DWORD exit_code) {
+ if (core_) {
+ core_->KillProcess(exit_code);
+ }
+}
+
+bool WtsSessionProcessDelegate::LaunchProcess(
+ const std::string& channel_name,
+ base::win::ScopedHandle* process_exit_event_out) {
+ if (!core_)
+ return false;
+
+ return core_->LaunchProcess(channel_name, process_exit_event_out);
+}
+
+} // namespace remoting
diff --git a/remoting/host/win/wts_session_process_delegate.h b/remoting/host/win/wts_session_process_delegate.h
new file mode 100644
index 0000000..106820e
--- /dev/null
+++ b/remoting/host/win/wts_session_process_delegate.h
@@ -0,0 +1,51 @@
+// 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_WIN_WTS_SESSION_PROCESS_DELEGATE_H_
+#define REMOTING_HOST_WIN_WTS_SESSION_PROCESS_DELEGATE_H_
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/memory/ref_counted.h"
+#include "remoting/host/win/worker_process_launcher.h"
+
+class FilePath;
+
+namespace base {
+class SingleThreadTaskRunner;
+} // namespace base
+
+namespace remoting {
+
+// Implements logic for launching and monitoring a worker process in a different
+// session.
+class WtsSessionProcessDelegate
+ : public WorkerProcessLauncher::Delegate {
+ public:
+ WtsSessionProcessDelegate(
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ const FilePath& binary_path,
+ uint32 session_id,
+ bool launch_elevated);
+ ~WtsSessionProcessDelegate();
+
+ // WorkerProcessLauncher::Delegate implementation.
+ virtual DWORD GetExitCode() OVERRIDE;
+ virtual void KillProcess(DWORD exit_code) OVERRIDE;
+ virtual bool LaunchProcess(
+ const std::string& channel_name,
+ base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
+
+ private:
+ // The actual implementation resides in WtsSessionProcessDelegate::Core class.
+ class Core;
+ scoped_refptr<Core> core_;
+
+ DISALLOW_COPY_AND_ASSIGN(WtsSessionProcessDelegate);
+};
+
+} // namespace remoting
+
+#endif // REMOTING_HOST_WIN_WTS_SESSION_PROCESS_DELEGATE_H_
diff --git a/remoting/host/win/wts_session_process_launcher.cc b/remoting/host/win/wts_session_process_launcher.cc
deleted file mode 100644
index 9b991fa..0000000
--- a/remoting/host/win/wts_session_process_launcher.cc
+++ /dev/null
@@ -1,713 +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.
-//
-// This file implements the Windows service controlling Me2Me host processes
-// running within user sessions.
-
-#include "remoting/host/win/wts_session_process_launcher.h"
-
-#include <windows.h>
-#include <sddl.h>
-#include <limits>
-
-#include "base/base_switches.h"
-#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/logging.h"
-#include "base/single_thread_task_runner.h"
-#include "base/path_service.h"
-#include "base/rand_util.h"
-#include "base/stringprintf.h"
-#include "base/utf_string_conversions.h"
-#include "base/win/scoped_handle.h"
-#include "base/win/windows_version.h"
-#include "ipc/ipc_channel_proxy.h"
-#include "ipc/ipc_message.h"
-#include "ipc/ipc_message_macros.h"
-#include "remoting/host/chromoting_messages.h"
-#include "remoting/host/host_exit_codes.h"
-#include "remoting/host/win/launch_process_with_token.h"
-#include "remoting/host/win/wts_console_monitor.h"
-
-using base::win::ScopedHandle;
-using base::TimeDelta;
-
-namespace {
-
-// The minimum and maximum delays between attempts to inject host process into
-// a session.
-const int kMaxLaunchDelaySeconds = 60;
-const int kMinLaunchDelaySeconds = 1;
-
-const FilePath::CharType kMe2meHostBinaryName[] =
- FILE_PATH_LITERAL("remoting_host.exe");
-
-const FilePath::CharType kDaemonBinaryName[] =
- FILE_PATH_LITERAL("remoting_daemon.exe");
-
-// 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[] = {
- "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 kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)";
-
-} // namespace
-
-namespace remoting {
-
-class WtsSessionProcessLauncherImpl
- : public base::RefCountedThreadSafe<WtsSessionProcessLauncherImpl>,
- public WorkerProcessLauncher::Delegate {
- public:
- // Returns the exit code of the worker process.
- virtual DWORD GetExitCode() = 0;
-
- // Stops the object asynchronously.
- virtual void Stop() = 0;
-
- protected:
- friend class base::RefCountedThreadSafe<WtsSessionProcessLauncherImpl>;
- virtual ~WtsSessionProcessLauncherImpl();
-};
-
-namespace {
-
-// Implements |WorkerProcessLauncher::Delegate| that starts the specified
-// process in a different session via CreateProcessAsUser() and uses
-// the returned handle to monitor and terminate the process.
-class SingleProcessLauncher : public WtsSessionProcessLauncherImpl {
- public:
- SingleProcessLauncher(
- uint32 session_id,
- const FilePath& binary_path,
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
-
- // WorkerProcessLauncher::Delegate implementation.
- virtual bool DoLaunchProcess(
- const std::string& channel_name,
- ScopedHandle* process_exit_event_out) OVERRIDE;
- virtual void DoKillProcess(DWORD exit_code) OVERRIDE;
-
- // WtsSessionProcessLauncherImpl implementation.
- virtual DWORD GetExitCode() OVERRIDE;
- virtual void Stop() OVERRIDE;
-
- private:
- FilePath binary_path_;
-
- // The task runner all public methods of this class should be called on.
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
-
- // A handle that becomes signalled once the process associated has been
- // terminated.
- ScopedHandle process_exit_event_;
-
- // The token to be used to launch a process in a different session.
- ScopedHandle user_token_;
-
- // The handle of the worker process, if launched.
- ScopedHandle worker_process_;
-
- DISALLOW_COPY_AND_ASSIGN(SingleProcessLauncher);
-};
-
-// Implements |WorkerProcessLauncher::Delegate| that starts the specified
-// process (UAC) elevated in a different session. |ElevatedProcessLauncher|
-// utilizes a helper process to bypass limitations of ShellExecute() which
-// cannot spawn processes across the session boundary.
-class ElevatedProcessLauncher : public WtsSessionProcessLauncherImpl,
- public base::MessagePumpForIO::IOHandler {
- public:
- ElevatedProcessLauncher(
- uint32 session_id,
- const FilePath& binary_path,
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
-
- // base::MessagePumpForIO::IOHandler implementation.
- virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
- DWORD bytes_transferred,
- DWORD error) 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;
-
- // WtsSessionProcessLauncherImpl implementation.
- virtual DWORD GetExitCode() OVERRIDE;
- virtual void Stop() OVERRIDE;
-
- private:
- // Drains the completion port queue to make sure that all job object
- // notifications have been received.
- void DrainJobNotifications();
-
- // Notified that the completion port queue has been drained.
- void DrainJobNotificationsCompleted();
-
- // Creates and initializes the job object that will sandbox the launched child
- // processes.
- void InitializeJob();
-
- // Notified that the job object initialization is complete.
- void InitializeJobCompleted(scoped_ptr<ScopedHandle> job);
-
- // Called to process incoming job object notifications.
- void OnJobNotification(DWORD message, DWORD pid);
-
- FilePath binary_path_;
-
- // The task runner all public methods of this class should be called on.
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
-
- // The task runner serving job object notifications.
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
-
- // The job object used to control the lifetime of child processes.
- ScopedHandle job_;
-
- // A waiting handle that becomes signalled once all process associated with
- // the job have been terminated.
- ScopedHandle process_exit_event_;
-
- // The token to be used to launch a process in a different session.
- ScopedHandle user_token_;
-
- // The handle of the worker process, if launched.
- ScopedHandle worker_process_;
-
- DISALLOW_COPY_AND_ASSIGN(ElevatedProcessLauncher);
-};
-
-SingleProcessLauncher::SingleProcessLauncher(
- uint32 session_id,
- const FilePath& binary_path,
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
- : binary_path_(binary_path),
- main_task_runner_(main_task_runner) {
- CHECK(CreateSessionToken(session_id, &user_token_));
-}
-
-bool SingleProcessLauncher::DoLaunchProcess(
- const std::string& channel_name,
- ScopedHandle* process_exit_event_out) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- // Create the command line passing the name of the IPC channel to use and
- // copying known switches from the caller's command line.
- CommandLine command_line(binary_path_);
- command_line.AppendSwitchNative(kDaemonIpcSwitchName,
- UTF8ToWide(channel_name));
- command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
- kCopiedSwitchNames,
- _countof(kCopiedSwitchNames));
-
- // Try to launch the process and attach an object watcher to the returned
- // handle so that we get notified when the process terminates.
- ScopedHandle worker_thread;
- worker_process_.Close();
- if (!LaunchProcessWithToken(binary_path_,
- command_line.GetCommandLineString(),
- user_token_,
- 0,
- &worker_process_,
- &worker_thread)) {
- return false;
- }
-
- ScopedHandle process_exit_event;
- if (!DuplicateHandle(GetCurrentProcess(),
- worker_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 SingleProcessLauncher::DoKillProcess(DWORD exit_code) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- if (worker_process_.IsValid()) {
- TerminateProcess(worker_process_, exit_code);
- }
-}
-
-DWORD SingleProcessLauncher::GetExitCode() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- DWORD exit_code = CONTROL_C_EXIT;
- if (worker_process_.IsValid()) {
- if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
- LOG_GETLASTERROR(INFO)
- << "Failed to query the exit code of the worker process";
- exit_code = CONTROL_C_EXIT;
- }
-
- worker_process_.Close();
- }
-
- return exit_code;
-}
-
-void SingleProcessLauncher::Stop() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-}
-
-ElevatedProcessLauncher::ElevatedProcessLauncher(
- uint32 session_id,
- const FilePath& binary_path,
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
- scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
- : binary_path_(binary_path),
- main_task_runner_(main_task_runner),
- io_task_runner_(io_task_runner) {
- process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
- CHECK(process_exit_event_.IsValid());
-
- CHECK(CreateSessionToken(session_id, &user_token_));
-
- // To receive job object notifications the job object is registered with
- // the completion port represented by |io_task_runner|. The registration has
- // to be done on the I/O thread because MessageLoopForIO::RegisterJobObject()
- // can only be called via MessageLoopForIO::current().
- io_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&ElevatedProcessLauncher::InitializeJob, this));
-}
-
-void ElevatedProcessLauncher::OnIOCompleted(
- base::MessagePumpForIO::IOContext* context,
- DWORD bytes_transferred,
- DWORD error) {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
-
- // |bytes_transferred| is used in job object notifications to supply
- // the message ID; |context| carries process ID.
- main_task_runner_->PostTask(FROM_HERE, base::Bind(
- &ElevatedProcessLauncher::OnJobNotification, this, bytes_transferred,
- reinterpret_cast<DWORD>(context)));
-}
-
-bool ElevatedProcessLauncher::DoLaunchProcess(
- const std::string& channel_name,
- ScopedHandle* process_exit_event_out) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- // The job object is not ready. Retry starting the host process later.
- if (!job_.IsValid()) {
- return false;
- }
-
- // Construct the helper 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 daemon_binary = dir_path.Append(kDaemonBinaryName);
-
- // Create the command line passing the name of the IPC channel to use and
- // copying known switches from the caller's command line.
- CommandLine command_line(daemon_binary);
- command_line.AppendSwitchPath(kElevateSwitchName, binary_path_);
- command_line.AppendSwitchNative(kDaemonIpcSwitchName,
- UTF8ToWide(channel_name));
- command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
- kCopiedSwitchNames,
- _countof(kCopiedSwitchNames));
-
- CHECK(ResetEvent(process_exit_event_));
-
- // Try to launch the process and attach an object watcher to the returned
- // handle so that we get notified when the process terminates.
- ScopedHandle worker_process;
- ScopedHandle worker_thread;
- if (!LaunchProcessWithToken(daemon_binary,
- command_line.GetCommandLineString(),
- user_token_,
- CREATE_SUSPENDED,
- &worker_process,
- &worker_thread)) {
- return false;
- }
-
- if (!AssignProcessToJobObject(job_, worker_process)) {
- LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object";
- TerminateProcess(worker_process, CONTROL_C_EXIT);
- return false;
- }
-
- if (!ResumeThread(worker_thread)) {
- LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
- DoKillProcess(CONTROL_C_EXIT);
- return false;
- }
-
- ScopedHandle process_exit_event;
- if (!DuplicateHandle(GetCurrentProcess(),
- process_exit_event_,
- 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 ElevatedProcessLauncher::DoKillProcess(DWORD exit_code) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- if (job_.IsValid()) {
- TerminateJobObject(job_, exit_code);
- }
-}
-
-DWORD ElevatedProcessLauncher::GetExitCode() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- DWORD exit_code = CONTROL_C_EXIT;
- if (worker_process_.IsValid()) {
- if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
- LOG_GETLASTERROR(INFO)
- << "Failed to query the exit code of the worker process";
- exit_code = CONTROL_C_EXIT;
- }
-
- worker_process_.Close();
- }
-
- return exit_code;
-}
-
-void ElevatedProcessLauncher::Stop() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- // Drain the completion queue to make sure all job object notification have
- // been received.
- DrainJobNotificationsCompleted();
-}
-
-void ElevatedProcessLauncher::DrainJobNotifications() {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
-
- // DrainJobNotifications() is posted after the job object is destroyed, so
- // by this time all notifications from the job object have been processed
- // already. Let the main thread know that the queue has been drained.
- main_task_runner_->PostTask(FROM_HERE, base::Bind(
- &ElevatedProcessLauncher::DrainJobNotificationsCompleted, this));
-}
-
-void ElevatedProcessLauncher::DrainJobNotificationsCompleted() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- if (job_.IsValid()) {
- job_.Close();
-
- // Drain the completion queue to make sure all job object notification have
- // been received.
- io_task_runner_->PostTask(FROM_HERE, base::Bind(
- &ElevatedProcessLauncher::DrainJobNotifications, this));
- }
-}
-
-void ElevatedProcessLauncher::InitializeJob() {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
-
- ScopedHandle job;
- job.Set(CreateJobObject(NULL, NULL));
- if (!job.IsValid()) {
- LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
- return;
- }
-
- // Limit the number of active processes in the job to two (the process
- // performing elevation and the host) and make sure that all processes will be
- // killed once the job object is destroyed.
- JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
- memset(&info, 0, sizeof(info));
- info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
- JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
- info.BasicLimitInformation.ActiveProcessLimit = 2;
- if (!SetInformationJobObject(job,
- JobObjectExtendedLimitInformation,
- &info,
- sizeof(info))) {
- LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
- return;
- }
-
- // Register the job object with the completion port in the I/O thread to
- // receive job notifications.
- if (!MessageLoopForIO::current()->RegisterJobObject(job, this)) {
- LOG_GETLASTERROR(ERROR)
- << "Failed to associate the job object with a completion port";
- return;
- }
-
- // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
- // pointer.
- scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
- *job_wrapper = job.Pass();
-
- // Let the main thread know that initialization is complete.
- main_task_runner_->PostTask(FROM_HERE, base::Bind(
- &ElevatedProcessLauncher::InitializeJobCompleted, this,
- base::Passed(&job_wrapper)));
-}
-
-void ElevatedProcessLauncher::InitializeJobCompleted(
- scoped_ptr<ScopedHandle> job) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
- DCHECK(!job_.IsValid());
-
- job_ = job->Pass();
-}
-
-void ElevatedProcessLauncher::OnJobNotification(DWORD message, DWORD pid) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- switch (message) {
- case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
- CHECK(SetEvent(process_exit_event_));
- break;
-
- case JOB_OBJECT_MSG_NEW_PROCESS:
- // We report the exit code of the worker process to be |CONTROL_C_EXIT|
- // if we cannot get the actual exit code. So here we can safely ignore
- // the error returned by OpenProcess().
- worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
- break;
- }
-}
-
-} // namespace
-
-WtsSessionProcessLauncherImpl::~WtsSessionProcessLauncherImpl() {
-}
-
-WtsSessionProcessLauncher::WtsSessionProcessLauncher(
- const base::Closure& stopped_callback,
- WtsConsoleMonitor* monitor,
- scoped_refptr<base::SingleThreadTaskRunner> main_message_loop,
- scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop)
- : Stoppable(main_message_loop, stopped_callback),
- attached_(false),
- main_message_loop_(main_message_loop),
- ipc_message_loop_(ipc_message_loop),
- monitor_(monitor) {
- monitor_->AddWtsConsoleObserver(this);
-
- // 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.";
- Stop();
- return;
- }
- host_binary_ = dir_path.Append(kMe2meHostBinaryName);
-}
-
-WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
- // 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);
-
- monitor_->RemoveWtsConsoleObserver(this);
-
- CHECK(!attached_);
- CHECK(!timer_.IsRunning());
-}
-
-void WtsSessionProcessLauncher::OnChannelConnected() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
-}
-
-bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
-
- return false;
-}
-
-void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
-
- if (stoppable_state() != Stoppable::kRunning) {
- return;
- }
-
- DCHECK(!attached_);
- DCHECK(!timer_.IsRunning());
-
- attached_ = true;
-
- // Reset the backoff timeout every time the session is switched.
- launch_backoff_ = base::TimeDelta();
-
- // The workaround we use to launch a process in a not yet logged in session
- // (see CreateRemoteSessionProcess()) on Windows XP/2K3 does not let us to
- // assign the started process to a job. However on Vista+ we have to start
- // the host via a helper process. The helper process calls ShellExecute() that
- // does not work across the session boundary but it required to launch
- // a binary specifying uiAccess='true' in its manifest.
- //
- // Below we choose which implementation of |WorkerProcessLauncher::Delegate|
- // to use. A single process is launched on XP (since uiAccess='true' does not
- // have effect any way). Vista+ utilizes a helper process and assign it to
- // a job object to control it.
- if (new_impl_.get()) {
- new_impl_->Stop();
- new_impl_ = NULL;
- }
- if (base::win::GetVersion() == base::win::VERSION_XP) {
- new_impl_ = new SingleProcessLauncher(session_id, host_binary_,
- main_message_loop_);
- } else {
- new_impl_ = new ElevatedProcessLauncher(session_id, host_binary_,
- main_message_loop_,
- ipc_message_loop_);
- }
-
- if (launcher_.get() == NULL) {
- LaunchProcess();
- }
-}
-
-void WtsSessionProcessLauncher::OnSessionDetached() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
- DCHECK(attached_);
-
- attached_ = false;
- launch_backoff_ = base::TimeDelta();
- timer_.Stop();
-
- if (launcher_.get() != NULL) {
- launcher_->Stop();
- }
-}
-
-void WtsSessionProcessLauncher::DoStop() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
-
- if (attached_) {
- OnSessionDetached();
- }
-
- // Don't complete shutdown if |launcher_| is not completely stopped.
- if (launcher_.get() != NULL) {
- return;
- }
-
- // Tear down implementation objects asynchromously.
- if (new_impl_.get()) {
- new_impl_->Stop();
- new_impl_ = NULL;
- }
- if (impl_.get()) {
- impl_->Stop();
- impl_ = NULL;
- }
-
- CompleteStopping();
-}
-
-void WtsSessionProcessLauncher::LaunchProcess() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
- DCHECK(attached_);
- DCHECK(launcher_.get() == NULL);
- DCHECK(!timer_.IsRunning());
-
- // Switch to a new implementation object if needed.
- if (new_impl_.get() != NULL) {
- if (impl_.get() != NULL) {
- impl_->Stop();
- impl_ = NULL;
- }
-
- impl_.swap(new_impl_);
- }
-
- DCHECK(impl_.get() != NULL);
-
- launch_time_ = base::Time::Now();
- launcher_.reset(new WorkerProcessLauncher(
- impl_.get(), this,
- base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
- base::Unretained(this)),
- main_message_loop_,
- ipc_message_loop_));
- launcher_->Start(kDaemonIpcSecurityDescriptor);
-}
-
-void WtsSessionProcessLauncher::OnLauncherStopped() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
-
- // Retrieve the exit code of the worker process.
- DWORD exit_code = impl_->GetExitCode();
-
- launcher_.reset(NULL);
-
- // Do not relaunch the worker 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;
- }
-
- // Try to restart the worker process if we are still attached to a session.
- if (attached_) {
- // 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, &WtsSessionProcessLauncher::LaunchProcess);
- }
-}
-
-} // namespace remoting
diff --git a/remoting/host/win/wts_session_process_launcher.h b/remoting/host/win/wts_session_process_launcher.h
deleted file mode 100644
index e9a8e54..0000000
--- a/remoting/host/win/wts_session_process_launcher.h
+++ /dev/null
@@ -1,113 +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_WIN_WTS_SESSION_PROCESS_LAUNCHER_H_
-#define REMOTING_HOST_WIN_WTS_SESSION_PROCESS_LAUNCHER_H_
-
-#include <windows.h>
-
-#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"
-#include "base/process.h"
-#include "base/time.h"
-#include "base/timer.h"
-#include "base/win/scoped_handle.h"
-#include "ipc/ipc_channel.h"
-#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 remoting {
-
-class WtsConsoleMonitor;
-class WtsSessionProcessLauncherImpl;
-
-class WtsSessionProcessLauncher
- : public Stoppable,
- public WorkerProcessIpcDelegate,
- public WtsConsoleObserver {
- public:
- // Constructs a WtsSessionProcessLauncher object. |stopped_callback| and
- // |main_message_loop| are passed to the undelying |Stoppable| implementation.
- // All interaction with |monitor| should happen on |main_message_loop|.
- // |ipc_message_loop| must be an I/O message loop.
- WtsSessionProcessLauncher(
- const base::Closure& stopped_callback,
- WtsConsoleMonitor* monitor,
- scoped_refptr<base::SingleThreadTaskRunner> main_message_loop,
- scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop);
-
- virtual ~WtsSessionProcessLauncher();
-
- // WorkerProcessIpcDelegate implementation.
- virtual void OnChannelConnected() OVERRIDE;
- virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
-
- // WtsConsoleObserver implementation.
- virtual void OnSessionAttached(uint32 session_id) OVERRIDE;
- virtual void OnSessionDetached() OVERRIDE;
-
- protected:
- // Stoppable implementation.
- virtual void DoStop() OVERRIDE;
-
- private:
- // Attempts to launch the host process in the current console session.
- // Schedules next launch attempt if creation of the process fails for any
- // reason.
- void LaunchProcess();
-
- // Called when the launcher reports the process to be stopped.
- void OnLauncherStopped();
-
- // |true| if this object is currently attached to the console session.
- bool attached_;
-
- // Path to the host binary to launch.
- FilePath host_binary_;
-
- // OS version dependent WorkerProcessLauncher::Delegate implementation
- // instance for the currently-running child process.
- scoped_refptr<WtsSessionProcessLauncherImpl> impl_;
-
- // WorkerProcessLauncher::Delegate implementation that will be used to launch
- // the new child process after a session switch.
- scoped_refptr<WtsSessionProcessLauncherImpl> new_impl_;
-
- // 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<WtsSessionProcessLauncher> timer_;
-
- // The main service message loop.
- scoped_refptr<base::SingleThreadTaskRunner> main_message_loop_;
-
- // Message loop used by the IPC channel.
- scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop_;
-
- // This pointer is used to unsubscribe from session attach and detach events.
- WtsConsoleMonitor* monitor_;
-
- scoped_ptr<WorkerProcessLauncher> launcher_;
-
- DISALLOW_COPY_AND_ASSIGN(WtsSessionProcessLauncher);
-};
-
-} // namespace remoting
-
-#endif // REMOTING_HOST_WIN_WTS_SESSION_PROCESS_LAUNCHER_H_
diff --git a/remoting/host/worker_process_ipc_delegate.h b/remoting/host/worker_process_ipc_delegate.h
index 6a8ff58..4a03e8f 100644
--- a/remoting/host/worker_process_ipc_delegate.h
+++ b/remoting/host/worker_process_ipc_delegate.h
@@ -25,6 +25,9 @@ class WorkerProcessIpcDelegate {
// Processes messages sent by the client.
virtual bool OnMessageReceived(const IPC::Message& message) = 0;
+
+ // Notifies that a permanent error was encountered.
+ virtual void OnPermanentError() = 0;
};
} // namespace remoting
diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp
index 283db3c..48ae65d 100644
--- a/remoting/remoting.gyp
+++ b/remoting/remoting.gyp
@@ -592,6 +592,7 @@
'../base/base.gyp:base_static',
'../base/third_party/dynamic_annotations/dynamic_annotations.gyp:dynamic_annotations',
'../ipc/ipc.gyp:ipc',
+ '../net/net.gyp:net',
'remoting_base',
'remoting_breakpad',
'remoting_version_resources',
@@ -619,12 +620,16 @@
'host/win/launch_process_with_token.h',
'host/win/omaha.cc',
'host/win/omaha.h',
+ 'host/win/unprivileged_process_delegate.cc',
+ 'host/win/unprivileged_process_delegate.h',
'host/win/worker_process_launcher.cc',
'host/win/worker_process_launcher.h',
'host/win/wts_console_monitor.h',
'host/win/wts_console_observer.h',
- 'host/win/wts_session_process_launcher.cc',
- 'host/win/wts_session_process_launcher.h',
+ 'host/win/wts_console_session_process_driver.cc',
+ 'host/win/wts_console_session_process_driver.h',
+ 'host/win/wts_session_process_delegate.cc',
+ 'host/win/wts_session_process_delegate.h',
'host/worker_process_ipc_delegate.h',
],
'msvs_settings': {
@@ -1944,6 +1949,9 @@
'host/video_frame_capturer_helper_unittest.cc',
'host/video_frame_capturer_mac_unittest.cc',
'host/video_frame_capturer_unittest.cc',
+ 'host/win/worker_process_launcher.cc',
+ 'host/win/worker_process_launcher.h',
+ 'host/win/worker_process_launcher_unittest.cc',
'jingle_glue/chromium_socket_factory_unittest.cc',
'jingle_glue/fake_signal_strategy.cc',
'jingle_glue/fake_signal_strategy.h',
@@ -1981,6 +1989,9 @@
],
'conditions': [
[ 'OS=="win"', {
+ 'dependencies': [
+ '../ipc/ipc.gyp:ipc',
+ ],
'include_dirs': [
'../breakpad/src',
],