| 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..969609813f27a4c5a8949416ef2300aebb55457d 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,80 +67,275 @@ 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 WtsSessionProcessLauncherImpl
|
| + : public base::RefCountedThreadSafe<WtsSessionProcessLauncherImpl>,
|
| + public WorkerProcessLauncher::Delegate {
|
| + public:
|
| + // Returns the exit code of the worker process.
|
| + virtual DWORD GetExitCode() = 0;
|
|
|
| - process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
|
| - CHECK(process_exit_event_.IsValid());
|
| + // Sets the token to be used to launch the worker process.
|
| + virtual void ResetUserToken(ScopedHandle* user_token) = 0;
|
| +
|
| + // Stops the object asynchronously.
|
| + virtual void Stop() = 0;
|
| +
|
| + protected:
|
| + friend class base::RefCountedThreadSafe<WtsSessionProcessLauncherImpl>;
|
| + virtual ~WtsSessionProcessLauncherImpl();
|
| +};
|
| +
|
| +namespace {
|
|
|
| - // To receive job object notifications it is registered with the completion
|
| - // 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)));
|
| +// 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(
|
| + 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 ResetUserToken(ScopedHandle* user_token) 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(
|
| + 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 ResetUserToken(ScopedHandle* user_token) 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(
|
| + const FilePath& binary_path,
|
| + scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
|
| + : binary_path_(binary_path),
|
| + main_task_runner_(main_task_runner) {
|
| }
|
|
|
| -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);
|
| +bool SingleProcessLauncher::DoLaunchProcess(
|
| + const std::string& channel_name,
|
| + ScopedHandle* process_exit_event_out) {
|
| + DCHECK(main_task_runner_->BelongsToCurrentThread());
|
|
|
| - monitor_->RemoveWtsConsoleObserver(this);
|
| + // 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));
|
|
|
| - CHECK(!attached_);
|
| - CHECK(!timer_.IsRunning());
|
| + // 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);
|
| + }
|
| }
|
|
|
| -void WtsSessionProcessLauncher::OnIOCompleted(
|
| +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::ResetUserToken(ScopedHandle* user_token) {
|
| + DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| +
|
| + user_token_ = user_token->Pass();
|
| +}
|
| +
|
| +void SingleProcessLauncher::Stop() {
|
| + DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| +}
|
| +
|
| +ElevatedProcessLauncher::ElevatedProcessLauncher(
|
| + 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());
|
| +
|
| + // 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(ipc_message_loop_->BelongsToCurrentThread());
|
| + DCHECK(io_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(
|
| + &ElevatedProcessLauncher::OnJobNotification, this, bytes_transferred,
|
| reinterpret_cast<DWORD>(context)));
|
| }
|
|
|
| -bool WtsSessionProcessLauncher::DoLaunchProcess(
|
| +bool ElevatedProcessLauncher::DoLaunchProcess(
|
| const std::string& channel_name,
|
| ScopedHandle* process_exit_event_out) {
|
| - DCHECK(main_message_loop_->BelongsToCurrentThread());
|
| + 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 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.
|
| + // 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(service_binary);
|
| - command_line.AppendSwitchPath(kElevateSwitchName, host_binary);
|
| + command_line.AppendSwitchPath(kElevateSwitchName, binary_path_);
|
| command_line.AppendSwitchNative(kDaemonIpcSwitchName,
|
| UTF8ToWide(channel_name));
|
| command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
|
| @@ -150,11 +346,11 @@ bool WtsSessionProcessLauncher::DoLaunchProcess(
|
|
|
| // 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_process;
|
| - base::win::ScopedHandle worker_thread;
|
| + ScopedHandle worker_process;
|
| + ScopedHandle worker_thread;
|
| if (!LaunchProcessWithToken(service_binary,
|
| command_line.GetCommandLineString(),
|
| - session_token_,
|
| + user_token_,
|
| CREATE_SUSPENDED,
|
| &worker_process,
|
| &worker_thread)) {
|
| @@ -190,110 +386,71 @@ bool WtsSessionProcessLauncher::DoLaunchProcess(
|
| return true;
|
| }
|
|
|
| -void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) {
|
| - DCHECK(main_message_loop_->BelongsToCurrentThread());
|
| +void ElevatedProcessLauncher::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;
|
| -}
|
| +DWORD ElevatedProcessLauncher::GetExitCode() {
|
| + DCHECK(main_task_runner_->BelongsToCurrentThread());
|
|
|
| -void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
|
| - 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;
|
| + }
|
|
|
| - 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_);
|
| +void ElevatedProcessLauncher::ResetUserToken(
|
| + ScopedHandle* user_token) {
|
| + DCHECK(main_task_runner_->BelongsToCurrentThread());
|
|
|
| - attached_ = false;
|
| - launch_backoff_ = base::TimeDelta();
|
| - session_token_.Close();
|
| - timer_.Stop();
|
| -
|
| - if (launcher_.get() != NULL) {
|
| - launcher_->Stop();
|
| - }
|
| + user_token_ = user_token->Pass();
|
| }
|
|
|
| -void WtsSessionProcessLauncher::DoStop() {
|
| - DCHECK(main_message_loop_->BelongsToCurrentThread());
|
| -
|
| - if (attached_) {
|
| - OnSessionDetached();
|
| - }
|
| -
|
| - job_.Close();
|
| +void ElevatedProcessLauncher::Stop() {
|
| + DCHECK(main_task_runner_->BelongsToCurrentThread());
|
|
|
| // 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 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_message_loop_->PostTask(FROM_HERE, base::Bind(
|
| - &WtsSessionProcessLauncher::DrainJobNotificationsCompleted,
|
| - base::Unretained(this)));
|
| + main_task_runner_->PostTask(FROM_HERE, base::Bind(
|
| + &ElevatedProcessLauncher::DrainJobNotificationsCompleted, this));
|
| }
|
|
|
| -void WtsSessionProcessLauncher::DrainJobNotificationsCompleted() {
|
| - DCHECK(main_message_loop_->BelongsToCurrentThread());
|
| - DCHECK_EQ(job_state_, kJobStopping);
|
| +void ElevatedProcessLauncher::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.
|
| + io_task_runner_->PostTask(FROM_HERE, base::Bind(
|
| + &ElevatedProcessLauncher::DrainJobNotifications, this));
|
| + }
|
| }
|
|
|
| -void WtsSessionProcessLauncher::InitializeJob() {
|
| - DCHECK(ipc_message_loop_->BelongsToCurrentThread());
|
| +void ElevatedProcessLauncher::InitializeJob() {
|
| + DCHECK(io_task_runner_->BelongsToCurrentThread());
|
|
|
| ScopedHandle job;
|
| job.Set(CreateJobObject(NULL, NULL));
|
| @@ -332,25 +489,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(
|
| + &ElevatedProcessLauncher::InitializeJobCompleted, this,
|
| + base::Passed(&job_wrapper)));
|
| }
|
|
|
| -void WtsSessionProcessLauncher::InitializeJobCompleted(
|
| +void ElevatedProcessLauncher::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 ElevatedProcessLauncher::OnJobNotification(DWORD message, DWORD pid) {
|
| + DCHECK(main_task_runner_->BelongsToCurrentThread());
|
|
|
| switch (message) {
|
| case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
|
| @@ -366,6 +519,127 @@ void WtsSessionProcessLauncher::OnJobNotification(DWORD message, DWORD pid) {
|
| }
|
| }
|
|
|
| +} // 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;
|
| + }
|
| + 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 ElevatedProcessLauncher(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.
|
| + ScopedHandle user_token;
|
| + if (CreateSessionToken(session_id, &user_token)) {
|
| + impl_->ResetUserToken(&user_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();
|
| + impl_ = NULL;
|
| + }
|
| +
|
| + CompleteStopping();
|
| +}
|
| +
|
| void WtsSessionProcessLauncher::LaunchProcess() {
|
| DCHECK(main_message_loop_->BelongsToCurrentThread());
|
| DCHECK(attached_);
|
| @@ -374,7 +648,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 +659,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);
|
|
|
|
|