Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(2114)

Unified Diff: remoting/host/win/wts_session_process_launcher.cc

Issue 10911267: [Chromoting] Don't use a job object to control the me2me host process on Windows XP. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: CR feedback. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: remoting/host/win/wts_session_process_launcher.cc
diff --git a/remoting/host/win/wts_session_process_launcher.cc b/remoting/host/win/wts_session_process_launcher.cc
index d659f156be51f2a91e7d86242f0edb463f28fa41..ee46c13731a1c93603fcc860d7baad31d89e739e 100644
--- a/remoting/host/win/wts_session_process_launcher.cc
+++ b/remoting/host/win/wts_session_process_launcher.cc
@@ -24,6 +24,7 @@
#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"
@@ -66,19 +67,233 @@ const char kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)";
namespace remoting {
-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),
- job_state_(kJobUninitialized) {
- monitor_->AddWtsConsoleObserver(this);
+class WtsProcessLauncherImpl
+ : public base::RefCountedThreadSafe<WtsProcessLauncherImpl>,
+ public WorkerProcessLauncher::Delegate {
+ public:
+ // Returns the exit code of the worker process.
+ virtual DWORD GetExitCode() = 0;
+
+ // Sets the token to be used to launch the worker process.
+ virtual bool SetSessionToken(HANDLE session_token) = 0;
+
+ // Stops the object asynchronously.
+ virtual void Stop() = 0;
+
+ protected:
+ friend class base::RefCountedThreadSafe<WtsProcessLauncherImpl>;
+ virtual ~WtsProcessLauncherImpl();
+};
+
+namespace {
+
+// Implements |WorkerProcessLauncher::Delegate| that starts the host process
Wez 2012/09/14 23:36:18 nit: host process -> specified process?
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+// in a different session via CreateProcessAsUser and uses the returned process
+// handle to control it.
Wez 2012/09/14 23:36:18 nit: ... to monitor and close it. ?
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+class SingleProcessLauncher : public WtsProcessLauncherImpl {
+ public:
+ SingleProcessLauncher(
+ const FilePath& binary,
Wez 2012/09/14 23:36:18 nit: binary_path
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
+
+ // WorkerProcessLauncher::Delegate implementation.
+ virtual bool DoLaunchProcess(
+ const std::string& channel_name,
+ base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
+ virtual void DoKillProcess(DWORD exit_code) OVERRIDE;
+
+ // WtsProcessLauncherImpl implementation.
+ virtual DWORD GetExitCode() OVERRIDE;
+ virtual bool SetSessionToken(HANDLE session_token) OVERRIDE;
+ virtual void Stop() OVERRIDE;
+
+ private:
+ FilePath binary_;
+
+ // The main service message loop.
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+ // A waiting handle that becomes signalled once all process associated with
Wez 2012/09/14 23:36:18 nit: Handle that becomes ... once the process has
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+ // 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_;
+
+ base::win::ScopedHandle worker_process_;
+
+ DISALLOW_COPY_AND_ASSIGN(SingleProcessLauncher);
+};
+
+// Implements |WorkerProcessLauncher::Delegate| that starts the host process
+// in a different session and uses job object to control it. This object
Wez 2012/09/14 23:36:18 nit: You mention it uses [a] job object, and then
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+// utilizes a helper process to bypass limitations of ShellExecute() which
+// cannot spawn processes across the session boundary. ShellExecute() in its
+// turn is needed to start a binary specifying uiAccess='true' in its manifest.
+class InJobLauncher : public WtsProcessLauncherImpl,
Wez 2012/09/14 23:36:18 nit: ShellExecuteLauncher? Then you can describe
alexeypa (please no reviews) 2012/09/18 16:58:06 Thinking about what this class is actually doing,
+ public base::MessagePumpForIO::IOHandler {
+ public:
+ InJobLauncher(
+ const FilePath& binary,
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> ipc_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,
+ base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
+ virtual void DoKillProcess(DWORD exit_code) OVERRIDE;
+
+ // WtsProcessLauncherImpl implementation.
+ virtual DWORD GetExitCode() OVERRIDE;
+ virtual bool SetSessionToken(HANDLE session_token) OVERRIDE;
+ virtual void Stop() OVERRIDE;
+
+ private:
+ // Drains the completion port queue to make sure that all job object
+ // notifications has been received.
Wez 2012/09/14 23:36:18 typo: has -> have
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+ void DrainJobNotifications();
+
+ // Notifies that the completion port queue has been drained.
+ void DrainJobNotificationsCompleted();
Wez 2012/09/14 23:36:18 Does this method trigger a notification, or is it
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+
+ // Creates and initializes the job object that will sandbox the launched child
+ // processes.
+ void InitializeJob();
+
+ // Notifies that the job object initialization is complete.
Wez 2012/09/14 23:36:18 nit: See above re "Notified when..."
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+ void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job);
+
+ void OnJobNotification(DWORD message, DWORD pid);
Wez 2012/09/14 23:36:18 nit: What's this for?
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+
+ FilePath binary_;
+
+ // The main service message loop.
Wez 2012/09/14 23:36:18 Can you re-describe this in terms of what this cla
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+
+ // Message loop used by the IPC channel.
+ scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
+
+ // The job object used to control the lifetime of child processes.
+ base::win::ScopedHandle job_;
+
+ // A waiting handle that becomes signalled once all process 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_;
+
+ base::win::ScopedHandle worker_process_;
Wez 2012/09/14 23:36:18 nit: Handle to the worker process, if launched?
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+
+ DISALLOW_COPY_AND_ASSIGN(InJobLauncher);
+};
+
+SingleProcessLauncher::SingleProcessLauncher(
+ const FilePath& binary,
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
+ : binary_(binary),
+ main_task_runner_(main_task_runner) {
+}
+
+bool SingleProcessLauncher::DoLaunchProcess(
+ const std::string& channel_name,
+ base::win::ScopedHandle* process_exit_event_out) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ // Create the host process command line passing the name of the IPC channel
Wez 2012/09/14 23:36:18 nit: host process command line -> command line?
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+ // to use and copying known switches from the service's command line.
Wez 2012/09/14 23:36:18 nit: service's -> caller's?
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+ CommandLine command_line(binary_);
+ 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.
+ base::win::ScopedHandle worker_thread;
+ worker_process_.Close();
+ if (!LaunchProcessWithToken(binary_,
+ command_line.GetCommandLineString(),
+ session_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() {
+ 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;
+}
+
+bool SingleProcessLauncher::SetSessionToken(HANDLE session_token) {
Wez 2012/09/14 23:36:18 nit: Rename this ResetUserToken() and have it take
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
+ session_token_.Close();
+ if (!DuplicateHandle(GetCurrentProcess(),
+ session_token,
+ GetCurrentProcess(),
+ session_token_.Receive(),
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
+ return false;
+ }
+
+ return true;
+}
+void SingleProcessLauncher::Stop() {
+}
+
+InJobLauncher::InJobLauncher(
+ const FilePath& binary,
+ scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
+ scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner)
+ : binary_(binary),
+ main_task_runner_(main_task_runner),
+ ipc_task_runner_(ipc_task_runner) {
process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
CHECK(process_exit_event_.IsValid());
@@ -86,60 +301,45 @@ WtsSessionProcessLauncher::WtsSessionProcessLauncher(
// port represented by |ipc_message_loop|. The registration has to be done on
// the I/O thread because MessageLoopForIO::RegisterJobObject() can only be
// called via MessageLoopForIO::current().
- ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
- &WtsSessionProcessLauncher::InitializeJob,
- base::Unretained(this)));
+ ipc_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&InJobLauncher::InitializeJob, this));
}
-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::OnIOCompleted(
+void InJobLauncher::OnIOCompleted(
base::MessagePumpForIO::IOContext* context,
DWORD bytes_transferred,
DWORD error) {
- DCHECK(ipc_message_loop_->BelongsToCurrentThread());
+ DCHECK(ipc_task_runner_->BelongsToCurrentThread());
// |bytes_transferred| is used in job object notifications to supply
// the message ID; |context| carries process ID.
- main_message_loop_->PostTask(FROM_HERE, base::Bind(
- &WtsSessionProcessLauncher::OnJobNotification,
- base::Unretained(this), bytes_transferred,
+ main_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &InJobLauncher::OnJobNotification, this, bytes_transferred,
reinterpret_cast<DWORD>(context)));
}
-bool WtsSessionProcessLauncher::DoLaunchProcess(
+bool InJobLauncher::DoLaunchProcess(
const std::string& channel_name,
- ScopedHandle* process_exit_event_out) {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
+ base::win::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 host binary name.
+ // Construct the helper binary 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);
FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName);
// 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(service_binary);
- command_line.AppendSwitchPath(kElevateSwitchName, host_binary);
+ command_line.AppendSwitchPath(kElevateSwitchName, binary_);
command_line.AppendSwitchNative(kDaemonIpcSwitchName,
UTF8ToWide(channel_name));
command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
@@ -190,110 +390,76 @@ bool WtsSessionProcessLauncher::DoLaunchProcess(
return true;
}
-void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
+void InJobLauncher::DoKillProcess(DWORD exit_code) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
if (job_.IsValid()) {
TerminateJobObject(job_, exit_code);
}
}
-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());
+DWORD InJobLauncher::GetExitCode() {
+ 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;
+ }
- if (stoppable_state() != Stoppable::kRunning) {
- return;
+ worker_process_.Close();
}
- DCHECK(!attached_);
- DCHECK(!timer_.IsRunning());
-
- attached_ = true;
-
- // Create a session token for the launched process.
- if (!CreateSessionToken(session_id, &session_token_))
- return;
-
- // Now try to launch the host.
- LaunchProcess();
+ return exit_code;
}
-void WtsSessionProcessLauncher::OnSessionDetached() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
- DCHECK(attached_);
-
- attached_ = false;
- launch_backoff_ = base::TimeDelta();
+bool InJobLauncher::SetSessionToken(HANDLE session_token) {
session_token_.Close();
- timer_.Stop();
-
- if (launcher_.get() != NULL) {
- launcher_->Stop();
- }
-}
-
-void WtsSessionProcessLauncher::DoStop() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
-
- if (attached_) {
- OnSessionDetached();
+ if (!DuplicateHandle(GetCurrentProcess(),
+ session_token,
+ GetCurrentProcess(),
+ session_token_.Receive(),
+ 0,
+ FALSE,
+ DUPLICATE_SAME_ACCESS)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
+ return false;
}
- job_.Close();
+ return true;
+}
+void InJobLauncher::Stop() {
// Drain the completion queue to make sure all job object notification have
// been received.
- if (job_state_ == kJobRunning) {
- job_state_ = kJobStopping;
- ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
- &WtsSessionProcessLauncher::DrainJobNotifications,
- base::Unretained(this)));
- }
-
- // Don't complete shutdown if |launcher_| is not completely stopped.
- if (launcher_.get() != NULL) {
- return;
- }
-
- // Don't complete shutdown if the completion queue hasn't been drained.
- if (job_state_ != kJobUninitialized && job_state_ != kJobStopped) {
- return;
- }
-
- CompleteStopping();
+ DrainJobNotificationsCompleted();
}
-void WtsSessionProcessLauncher::DrainJobNotifications() {
- DCHECK(ipc_message_loop_->BelongsToCurrentThread());
+void InJobLauncher::DrainJobNotifications() {
+ DCHECK(ipc_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_message_loop_->PostTask(FROM_HERE, base::Bind(
- &WtsSessionProcessLauncher::DrainJobNotificationsCompleted,
- base::Unretained(this)));
+ main_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &InJobLauncher::DrainJobNotificationsCompleted, this));
}
-void WtsSessionProcessLauncher::DrainJobNotificationsCompleted() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
- DCHECK_EQ(job_state_, kJobStopping);
+void InJobLauncher::DrainJobNotificationsCompleted() {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
+
+ if (job_.IsValid()) {
+ job_.Close();
- job_state_ = kJobStopped;
- Stop();
+ // Drain the completion queue to make sure all job object notification have
+ // been received.
+ ipc_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &InJobLauncher::DrainJobNotifications, this));
+ }
}
-void WtsSessionProcessLauncher::InitializeJob() {
- DCHECK(ipc_message_loop_->BelongsToCurrentThread());
+void InJobLauncher::InitializeJob() {
+ DCHECK(ipc_task_runner_->BelongsToCurrentThread());
ScopedHandle job;
job.Set(CreateJobObject(NULL, NULL));
@@ -332,25 +498,21 @@ void WtsSessionProcessLauncher::InitializeJob() {
*job_wrapper = job.Pass();
// Let the main thread know that initialization is complete.
- main_message_loop_->PostTask(FROM_HERE, base::Bind(
- &WtsSessionProcessLauncher::InitializeJobCompleted,
- base::Unretained(this), base::Passed(&job_wrapper)));
+ main_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &InJobLauncher::InitializeJobCompleted, this,
+ base::Passed(&job_wrapper)));
}
-void WtsSessionProcessLauncher::InitializeJobCompleted(
+void InJobLauncher::InitializeJobCompleted(
scoped_ptr<ScopedHandle> job) {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
DCHECK(!job_.IsValid());
- DCHECK_EQ(job_state_, kJobUninitialized);
- if (stoppable_state() == Stoppable::kRunning) {
- job_ = job->Pass();
- job_state_ = kJobRunning;
- }
+ job_ = job->Pass();
}
-void WtsSessionProcessLauncher::OnJobNotification(DWORD message, DWORD pid) {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
+void InJobLauncher::OnJobNotification(DWORD message, DWORD pid) {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
switch (message) {
case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
@@ -366,6 +528,126 @@ void WtsSessionProcessLauncher::OnJobNotification(DWORD message, DWORD pid) {
}
}
+} // namespace
+
+WtsProcessLauncherImpl::~WtsProcessLauncherImpl() {
+}
+
+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;
+ }
+ FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
+
+ // 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 (base::win::GetVersion() == base::win::VERSION_XP) {
+ impl_ = new SingleProcessLauncher(host_binary, main_message_loop);
+ } else {
+ impl_ = new InJobLauncher(host_binary, main_message_loop, ipc_message_loop);
+ }
+}
+
+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;
+
+ // Create a session token and launch the host.
+ base::win::ScopedHandle session_token;
+ if (CreateSessionToken(session_id, &session_token) &&
+ impl_->SetSessionToken(session_token)) {
+ 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 the core asynchromously.
+ if (impl_.get()) {
+ impl_->Stop();
Wez 2012/09/14 23:36:18 nit: Stop() doesn't seem to actually stop anything
alexeypa (please no reviews) 2012/09/18 16:58:06 It does "stop" job object notification. I'd like t
+ impl_ = NULL;
+ }
+
+ CompleteStopping();
+}
+
void WtsSessionProcessLauncher::LaunchProcess() {
DCHECK(main_message_loop_->BelongsToCurrentThread());
DCHECK(attached_);
@@ -374,7 +656,7 @@ void WtsSessionProcessLauncher::LaunchProcess() {
launch_time_ = base::Time::Now();
launcher_.reset(new WorkerProcessLauncher(
- this, this,
+ impl_.get(), this,
base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
base::Unretained(this)),
main_message_loop_,
@@ -385,16 +667,8 @@ void WtsSessionProcessLauncher::LaunchProcess() {
void WtsSessionProcessLauncher::OnLauncherStopped() {
DCHECK(main_message_loop_->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();
- }
+ // Retrieve the exit code of the worker process.
+ DWORD exit_code = impl_->GetExitCode();
launcher_.reset(NULL);

Powered by Google App Engine
This is Rietveld 408576698