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

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

Issue 15077010: [Chromoting] Refactored worker process launching code and speeded up the desktop process launch. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: - Created 7 years, 7 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_delegate.cc
diff --git a/remoting/host/win/wts_session_process_delegate.cc b/remoting/host/win/wts_session_process_delegate.cc
index 72630c96e12d6a7baaf2851536a3d40fb95453bd..3c001144e282c98a754d6c20d36c0bae8780c25e 100644
--- a/remoting/host/win/wts_session_process_delegate.cc
+++ b/remoting/host/win/wts_session_process_delegate.cc
@@ -8,21 +8,19 @@
#include "remoting/host/win/wts_session_process_delegate.h"
#include "base/bind.h"
-#include "base/bind_helpers.h"
#include "base/command_line.h"
-#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
-#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
#include "base/single_thread_task_runner.h"
+#include "base/thread_task_runner_handle.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_listener.h"
#include "ipc/ipc_message.h"
-#include "remoting/host/host_exit_codes.h"
#include "remoting/host/host_main.h"
#include "remoting/host/ipc_constants.h"
#include "remoting/host/win/launch_process_with_token.h"
@@ -41,45 +39,44 @@ namespace remoting {
// |WtsSessionProcessDelegate|. This class is ref-counted and implements
// asynchronous fire-and-forget shutdown.
class WtsSessionProcessDelegate::Core
- : public base::RefCountedThreadSafe<WtsSessionProcessDelegate::Core>,
+ : public base::RefCountedThreadSafe<Core>,
public base::MessagePumpForIO::IOHandler,
- public WorkerProcessLauncher::Delegate {
+ public IPC::Listener {
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,
- scoped_ptr<CommandLine> target_command,
+ Core(scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
+ scoped_ptr<CommandLine> target,
bool launch_elevated,
const std::string& channel_security);
- // base::MessagePumpForIO::IOHandler implementation.
- virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
- DWORD bytes_transferred,
- DWORD error) OVERRIDE;
-
- // IPC::Sender implementation.
- virtual bool Send(IPC::Message* message) OVERRIDE;
-
- // WorkerProcessLauncher::Delegate implementation.
- virtual void CloseChannel() OVERRIDE;
- virtual DWORD GetProcessId() const OVERRIDE;
- virtual bool IsPermanentError(int failure_count) const OVERRIDE;
- virtual void KillProcess(DWORD exit_code) OVERRIDE;
- virtual bool LaunchProcess(
- IPC::Listener* delegate,
- 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();
+ // Mirrors WorkerProcessLauncher::Delegate.
+ void LaunchProcess(WorkerProcessLauncher* event_handler);
+ void Send(IPC::Message* message);
+ void CloseChannel();
+ void KillProcess();
+
private:
friend class base::RefCountedThreadSafe<Core>;
virtual ~Core();
+ // base::MessagePumpForIO::IOHandler implementation.
+ virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
+ DWORD bytes_transferred,
+ DWORD error) OVERRIDE;
+
+ // IPC::Listener implementation.
+ virtual bool OnMessageReceived(const IPC::Message& message) OVERRIDE;
+ virtual void OnChannelConnected(int32 peer_pid) OVERRIDE;
+ virtual void OnChannelError() OVERRIDE;
+
+ // The actual implementation of LaunchProcess()
+ void DoLaunchProcess();
+
// Drains the completion port queue to make sure that all job object
// notifications have been received.
void DrainJobNotifications();
@@ -89,23 +86,23 @@ class WtsSessionProcessDelegate::Core
// Creates and initializes the job object that will sandbox the launched child
// processes.
- void InitializeJob();
+ void InitializeJob(scoped_ptr<base::win::ScopedHandle> job);
// 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);
+ // Called when the number of processes running in the job reaches zero.
+ void OnActiveProcessZero();
+
+ void ReportFatalError();
+ void ReportProcessLaunched(base::win::ScopedHandle worker_process);
// The task runner all public methods of this class should be called on.
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
+ scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_;
// The task runner serving job object notifications.
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
- // Command line of the launched process.
- scoped_ptr<CommandLine> target_command_;
-
// The server end of the IPC channel used to communicate to the worker
// process.
scoped_ptr<IPC::ChannelProxy> channel_;
@@ -113,6 +110,8 @@ class WtsSessionProcessDelegate::Core
// Security descriptor (as SDDL) to be applied to |channel_|.
std::string channel_security_;
+ WorkerProcessLauncher* event_handler_;
+
// Pointer to GetNamedPipeClientProcessId() API if it is available.
typedef BOOL (WINAPI * GetNamedPipeClientProcessIdFn)(HANDLE, DWORD*);
GetNamedPipeClientProcessIdFn get_named_pipe_client_pid_;
@@ -123,18 +122,17 @@ class WtsSessionProcessDelegate::Core
// True if the worker process should be launched elevated.
bool launch_elevated_;
+ // True if a laucnh attemp is pending.
+ bool launch_pending_;
+
// The named pipe used as the transport by |channel_|.
base::win::ScopedHandle pipe_;
- // 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_;
+ // Command line of the launched process.
+ scoped_ptr<CommandLine> target_command_;
// The handle of the worker process, if launched.
base::win::ScopedHandle worker_process_;
@@ -143,138 +141,246 @@ class WtsSessionProcessDelegate::Core
};
WtsSessionProcessDelegate::Core::Core(
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_ptr<CommandLine> target_command,
bool launch_elevated,
const std::string& channel_security)
- : main_task_runner_(main_task_runner),
+ : caller_task_runner_(base::ThreadTaskRunnerHandle::Get()),
io_task_runner_(io_task_runner),
- target_command_(target_command.Pass()),
channel_security_(channel_security),
+ event_handler_(NULL),
get_named_pipe_client_pid_(NULL),
launch_elevated_(launch_elevated),
- stopping_(false) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+ launch_pending_(false),
+ target_command_(target_command.Pass()) {
}
-void WtsSessionProcessDelegate::Core::OnIOCompleted(
- base::MessagePumpForIO::IOContext* context,
- DWORD bytes_transferred,
- DWORD error) {
- DCHECK(io_task_runner_->BelongsToCurrentThread());
+bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) {
+ DCHECK(caller_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)));
+ if (base::win::GetVersion() < base::win::VERSION_VISTA)
garykac 2013/05/15 23:24:30 The other |launch_elevated| code is set when versi
alexeypa (please no reviews) 2013/05/16 17:50:04 Yes, |< VISTA| is the right thing to do here.
+ launch_elevated_ = false;
+
+ if (launch_elevated_) {
+ // GetNamedPipeClientProcessId() is available starting from Vista.
garykac 2013/05/15 23:24:30 This seems to answer part of my question above. Bu
alexeypa (please no reviews) 2013/05/16 17:50:04 We launch the process directly in that case. So th
+ HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
+ CHECK(kernel32 != NULL);
+
+ get_named_pipe_client_pid_ =
+ reinterpret_cast<GetNamedPipeClientProcessIdFn>(
+ GetProcAddress(kernel32, "GetNamedPipeClientProcessId"));
+ CHECK(get_named_pipe_client_pid_ != NULL);
+
+ ScopedHandle job;
+ job.Set(CreateJobObject(NULL, NULL));
+ if (!job.IsValid()) {
+ LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
+ return false;
+ }
+
+ // Limit the number of active processes in the job to two (the helper
+ // process performing elevation and the worker process itself) 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 false;
+ }
+
+ // 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();
+
+ // 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, base::Passed(&job_wrapper)));
+ }
+
+ // Create a session token for the launched process.
+ return CreateSessionToken(session_id, &session_token_);
+}
+
+void WtsSessionProcessDelegate::Core::Stop() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ KillProcess();
+
+ // Drain the completion queue to make sure all job object notifications have
+ // been received.
+ DrainJobNotificationsCompleted();
}
-bool WtsSessionProcessDelegate::Core::Send(IPC::Message* message) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+void WtsSessionProcessDelegate::Core::LaunchProcess(
+ WorkerProcessLauncher* event_handler) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ DCHECK(!event_handler_);
+
+ event_handler_ = event_handler;
+ DoLaunchProcess();
+}
+
+void WtsSessionProcessDelegate::Core::Send(IPC::Message* message) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (channel_) {
- return channel_->Send(message);
+ channel_->Send(message);
} else {
delete message;
- return false;
}
}
void WtsSessionProcessDelegate::Core::CloseChannel() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
channel_.reset();
pipe_.Close();
}
-DWORD WtsSessionProcessDelegate::Core::GetProcessId() const {
- DWORD pid = 0;
- if (launch_elevated_ && pipe_.IsValid() &&
- get_named_pipe_client_pid_(pipe_, &pid)) {
- return pid;
+void WtsSessionProcessDelegate::Core::KillProcess() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ channel_.reset();
+ event_handler_ = NULL;
+ launch_pending_ = false;
+ pipe_.Close();
+
+ if (launch_elevated_) {
+ if (job_.IsValid())
+ TerminateJobObject(job_, CONTROL_C_EXIT);
+ } else {
+ if (worker_process_.IsValid())
+ TerminateProcess(worker_process_, CONTROL_C_EXIT);
}
- if (worker_process_.IsValid())
- return ::GetProcessId(worker_process_);
+ worker_process_.Close();
+}
- return 0;
+WtsSessionProcessDelegate::Core::~Core() {
+ DCHECK(!channel_);
+ DCHECK(!event_handler_);
+ DCHECK(!pipe_.IsValid());
+ DCHECK(!worker_process_.IsValid());
}
-bool WtsSessionProcessDelegate::Core::IsPermanentError(
- int failure_count) const {
- // Get exit code of the worker process if it is available.
- 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;
- }
- }
+void WtsSessionProcessDelegate::Core::OnIOCompleted(
+ base::MessagePumpForIO::IOContext* context,
+ DWORD bytes_transferred,
+ DWORD error) {
+ DCHECK(io_task_runner_->BelongsToCurrentThread());
- // Stop trying to restart the worker process if it exited due to
- // misconfiguration.
- return (kMinPermanentErrorExitCode <= exit_code &&
- exit_code <= kMaxPermanentErrorExitCode);
+ // |bytes_transferred| is used in job object notifications to supply
+ // the message ID; |context| carries process ID.
+ if (bytes_transferred == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO) {
+ caller_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&Core::OnActiveProcessZero, this));
+ }
}
-void WtsSessionProcessDelegate::Core::KillProcess(DWORD exit_code) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+bool WtsSessionProcessDelegate::Core::OnMessageReceived(
+ const IPC::Message& message) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
- channel_.reset();
- pipe_.Close();
+ return event_handler_->OnMessageReceived(message);
+}
+void WtsSessionProcessDelegate::Core::OnChannelConnected(int32 peer_pid) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ // Report the worker PID now if the worker process is launched indirectly.
+ // Note that in this case the pipe's security descriptor is the only
+ // protection against a malicious processed connecting to the pipe.
if (launch_elevated_) {
- if (job_.IsValid()) {
- TerminateJobObject(job_, exit_code);
+ DWORD pid;
+ if (!get_named_pipe_client_pid_(pipe_, &pid)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to retrive PID of the client";
+ ReportFatalError();
+ return;
}
- } else {
- if (worker_process_.IsValid()) {
- TerminateProcess(worker_process_, exit_code);
+
+ if (pid != static_cast<DWORD>(peer_pid)) {
+ LOG(ERROR) << "The actual client PID " << pid
+ << " does not match the one reported by the client: "
+ << peer_pid;
+ ReportFatalError();
+ return;
}
+
+ DWORD desired_access =
+ SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
+ ScopedHandle worker_process(OpenProcess(desired_access, false, pid));
+ if (!worker_process.IsValid()) {
+ LOG_GETLASTERROR(ERROR) << "Failed to open process " << pid;
+ ReportFatalError();
+ return;
+ }
+
+ ReportProcessLaunched(worker_process.Pass());
}
+
+ if (event_handler_)
+ event_handler_->OnChannelConnected(peer_pid);
+}
+
+void WtsSessionProcessDelegate::Core::OnChannelError() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ event_handler_->OnChannelError();
}
-bool WtsSessionProcessDelegate::Core::LaunchProcess(
- IPC::Listener* delegate,
- ScopedHandle* process_exit_event_out) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+void WtsSessionProcessDelegate::Core::DoLaunchProcess() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ DCHECK(!channel_);
+ DCHECK(!pipe_.IsValid());
+ DCHECK(!worker_process_.IsValid());
CommandLine command_line(target_command_->argv());
if (launch_elevated_) {
// The job object is not ready. Retry starting the host process later.
if (!job_.IsValid()) {
- LOG(ERROR) << "The job object is not ready yet.";
- return false;
+ launch_pending_ = true;
+ return;
}
// Construct the helper binary name.
base::FilePath helper_binary;
- if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary))
- return false;
+ if (!GetInstalledBinaryPath(kHostBinaryName, &helper_binary)) {
+ ReportFatalError();
+ return;
+ }
// 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(helper_binary);
command_line.AppendSwitchPath(kElevateSwitchName,
target_command_->GetProgram());
-
- CHECK(ResetEvent(process_exit_event_));
}
// Create the server end of the IPC channel.
std::string channel_name = IPC::Channel::GenerateUniqueRandomChannelID();
ScopedHandle pipe;
- if (!CreateIpcChannel(channel_name, channel_security_, &pipe))
- return false;
+ if (!CreateIpcChannel(channel_name, channel_security_, &pipe)) {
+ ReportFatalError();
+ return;
+ }
// Wrap the pipe into an IPC channel.
scoped_ptr<IPC::ChannelProxy> channel(new IPC::ChannelProxy(
IPC::ChannelHandle(pipe),
IPC::Channel::MODE_SERVER,
- delegate,
+ this,
io_task_runner_));
// Pass the name of the IPC channel to use.
@@ -294,97 +400,33 @@ bool WtsSessionProcessDelegate::Core::LaunchProcess(
UTF8ToUTF16(kDefaultDesktopName).c_str(),
&worker_process,
&worker_thread)) {
- return false;
+ ReportFatalError();
+ return;
}
- 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;
+ ReportFatalError();
+ return;
}
-
- 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;
+ ReportFatalError();
+ return;
}
channel_ = channel.Pass();
pipe_ = pipe.Pass();
- *process_exit_event_out = process_exit_event.Pass();
- return true;
-}
-
-bool WtsSessionProcessDelegate::Core::Initialize(uint32 session_id) {
- if (base::win::GetVersion() < base::win::VERSION_VISTA)
- launch_elevated_ = false;
-
- if (launch_elevated_) {
- // GetNamedPipeClientProcessId() is available starting from Vista.
- HMODULE kernel32 = ::GetModuleHandle(L"kernel32.dll");
- CHECK(kernel32 != NULL);
-
- get_named_pipe_client_pid_ =
- reinterpret_cast<GetNamedPipeClientProcessIdFn>(
- GetProcAddress(kernel32, "GetNamedPipeClientProcessId"));
- CHECK(get_named_pipe_client_pid_ != NULL);
-
- 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() {
+ // Report success if the worker process is lauched directly. Otherwise, PID of
+ // the client connected to the pipe will be used later. See
+ // OnChannelConnected().
+ if (!launch_elevated_)
+ ReportProcessLaunched(worker_process.Pass());
}
void WtsSessionProcessDelegate::Core::DrainJobNotifications() {
@@ -393,12 +435,12 @@ void WtsSessionProcessDelegate::Core::DrainJobNotifications() {
// 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(
+ caller_task_runner_->PostTask(FROM_HERE, base::Bind(
&Core::DrainJobNotificationsCompleted, this));
}
void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
if (job_.IsValid()) {
job_.Close();
@@ -410,132 +452,115 @@ void WtsSessionProcessDelegate::Core::DrainJobNotificationsCompleted() {
}
}
-void WtsSessionProcessDelegate::Core::InitializeJob() {
+void WtsSessionProcessDelegate::Core::InitializeJob(
+ scoped_ptr<base::win::ScopedHandle> job) {
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 (!base::MessageLoopForIO::current()->RegisterJobObject(job, this)) {
+ if (!base::MessageLoopForIO::current()->RegisterJobObject(job->Get(), 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)));
+ caller_task_runner_->PostTask(FROM_HERE, base::Bind(
+ &Core::InitializeJobCompleted, this, base::Passed(&job)));
}
void WtsSessionProcessDelegate::Core::InitializeJobCompleted(
scoped_ptr<ScopedHandle> job) {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
DCHECK(!job_.IsValid());
job_ = job->Pass();
+
+ if (launch_pending_)
+ DoLaunchProcess();
}
-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;
+void WtsSessionProcessDelegate::Core::OnActiveProcessZero() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ if (launch_pending_) {
+ LOG(ERROR) << "The worker process exited before connecting via IPC.";
+ launch_pending_ = false;
+ ReportFatalError();
}
}
+void WtsSessionProcessDelegate::Core::ReportFatalError() {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+
+ channel_.reset();
+ pipe_.Close();
+
+ WorkerProcessLauncher* event_handler = event_handler_;
+ event_handler_ = NULL;
+ event_handler->OnFatalError();
+}
+
+void WtsSessionProcessDelegate::Core::ReportProcessLaunched(
+ base::win::ScopedHandle worker_process) {
+ DCHECK(caller_task_runner_->BelongsToCurrentThread());
+ DCHECK(!worker_process_.IsValid());
+
+ worker_process_ = worker_process.Pass();
+
+ // Report a handle that can be used to wait for the worker process completion,
+ // query information about the process and duplicate handles.
+ DWORD desired_access =
+ SYNCHRONIZE | PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION;
+ ScopedHandle limited_handle;
+ if (!DuplicateHandle(GetCurrentProcess(),
+ worker_process_,
+ GetCurrentProcess(),
+ limited_handle.Receive(),
+ desired_access,
+ FALSE,
+ 0)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
+ ReportFatalError();
+ return;
+ }
+
+ event_handler_->OnProcessLaunched(limited_handle.Pass());
+}
+
WtsSessionProcessDelegate::WtsSessionProcessDelegate(
- scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
scoped_refptr<base::SingleThreadTaskRunner> io_task_runner,
scoped_ptr<CommandLine> target_command,
- uint32 session_id,
bool launch_elevated,
const std::string& channel_security) {
- core_ = new Core(main_task_runner, io_task_runner, target_command.Pass(),
- launch_elevated, channel_security);
- if (!core_->Initialize(session_id)) {
- core_->Stop();
- core_ = NULL;
- }
+ core_ = new Core(io_task_runner,
+ target_command.Pass(),
+ launch_elevated,
+ channel_security);
}
WtsSessionProcessDelegate::~WtsSessionProcessDelegate() {
- if (core_) {
- core_->Stop();
- core_ = NULL;
- }
+ core_->Stop();
}
-bool WtsSessionProcessDelegate::Send(IPC::Message* message) {
- return core_->Send(message);
+bool WtsSessionProcessDelegate::Initialize(uint32 session_id) {
+ return core_->Initialize(session_id);
}
-void WtsSessionProcessDelegate::CloseChannel() {
- if (core_)
- core_->CloseChannel();
+void WtsSessionProcessDelegate::LaunchProcess(
+ WorkerProcessLauncher* event_handler) {
+ core_->LaunchProcess(event_handler);
}
-DWORD WtsSessionProcessDelegate::GetProcessId() const {
- if (!core_)
- return 0;
-
- return core_->GetProcessId();
+void WtsSessionProcessDelegate::Send(IPC::Message* message) {
+ core_->Send(message);
}
-bool WtsSessionProcessDelegate::IsPermanentError(int failure_count) const {
- if (!core_)
- return false;
-
- return core_->IsPermanentError(failure_count);
-}
-
-void WtsSessionProcessDelegate::KillProcess(DWORD exit_code) {
- if (core_)
- core_->KillProcess(exit_code);
+void WtsSessionProcessDelegate::CloseChannel() {
+ core_->CloseChannel();
}
-bool WtsSessionProcessDelegate::LaunchProcess(
- IPC::Listener* delegate,
- base::win::ScopedHandle* process_exit_event_out) {
- if (!core_)
- return false;
-
- return core_->LaunchProcess(delegate, process_exit_event_out);
+void WtsSessionProcessDelegate::KillProcess() {
+ core_->KillProcess();
}
} // namespace remoting

Powered by Google App Engine
This is Rietveld 408576698