Chromium Code Reviews| 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); |