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

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

Issue 10831271: [Chromoting] Adding uiAccess='true' to the remoting_me2me_host.exe's manifest as it is required to … (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: CR feedback. Created 8 years, 4 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 e57d37c93e03cd573d8db8ddb0e28543c9353256..e497face4460649ce39b5251d5137edb2ddc82cc 100644
--- a/remoting/host/win/wts_session_process_launcher.cc
+++ b/remoting/host/win/wts_session_process_launcher.cc
@@ -47,9 +47,14 @@ const int kMinLaunchDelaySeconds = 1;
const FilePath::CharType kMe2meHostBinaryName[] =
FILE_PATH_LITERAL("remoting_me2me_host.exe");
+const FilePath::CharType kMe2meServiceBinaryName[] =
+ FILE_PATH_LITERAL("remoting_service.exe");
+
// The IPC channel name is passed to the host in the command line.
const char kChromotingIpcSwitchName[] = "chromoting-ipc";
+const char kElevateSwitchName[] = "elevate";
+
// The command line parameters that should be copied from the service's command
// line to the host process.
const char* kCopiedSwitchNames[] = {
@@ -72,15 +77,30 @@ WtsSessionProcessLauncher::WtsSessionProcessLauncher(
attached_(false),
main_message_loop_(main_message_loop),
ipc_message_loop_(ipc_message_loop),
- monitor_(monitor) {
+ monitor_(monitor),
+ job_state_(kJobUninitialized) {
monitor_->AddWtsConsoleObserver(this);
+
+ process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
+ CHECK(process_exit_event_.IsValid());
+
+ // Job object initalization has to be performed on the I/O thread because
+ // MessageLoopForIO::RegisterJobObject() can only be called via
+ // MessageLoopForIO::current().
Wez 2012/08/15 23:22:20 If that's the only limitation then why not move Re
alexeypa (please no reviews) 2012/08/15 23:55:50 It does not belong there (and to MessageLoopProxy
Wez 2012/08/16 00:06:33 OK, so the comment should state that job initializ
alexeypa (please no reviews) 2012/08/16 00:34:10 Done.
+ ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
+ &WtsSessionProcessLauncher::InitializeJob,
+ base::Unretained(this)));
}
WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
monitor_->RemoveWtsConsoleObserver(this);
- DCHECK(!attached_);
- DCHECK(!timer_.IsRunning());
+ // Failing this check means that the object hasn't been shutdown correctly and
+ // there still could be pending tasks posted to it.
Wez 2012/08/15 23:22:20 nit: You're checking this here, in addition to the
alexeypa (please no reviews) 2012/08/15 23:55:50 I'm duplicating this check for a reason. First, Wt
Wez 2012/08/16 00:06:33 Right, so the comment should simply clarify that w
alexeypa (please no reviews) 2012/08/16 00:34:10 Done.
+ CHECK_EQ(stoppable_state(), Stoppable::kStopped);
Wez 2012/08/15 23:22:20 nit: Doesn't this check belong before the RemoveWt
alexeypa (please no reviews) 2012/08/15 23:55:50 It does not really matter.
Wez 2012/08/16 00:06:33 Please move it before the RemoveWtsConsoleObserver
alexeypa (please no reviews) 2012/08/16 00:34:10 Done.
+
+ CHECK(!attached_);
+ CHECK(!timer_.IsRunning());
}
void WtsSessionProcessLauncher::LaunchProcess() {
@@ -103,19 +123,22 @@ void WtsSessionProcessLauncher::LaunchProcess() {
void WtsSessionProcessLauncher::OnLauncherStopped() {
DCHECK(main_message_loop_->BelongsToCurrentThread());
- DWORD exit_code;
- if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
- LOG_GETLASTERROR(INFO)
- << "Failed to query the exit code of the worker process";
- exit_code = CONTROL_C_EXIT;
+ 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();
}
launcher_.reset(NULL);
- worker_process_.Close();
// Do not relaunch the worker process if the caller has asked us to stop.
if (stoppable_state() != Stoppable::kRunning) {
- CompleteStopping();
+ Stop();
return;
}
@@ -148,12 +171,29 @@ void WtsSessionProcessLauncher::OnLauncherStopped() {
}
}
+void WtsSessionProcessLauncher::OnIOCompleted(
+ base::MessagePumpForIO::IOContext* context,
+ DWORD bytes_transferred,
+ DWORD error) {
+ DCHECK(ipc_message_loop_->BelongsToCurrentThread());
+
+ // |bytes_transferred| is used in job object notifications to supply
+ // the message ID.
+ if (bytes_transferred == JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO)
+ CHECK(SetEvent(process_exit_event_));
+}
+
bool WtsSessionProcessLauncher::DoLaunchProcess(
const std::string& channel_name,
ScopedHandle* process_exit_event_out) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
DCHECK(!worker_process_.IsValid());
+ // The job object is not ready. Retry starting the host process later.
+ if (!job_.IsValid()) {
+ return false;
+ }
+
// Construct the host binary name.
FilePath dir_path;
if (!PathService::Get(base::DIR_EXE, &dir_path)) {
@@ -161,28 +201,48 @@ bool WtsSessionProcessLauncher::DoLaunchProcess(
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(host_binary);
+ CommandLine command_line(service_binary);
+ command_line.AppendSwitchPath(kElevateSwitchName, host_binary);
command_line.AppendSwitchNative(kChromotingIpcSwitchName,
UTF8ToWide(channel_name));
command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
kCopiedSwitchNames,
_countof(kCopiedSwitchNames));
+ CHECK(ResetEvent(process_exit_event_));
+
// Try to launch the process and attach an object watcher to the returned
// handle so that we get notified when the process terminates.
- if (!LaunchProcessWithToken(host_binary,
+ base::win::ScopedHandle worker_process;
+ base::win::ScopedHandle worker_thread;
+ if (!LaunchProcessWithToken(service_binary,
command_line.GetCommandLineString(),
session_token_,
- &worker_process_)) {
+ CREATE_SUSPENDED,
+ &worker_process,
+ &worker_thread)) {
+ return false;
+ }
+
+ if (!AssignProcessToJobObject(job_, worker_process)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object";
+ TerminateProcess(worker_process, CONTROL_C_EXIT);
+ return false;
+ }
+
+ if (!ResumeThread(worker_thread)) {
+ LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
+ DoKillProcess(CONTROL_C_EXIT);
return false;
}
ScopedHandle process_exit_event;
if (!DuplicateHandle(GetCurrentProcess(),
- worker_process_,
+ process_exit_event_,
GetCurrentProcess(),
process_exit_event.Receive(),
SYNCHRONIZE,
@@ -200,21 +260,21 @@ bool WtsSessionProcessLauncher::DoLaunchProcess(
void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
- if (worker_process_.IsValid()) {
- TerminateProcess(worker_process_, exit_code);
+ if (job_.IsValid()) {
+ TerminateJobObject(job_, exit_code);
}
}
void WtsSessionProcessLauncher::OnChannelConnected(DWORD peer_pid) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
- DWORD expected_pid = GetProcessId(worker_process_);
- if (peer_pid != expected_pid) {
- LOG(ERROR)
- << "Unexpected client connected: expected=" << expected_pid
- << ", actual=" << peer_pid;
- Stop();
- }
+ // Nothing to validate. Only processes running as SYSTEM can connect to
+ // the pipe, so if it was a malicious process the game would be over already.
+
+ // Get a handle to the peer process so we could query its exit code later.
+ // Ignore the error. Generic |CONTROL_C_EXIT| will be used if the process
Wez 2012/08/15 23:22:20 nit: What does "will be used" mean here? You mean
alexeypa (please no reviews) 2012/08/15 23:55:50 OpenProcess can fail. I explain why this error is
Wez 2012/08/16 00:06:33 My point is just that the explanation is not clear
alexeypa (please no reviews) 2012/08/16 00:34:10 Done.
+ // cannot be opened.
+ worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, peer_pid));
}
bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
@@ -284,8 +344,103 @@ void WtsSessionProcessLauncher::DoStop() {
OnSessionDetached();
}
- if (launcher_.get() == NULL) {
- CompleteStopping();
+ job_.Close();
+
+ // 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();
+}
+
+void WtsSessionProcessLauncher::DrainJobNotifications() {
+ DCHECK(ipc_message_loop_->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)));
+}
+
+void WtsSessionProcessLauncher::DrainJobNotificationsCompleted() {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ DCHECK_EQ(job_state_, kJobStopping);
+
+ job_state_ = kJobStopped;
+ Stop();
+}
+
+void WtsSessionProcessLauncher::InitializeJob() {
+ DCHECK(ipc_message_loop_->BelongsToCurrentThread());
+
+ ScopedHandle job;
+ job.Set(CreateJobObject(NULL, NULL));
+ if (!job.IsValid()) {
+ LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
+ return;
+ }
+
+ // Limit the number of active processes in the job to two (the process
+ // performing elevation and the host) and make sure that all processes will be
+ // killed once the job object is destroyed.
+ JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
+ memset(&info, 0, sizeof(info));
+ info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
+ JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
+ info.BasicLimitInformation.ActiveProcessLimit = 2;
+ if (!SetInformationJobObject(job,
+ JobObjectExtendedLimitInformation,
+ &info,
+ sizeof(info))) {
+ LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
+ return;
+ }
+
+ // Register the job object with the completion port in the I/O thread to
+ // receive job notifications.
+ if (!MessageLoopForIO::current()->RegisterJobObject(job, this)) {
+ LOG_GETLASTERROR(ERROR)
+ << "Failed to associate the job object with a completion port";
+ return;
+ }
+
+ // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
+ // pointer.
+ scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
+ *job_wrapper = job.Pass();
+
+ // Let the main thread know that initialization is complete.
+ main_message_loop_->PostTask(FROM_HERE, base::Bind(
+ &WtsSessionProcessLauncher::InitializeJobCompleted,
+ base::Unretained(this), base::Passed(&job_wrapper)));
+}
+
+void WtsSessionProcessLauncher::InitializeJobCompleted(
+ scoped_ptr<ScopedHandle> job) {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ DCHECK(!job_.IsValid());
+ DCHECK_EQ(job_state_, kJobUninitialized);
+
+ if (stoppable_state() == Stoppable::kRunning) {
+ job_ = job->Pass();
+ job_state_ = kJobRunning;
}
}

Powered by Google App Engine
This is Rietveld 408576698