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

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: rebased + a couple of merge fixes. 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
« no previous file with comments | « remoting/host/win/wts_session_process_launcher.h ('k') | remoting/remoting.gyp » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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 42cb702a9d3dfff8b5354045670f579cb312c602..bec5db12ef9dba4bfa5004fd362e09e486dcf747 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,87 +77,57 @@ 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);
-}
-WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
- monitor_->RemoveWtsConsoleObserver(this);
-
- DCHECK(!attached_);
- DCHECK(!timer_.IsRunning());
-}
-
-void WtsSessionProcessLauncher::LaunchProcess() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
- DCHECK(attached_);
- DCHECK(launcher_.get() == NULL);
- DCHECK(!timer_.IsRunning());
- DCHECK(!worker_process_.IsValid());
+ process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
+ CHECK(process_exit_event_.IsValid());
- launch_time_ = base::Time::Now();
- launcher_.reset(new WorkerProcessLauncher(
- this,
- base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
- base::Unretained(this)),
- main_message_loop_,
- ipc_message_loop_));
- launcher_->Start(kChromotingChannelSecurityDescriptor);
+ // 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)));
}
-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;
- }
-
- 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();
- return;
- }
+WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
+ // Make sure that the object is completely stopped. The same check exists
+ // in Stoppable::~Stoppable() but this one allows us to examine the state of
+ // the object before destruction.
+ CHECK_EQ(stoppable_state(), Stoppable::kStopped);
- // Stop trying to restart the worker process if its process exited due to
- // misconfiguration.
- if (kMinPermanentErrorExitCode <= exit_code &&
- exit_code <= kMaxPermanentErrorExitCode) {
- Stop();
- return;
- }
+ monitor_->RemoveWtsConsoleObserver(this);
- // Try to restart the worker process if we are still attached to a session.
- if (attached_) {
- // Expand the backoff interval if the process has died quickly or reset it
- // if it was up longer than the maximum backoff delay.
- base::TimeDelta delta = base::Time::Now() - launch_time_;
- if (delta < base::TimeDelta() ||
- delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) {
- launch_backoff_ = base::TimeDelta();
- } else {
- launch_backoff_ = std::max(
- launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
- launch_backoff_ = std::min(
- launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
- }
+ CHECK(!attached_);
+ CHECK(!timer_.IsRunning());
+}
- // Try to launch the worker process.
- timer_.Start(FROM_HERE, launch_backoff_,
- this, &WtsSessionProcessLauncher::LaunchProcess);
- }
+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; |context| carries process ID.
+ main_message_loop_->PostTask(FROM_HERE, base::Bind(
+ &WtsSessionProcessLauncher::OnJobNotification,
+ base::Unretained(this), bytes_transferred,
+ reinterpret_cast<DWORD>(context)));
}
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;
@@ -161,28 +136,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,8 +195,8 @@ 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);
}
}
@@ -221,20 +216,6 @@ bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
return handled;
}
-void WtsSessionProcessLauncher::OnSendSasToConsole() {
- DCHECK(main_message_loop_->BelongsToCurrentThread());
-
- if (attached_) {
- if (sas_injector_.get() == NULL) {
- sas_injector_ = SasInjector::Create();
- }
-
- if (sas_injector_.get() != NULL) {
- sas_injector_->InjectSas();
- }
- }
-}
-
void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
DCHECK(main_message_loop_->BelongsToCurrentThread());
@@ -276,8 +257,201 @@ 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;
+ }
+}
+
+void WtsSessionProcessLauncher::OnJobNotification(DWORD message, DWORD pid) {
+ DCHECK(main_message_loop_->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 WtsSessionProcessLauncher::LaunchProcess() {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+ DCHECK(attached_);
+ DCHECK(launcher_.get() == NULL);
+ DCHECK(!timer_.IsRunning());
+
+ launch_time_ = base::Time::Now();
+ launcher_.reset(new WorkerProcessLauncher(
+ this,
+ base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
+ base::Unretained(this)),
+ main_message_loop_,
+ ipc_message_loop_));
+ launcher_->Start(kChromotingChannelSecurityDescriptor);
+}
+
+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();
+ }
+
+ launcher_.reset(NULL);
+
+ // Do not relaunch the worker process if the caller has asked us to stop.
+ if (stoppable_state() != Stoppable::kRunning) {
+ Stop();
+ return;
+ }
+
+ // Stop trying to restart the worker process if its process exited due to
+ // misconfiguration.
+ if (kMinPermanentErrorExitCode <= exit_code &&
+ exit_code <= kMaxPermanentErrorExitCode) {
+ Stop();
+ return;
+ }
+
+ // Try to restart the worker process if we are still attached to a session.
+ if (attached_) {
+ // Expand the backoff interval if the process has died quickly or reset it
+ // if it was up longer than the maximum backoff delay.
+ base::TimeDelta delta = base::Time::Now() - launch_time_;
+ if (delta < base::TimeDelta() ||
+ delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) {
+ launch_backoff_ = base::TimeDelta();
+ } else {
+ launch_backoff_ = std::max(
+ launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
+ launch_backoff_ = std::min(
+ launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
+ }
+
+ // Try to launch the worker process.
+ timer_.Start(FROM_HERE, launch_backoff_,
+ this, &WtsSessionProcessLauncher::LaunchProcess);
+ }
+}
+
+void WtsSessionProcessLauncher::OnSendSasToConsole() {
+ DCHECK(main_message_loop_->BelongsToCurrentThread());
+
+ if (attached_) {
+ if (sas_injector_.get() == NULL) {
+ sas_injector_ = SasInjector::Create();
+ }
+
+ if (sas_injector_.get() != NULL) {
+ sas_injector_->InjectSas();
+ }
}
}
« no previous file with comments | « remoting/host/win/wts_session_process_launcher.h ('k') | remoting/remoting.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698