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

Side by Side Diff: remoting/host/win/wts_session_process_launcher.cc

Issue 10911267: [Chromoting] Don't use a job object to control the me2me host process on Windows XP. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: made compilable on VS2008. Created 8 years, 3 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 unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « remoting/host/win/wts_session_process_launcher.h ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 // 4 //
5 // This file implements the Windows service controlling Me2Me host processes 5 // This file implements the Windows service controlling Me2Me host processes
6 // running within user sessions. 6 // running within user sessions.
7 7
8 #include "remoting/host/win/wts_session_process_launcher.h" 8 #include "remoting/host/win/wts_session_process_launcher.h"
9 9
10 #include <windows.h> 10 #include <windows.h>
11 #include <sddl.h> 11 #include <sddl.h>
12 #include <limits> 12 #include <limits>
13 13
14 #include "base/base_switches.h" 14 #include "base/base_switches.h"
15 #include "base/bind.h" 15 #include "base/bind.h"
16 #include "base/bind_helpers.h" 16 #include "base/bind_helpers.h"
17 #include "base/command_line.h" 17 #include "base/command_line.h"
18 #include "base/file_path.h" 18 #include "base/file_path.h"
19 #include "base/file_util.h" 19 #include "base/file_util.h"
20 #include "base/logging.h" 20 #include "base/logging.h"
21 #include "base/single_thread_task_runner.h" 21 #include "base/single_thread_task_runner.h"
22 #include "base/path_service.h" 22 #include "base/path_service.h"
23 #include "base/rand_util.h" 23 #include "base/rand_util.h"
24 #include "base/stringprintf.h" 24 #include "base/stringprintf.h"
25 #include "base/utf_string_conversions.h" 25 #include "base/utf_string_conversions.h"
26 #include "base/win/scoped_handle.h" 26 #include "base/win/scoped_handle.h"
27 #include "base/win/windows_version.h"
27 #include "ipc/ipc_channel_proxy.h" 28 #include "ipc/ipc_channel_proxy.h"
28 #include "ipc/ipc_message.h" 29 #include "ipc/ipc_message.h"
29 #include "ipc/ipc_message_macros.h" 30 #include "ipc/ipc_message_macros.h"
30 #include "remoting/host/constants.h" 31 #include "remoting/host/constants.h"
31 #include "remoting/host/chromoting_messages.h" 32 #include "remoting/host/chromoting_messages.h"
32 #include "remoting/host/win/launch_process_with_token.h" 33 #include "remoting/host/win/launch_process_with_token.h"
33 #include "remoting/host/win/wts_console_monitor.h" 34 #include "remoting/host/win/wts_console_monitor.h"
34 35
35 using base::win::ScopedHandle; 36 using base::win::ScopedHandle;
36 using base::TimeDelta; 37 using base::TimeDelta;
(...skipping 22 matching lines...) Expand all
59 "host-config", switches::kV, switches::kVModule }; 60 "host-config", switches::kV, switches::kVModule };
60 61
61 // The security descriptor of the daemon IPC endpoint. It gives full access 62 // The security descriptor of the daemon IPC endpoint. It gives full access
62 // to LocalSystem and denies access by anyone else. 63 // to LocalSystem and denies access by anyone else.
63 const char kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; 64 const char kDaemonIpcSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)";
64 65
65 } // namespace 66 } // namespace
66 67
67 namespace remoting { 68 namespace remoting {
68 69
69 WtsSessionProcessLauncher::WtsSessionProcessLauncher( 70 class WtsSessionProcessLauncherImpl
70 const base::Closure& stopped_callback, 71 : public base::RefCountedThreadSafe<WtsSessionProcessLauncherImpl>,
71 WtsConsoleMonitor* monitor, 72 public WorkerProcessLauncher::Delegate {
72 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop, 73 public:
73 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop) 74 // Returns the exit code of the worker process.
74 : Stoppable(main_message_loop, stopped_callback), 75 virtual DWORD GetExitCode() = 0;
75 attached_(false), 76
76 main_message_loop_(main_message_loop), 77 // Sets the token to be used to launch the worker process.
77 ipc_message_loop_(ipc_message_loop), 78 virtual void ResetUserToken(ScopedHandle* user_token) = 0;
78 monitor_(monitor), 79
79 job_state_(kJobUninitialized) { 80 // Stops the object asynchronously.
80 monitor_->AddWtsConsoleObserver(this); 81 virtual void Stop() = 0;
81 82
83 protected:
84 friend class base::RefCountedThreadSafe<WtsSessionProcessLauncherImpl>;
85 virtual ~WtsSessionProcessLauncherImpl();
86 };
87
88 namespace {
89
90 // Implements |WorkerProcessLauncher::Delegate| that starts the specified
91 // process in a different session via CreateProcessAsUser() and uses
92 // the returned handle to monitor and terminate the process.
93 class SingleProcessLauncher : public WtsSessionProcessLauncherImpl {
94 public:
95 SingleProcessLauncher(
96 const FilePath& binary_path,
97 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
98
99 // WorkerProcessLauncher::Delegate implementation.
100 virtual bool DoLaunchProcess(
101 const std::string& channel_name,
102 ScopedHandle* process_exit_event_out) OVERRIDE;
103 virtual void DoKillProcess(DWORD exit_code) OVERRIDE;
104
105 // WtsSessionProcessLauncherImpl implementation.
106 virtual DWORD GetExitCode() OVERRIDE;
107 virtual void ResetUserToken(ScopedHandle* user_token) OVERRIDE;
108 virtual void Stop() OVERRIDE;
109
110 private:
111 FilePath binary_path_;
112
113 // The task runner all public methods of this class should be called on.
114 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
115
116 // A handle that becomes signalled once the process associated has been
117 // terminated.
118 ScopedHandle process_exit_event_;
119
120 // The token to be used to launch a process in a different session.
121 ScopedHandle user_token_;
122
123 // The handle of the worker process, if launched.
124 ScopedHandle worker_process_;
125
126 DISALLOW_COPY_AND_ASSIGN(SingleProcessLauncher);
127 };
128
129 // Implements |WorkerProcessLauncher::Delegate| that starts the specified
130 // process (UAC) elevated in a different session. |ElevatedProcessLauncher|
131 // utilizes a helper process to bypass limitations of ShellExecute() which
132 // cannot spawn processes across the session boundary.
133 class ElevatedProcessLauncher : public WtsSessionProcessLauncherImpl,
134 public base::MessagePumpForIO::IOHandler {
135 public:
136 ElevatedProcessLauncher(
137 const FilePath& binary_path,
138 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
139 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner);
140
141 // base::MessagePumpForIO::IOHandler implementation.
142 virtual void OnIOCompleted(base::MessagePumpForIO::IOContext* context,
143 DWORD bytes_transferred,
144 DWORD error) OVERRIDE;
145
146 // WorkerProcessLauncher::Delegate implementation.
147 virtual bool DoLaunchProcess(
148 const std::string& channel_name,
149 ScopedHandle* process_exit_event_out) OVERRIDE;
150 virtual void DoKillProcess(DWORD exit_code) OVERRIDE;
151
152 // WtsSessionProcessLauncherImpl implementation.
153 virtual DWORD GetExitCode() OVERRIDE;
154 virtual void ResetUserToken(ScopedHandle* user_token) OVERRIDE;
155 virtual void Stop() OVERRIDE;
156
157 private:
158 // Drains the completion port queue to make sure that all job object
159 // notifications have been received.
160 void DrainJobNotifications();
161
162 // Notified that the completion port queue has been drained.
163 void DrainJobNotificationsCompleted();
164
165 // Creates and initializes the job object that will sandbox the launched child
166 // processes.
167 void InitializeJob();
168
169 // Notified that the job object initialization is complete.
170 void InitializeJobCompleted(scoped_ptr<ScopedHandle> job);
171
172 // Called to process incoming job object notifications.
173 void OnJobNotification(DWORD message, DWORD pid);
174
175 FilePath binary_path_;
176
177 // The task runner all public methods of this class should be called on.
178 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
179
180 // The task runner serving job object notifications.
181 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner_;
182
183 // The job object used to control the lifetime of child processes.
184 ScopedHandle job_;
185
186 // A waiting handle that becomes signalled once all process associated with
187 // the job have been terminated.
188 ScopedHandle process_exit_event_;
189
190 // The token to be used to launch a process in a different session.
191 ScopedHandle user_token_;
192
193 // The handle of the worker process, if launched.
194 ScopedHandle worker_process_;
195
196 DISALLOW_COPY_AND_ASSIGN(ElevatedProcessLauncher);
197 };
198
199 SingleProcessLauncher::SingleProcessLauncher(
200 const FilePath& binary_path,
201 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
202 : binary_path_(binary_path),
203 main_task_runner_(main_task_runner) {
204 }
205
206 bool SingleProcessLauncher::DoLaunchProcess(
207 const std::string& channel_name,
208 ScopedHandle* process_exit_event_out) {
209 DCHECK(main_task_runner_->BelongsToCurrentThread());
210
211 // Create the command line passing the name of the IPC channel to use and
212 // copying known switches from the caller's command line.
213 CommandLine command_line(binary_path_);
214 command_line.AppendSwitchNative(kDaemonIpcSwitchName,
215 UTF8ToWide(channel_name));
216 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
217 kCopiedSwitchNames,
218 _countof(kCopiedSwitchNames));
219
220 // Try to launch the process and attach an object watcher to the returned
221 // handle so that we get notified when the process terminates.
222 ScopedHandle worker_thread;
223 worker_process_.Close();
224 if (!LaunchProcessWithToken(binary_path_,
225 command_line.GetCommandLineString(),
226 user_token_,
227 0,
228 &worker_process_,
229 &worker_thread)) {
230 return false;
231 }
232
233 ScopedHandle process_exit_event;
234 if (!DuplicateHandle(GetCurrentProcess(),
235 worker_process_,
236 GetCurrentProcess(),
237 process_exit_event.Receive(),
238 SYNCHRONIZE,
239 FALSE,
240 0)) {
241 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
242 DoKillProcess(CONTROL_C_EXIT);
243 return false;
244 }
245
246 *process_exit_event_out = process_exit_event.Pass();
247 return true;
248 }
249
250 void SingleProcessLauncher::DoKillProcess(DWORD exit_code) {
251 DCHECK(main_task_runner_->BelongsToCurrentThread());
252
253 if (worker_process_.IsValid()) {
254 TerminateProcess(worker_process_, exit_code);
255 }
256 }
257
258 DWORD SingleProcessLauncher::GetExitCode() {
259 DCHECK(main_task_runner_->BelongsToCurrentThread());
260
261 DWORD exit_code = CONTROL_C_EXIT;
262 if (worker_process_.IsValid()) {
263 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
264 LOG_GETLASTERROR(INFO)
265 << "Failed to query the exit code of the worker process";
266 exit_code = CONTROL_C_EXIT;
267 }
268
269 worker_process_.Close();
270 }
271
272 return exit_code;
273 }
274
275 void SingleProcessLauncher::ResetUserToken(ScopedHandle* user_token) {
276 DCHECK(main_task_runner_->BelongsToCurrentThread());
277
278 user_token_ = user_token->Pass();
279 }
280
281 void SingleProcessLauncher::Stop() {
282 DCHECK(main_task_runner_->BelongsToCurrentThread());
283 }
284
285 ElevatedProcessLauncher::ElevatedProcessLauncher(
286 const FilePath& binary_path,
287 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
288 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner)
289 : binary_path_(binary_path),
290 main_task_runner_(main_task_runner),
291 io_task_runner_(io_task_runner) {
82 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL)); 292 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
83 CHECK(process_exit_event_.IsValid()); 293 CHECK(process_exit_event_.IsValid());
84 294
85 // To receive job object notifications it is registered with the completion 295 // To receive job object notifications the job object is registered with
86 // port represented by |ipc_message_loop|. The registration has to be done on 296 // the completion port represented by |io_task_runner|. The registration has
87 // the I/O thread because MessageLoopForIO::RegisterJobObject() can only be 297 // to be done on the I/O thread because MessageLoopForIO::RegisterJobObject()
88 // called via MessageLoopForIO::current(). 298 // can only be called via MessageLoopForIO::current().
89 ipc_message_loop_->PostTask(FROM_HERE, base::Bind( 299 io_task_runner_->PostTask(
90 &WtsSessionProcessLauncher::InitializeJob, 300 FROM_HERE,
91 base::Unretained(this))); 301 base::Bind(&ElevatedProcessLauncher::InitializeJob, this));
92 } 302 }
93 303
94 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { 304 void ElevatedProcessLauncher::OnIOCompleted(
95 // Make sure that the object is completely stopped. The same check exists
96 // in Stoppable::~Stoppable() but this one helps us to fail early and
97 // predictably.
98 CHECK_EQ(stoppable_state(), Stoppable::kStopped);
99
100 monitor_->RemoveWtsConsoleObserver(this);
101
102 CHECK(!attached_);
103 CHECK(!timer_.IsRunning());
104 }
105
106 void WtsSessionProcessLauncher::OnIOCompleted(
107 base::MessagePumpForIO::IOContext* context, 305 base::MessagePumpForIO::IOContext* context,
108 DWORD bytes_transferred, 306 DWORD bytes_transferred,
109 DWORD error) { 307 DWORD error) {
110 DCHECK(ipc_message_loop_->BelongsToCurrentThread()); 308 DCHECK(io_task_runner_->BelongsToCurrentThread());
111 309
112 // |bytes_transferred| is used in job object notifications to supply 310 // |bytes_transferred| is used in job object notifications to supply
113 // the message ID; |context| carries process ID. 311 // the message ID; |context| carries process ID.
114 main_message_loop_->PostTask(FROM_HERE, base::Bind( 312 main_task_runner_->PostTask(FROM_HERE, base::Bind(
115 &WtsSessionProcessLauncher::OnJobNotification, 313 &ElevatedProcessLauncher::OnJobNotification, this, bytes_transferred,
116 base::Unretained(this), bytes_transferred,
117 reinterpret_cast<DWORD>(context))); 314 reinterpret_cast<DWORD>(context)));
118 } 315 }
119 316
120 bool WtsSessionProcessLauncher::DoLaunchProcess( 317 bool ElevatedProcessLauncher::DoLaunchProcess(
121 const std::string& channel_name, 318 const std::string& channel_name,
122 ScopedHandle* process_exit_event_out) { 319 ScopedHandle* process_exit_event_out) {
123 DCHECK(main_message_loop_->BelongsToCurrentThread()); 320 DCHECK(main_task_runner_->BelongsToCurrentThread());
124 321
125 // The job object is not ready. Retry starting the host process later. 322 // The job object is not ready. Retry starting the host process later.
126 if (!job_.IsValid()) { 323 if (!job_.IsValid()) {
127 return false; 324 return false;
128 } 325 }
129 326
130 // Construct the host binary name. 327 // Construct the helper binary name.
131 FilePath dir_path; 328 FilePath dir_path;
132 if (!PathService::Get(base::DIR_EXE, &dir_path)) { 329 if (!PathService::Get(base::DIR_EXE, &dir_path)) {
133 LOG(ERROR) << "Failed to get the executable file name."; 330 LOG(ERROR) << "Failed to get the executable file name.";
134 return false; 331 return false;
135 } 332 }
136 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
137 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName); 333 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName);
138 334
139 // Create the host process command line passing the name of the IPC channel 335 // Create the command line passing the name of the IPC channel to use and
140 // to use and copying known switches from the service's command line. 336 // copying known switches from the caller's command line.
141 CommandLine command_line(service_binary); 337 CommandLine command_line(service_binary);
142 command_line.AppendSwitchPath(kElevateSwitchName, host_binary); 338 command_line.AppendSwitchPath(kElevateSwitchName, binary_path_);
143 command_line.AppendSwitchNative(kDaemonIpcSwitchName, 339 command_line.AppendSwitchNative(kDaemonIpcSwitchName,
144 UTF8ToWide(channel_name)); 340 UTF8ToWide(channel_name));
145 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), 341 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
146 kCopiedSwitchNames, 342 kCopiedSwitchNames,
147 _countof(kCopiedSwitchNames)); 343 _countof(kCopiedSwitchNames));
148 344
149 CHECK(ResetEvent(process_exit_event_)); 345 CHECK(ResetEvent(process_exit_event_));
150 346
151 // Try to launch the process and attach an object watcher to the returned 347 // Try to launch the process and attach an object watcher to the returned
152 // handle so that we get notified when the process terminates. 348 // handle so that we get notified when the process terminates.
153 base::win::ScopedHandle worker_process; 349 ScopedHandle worker_process;
154 base::win::ScopedHandle worker_thread; 350 ScopedHandle worker_thread;
155 if (!LaunchProcessWithToken(service_binary, 351 if (!LaunchProcessWithToken(service_binary,
156 command_line.GetCommandLineString(), 352 command_line.GetCommandLineString(),
157 session_token_, 353 user_token_,
158 CREATE_SUSPENDED, 354 CREATE_SUSPENDED,
159 &worker_process, 355 &worker_process,
160 &worker_thread)) { 356 &worker_thread)) {
161 return false; 357 return false;
162 } 358 }
163 359
164 if (!AssignProcessToJobObject(job_, worker_process)) { 360 if (!AssignProcessToJobObject(job_, worker_process)) {
165 LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object"; 361 LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object";
166 TerminateProcess(worker_process, CONTROL_C_EXIT); 362 TerminateProcess(worker_process, CONTROL_C_EXIT);
167 return false; 363 return false;
(...skipping 15 matching lines...) Expand all
183 0)) { 379 0)) {
184 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; 380 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
185 DoKillProcess(CONTROL_C_EXIT); 381 DoKillProcess(CONTROL_C_EXIT);
186 return false; 382 return false;
187 } 383 }
188 384
189 *process_exit_event_out = process_exit_event.Pass(); 385 *process_exit_event_out = process_exit_event.Pass();
190 return true; 386 return true;
191 } 387 }
192 388
193 void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) { 389 void ElevatedProcessLauncher::DoKillProcess(DWORD exit_code) {
194 DCHECK(main_message_loop_->BelongsToCurrentThread()); 390 DCHECK(main_task_runner_->BelongsToCurrentThread());
195 391
196 if (job_.IsValid()) { 392 if (job_.IsValid()) {
197 TerminateJobObject(job_, exit_code); 393 TerminateJobObject(job_, exit_code);
198 } 394 }
199 } 395 }
200 396
201 void WtsSessionProcessLauncher::OnChannelConnected() { 397 DWORD ElevatedProcessLauncher::GetExitCode() {
202 DCHECK(main_message_loop_->BelongsToCurrentThread()); 398 DCHECK(main_task_runner_->BelongsToCurrentThread());
399
400 DWORD exit_code = CONTROL_C_EXIT;
401 if (worker_process_.IsValid()) {
402 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
403 LOG_GETLASTERROR(INFO)
404 << "Failed to query the exit code of the worker process";
405 exit_code = CONTROL_C_EXIT;
406 }
407
408 worker_process_.Close();
409 }
410
411 return exit_code;
203 } 412 }
204 413
205 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { 414 void ElevatedProcessLauncher::ResetUserToken(
206 DCHECK(main_message_loop_->BelongsToCurrentThread()); 415 ScopedHandle* user_token) {
416 DCHECK(main_task_runner_->BelongsToCurrentThread());
207 417
208 return false; 418 user_token_ = user_token->Pass();
209 } 419 }
210 420
211 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { 421 void ElevatedProcessLauncher::Stop() {
212 DCHECK(main_message_loop_->BelongsToCurrentThread()); 422 DCHECK(main_task_runner_->BelongsToCurrentThread());
213
214 if (stoppable_state() != Stoppable::kRunning) {
215 return;
216 }
217
218 DCHECK(!attached_);
219 DCHECK(!timer_.IsRunning());
220
221 attached_ = true;
222
223 // Create a session token for the launched process.
224 if (!CreateSessionToken(session_id, &session_token_))
225 return;
226
227 // Now try to launch the host.
228 LaunchProcess();
229 }
230
231 void WtsSessionProcessLauncher::OnSessionDetached() {
232 DCHECK(main_message_loop_->BelongsToCurrentThread());
233 DCHECK(attached_);
234
235 attached_ = false;
236 launch_backoff_ = base::TimeDelta();
237 session_token_.Close();
238 timer_.Stop();
239
240 if (launcher_.get() != NULL) {
241 launcher_->Stop();
242 }
243 }
244
245 void WtsSessionProcessLauncher::DoStop() {
246 DCHECK(main_message_loop_->BelongsToCurrentThread());
247
248 if (attached_) {
249 OnSessionDetached();
250 }
251
252 job_.Close();
253 423
254 // Drain the completion queue to make sure all job object notification have 424 // Drain the completion queue to make sure all job object notification have
255 // been received. 425 // been received.
256 if (job_state_ == kJobRunning) { 426 DrainJobNotificationsCompleted();
257 job_state_ = kJobStopping;
258 ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
259 &WtsSessionProcessLauncher::DrainJobNotifications,
260 base::Unretained(this)));
261 }
262
263 // Don't complete shutdown if |launcher_| is not completely stopped.
264 if (launcher_.get() != NULL) {
265 return;
266 }
267
268 // Don't complete shutdown if the completion queue hasn't been drained.
269 if (job_state_ != kJobUninitialized && job_state_ != kJobStopped) {
270 return;
271 }
272
273 CompleteStopping();
274 } 427 }
275 428
276 void WtsSessionProcessLauncher::DrainJobNotifications() { 429 void ElevatedProcessLauncher::DrainJobNotifications() {
277 DCHECK(ipc_message_loop_->BelongsToCurrentThread()); 430 DCHECK(io_task_runner_->BelongsToCurrentThread());
278 431
279 // DrainJobNotifications() is posted after the job object is destroyed, so 432 // DrainJobNotifications() is posted after the job object is destroyed, so
280 // by this time all notifications from the job object have been processed 433 // by this time all notifications from the job object have been processed
281 // already. Let the main thread know that the queue has been drained. 434 // already. Let the main thread know that the queue has been drained.
282 main_message_loop_->PostTask(FROM_HERE, base::Bind( 435 main_task_runner_->PostTask(FROM_HERE, base::Bind(
283 &WtsSessionProcessLauncher::DrainJobNotificationsCompleted, 436 &ElevatedProcessLauncher::DrainJobNotificationsCompleted, this));
284 base::Unretained(this)));
285 } 437 }
286 438
287 void WtsSessionProcessLauncher::DrainJobNotificationsCompleted() { 439 void ElevatedProcessLauncher::DrainJobNotificationsCompleted() {
288 DCHECK(main_message_loop_->BelongsToCurrentThread()); 440 DCHECK(main_task_runner_->BelongsToCurrentThread());
289 DCHECK_EQ(job_state_, kJobStopping);
290 441
291 job_state_ = kJobStopped; 442 if (job_.IsValid()) {
292 Stop(); 443 job_.Close();
444
445 // Drain the completion queue to make sure all job object notification have
446 // been received.
447 io_task_runner_->PostTask(FROM_HERE, base::Bind(
448 &ElevatedProcessLauncher::DrainJobNotifications, this));
449 }
293 } 450 }
294 451
295 void WtsSessionProcessLauncher::InitializeJob() { 452 void ElevatedProcessLauncher::InitializeJob() {
296 DCHECK(ipc_message_loop_->BelongsToCurrentThread()); 453 DCHECK(io_task_runner_->BelongsToCurrentThread());
297 454
298 ScopedHandle job; 455 ScopedHandle job;
299 job.Set(CreateJobObject(NULL, NULL)); 456 job.Set(CreateJobObject(NULL, NULL));
300 if (!job.IsValid()) { 457 if (!job.IsValid()) {
301 LOG_GETLASTERROR(ERROR) << "Failed to create a job object"; 458 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
302 return; 459 return;
303 } 460 }
304 461
305 // Limit the number of active processes in the job to two (the process 462 // Limit the number of active processes in the job to two (the process
306 // performing elevation and the host) and make sure that all processes will be 463 // performing elevation and the host) and make sure that all processes will be
(...skipping 18 matching lines...) Expand all
325 << "Failed to associate the job object with a completion port"; 482 << "Failed to associate the job object with a completion port";
326 return; 483 return;
327 } 484 }
328 485
329 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped 486 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
330 // pointer. 487 // pointer.
331 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle()); 488 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
332 *job_wrapper = job.Pass(); 489 *job_wrapper = job.Pass();
333 490
334 // Let the main thread know that initialization is complete. 491 // Let the main thread know that initialization is complete.
335 main_message_loop_->PostTask(FROM_HERE, base::Bind( 492 main_task_runner_->PostTask(FROM_HERE, base::Bind(
336 &WtsSessionProcessLauncher::InitializeJobCompleted, 493 &ElevatedProcessLauncher::InitializeJobCompleted, this,
337 base::Unretained(this), base::Passed(&job_wrapper))); 494 base::Passed(&job_wrapper)));
338 } 495 }
339 496
340 void WtsSessionProcessLauncher::InitializeJobCompleted( 497 void ElevatedProcessLauncher::InitializeJobCompleted(
341 scoped_ptr<ScopedHandle> job) { 498 scoped_ptr<ScopedHandle> job) {
342 DCHECK(main_message_loop_->BelongsToCurrentThread()); 499 DCHECK(main_task_runner_->BelongsToCurrentThread());
343 DCHECK(!job_.IsValid()); 500 DCHECK(!job_.IsValid());
344 DCHECK_EQ(job_state_, kJobUninitialized);
345 501
346 if (stoppable_state() == Stoppable::kRunning) { 502 job_ = job->Pass();
347 job_ = job->Pass();
348 job_state_ = kJobRunning;
349 }
350 } 503 }
351 504
352 void WtsSessionProcessLauncher::OnJobNotification(DWORD message, DWORD pid) { 505 void ElevatedProcessLauncher::OnJobNotification(DWORD message, DWORD pid) {
353 DCHECK(main_message_loop_->BelongsToCurrentThread()); 506 DCHECK(main_task_runner_->BelongsToCurrentThread());
354 507
355 switch (message) { 508 switch (message) {
356 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: 509 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
357 CHECK(SetEvent(process_exit_event_)); 510 CHECK(SetEvent(process_exit_event_));
358 break; 511 break;
359 512
360 case JOB_OBJECT_MSG_NEW_PROCESS: 513 case JOB_OBJECT_MSG_NEW_PROCESS:
361 // We report the exit code of the worker process to be |CONTROL_C_EXIT| 514 // We report the exit code of the worker process to be |CONTROL_C_EXIT|
362 // if we cannot get the actual exit code. So here we can safely ignore 515 // if we cannot get the actual exit code. So here we can safely ignore
363 // the error returned by OpenProcess(). 516 // the error returned by OpenProcess().
364 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)); 517 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
365 break; 518 break;
366 } 519 }
367 } 520 }
368 521
522 } // namespace
523
524 WtsSessionProcessLauncherImpl::~WtsSessionProcessLauncherImpl() {
525 }
526
527 WtsSessionProcessLauncher::WtsSessionProcessLauncher(
528 const base::Closure& stopped_callback,
529 WtsConsoleMonitor* monitor,
530 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop,
531 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop)
532 : Stoppable(main_message_loop, stopped_callback),
533 attached_(false),
534 main_message_loop_(main_message_loop),
535 ipc_message_loop_(ipc_message_loop),
536 monitor_(monitor) {
537 monitor_->AddWtsConsoleObserver(this);
538
539 // Construct the host binary name.
540 FilePath dir_path;
541 if (!PathService::Get(base::DIR_EXE, &dir_path)) {
542 LOG(ERROR) << "Failed to get the executable file name.";
543 Stop();
544 return;
545 }
546 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
547
548 // The workaround we use to launch a process in a not yet logged in session
549 // (see CreateRemoteSessionProcess()) on Windows XP/2K3 does not let us to
550 // assign the started process to a job. However on Vista+ we have to start
551 // the host via a helper process. The helper process calls ShellExecute() that
552 // does not work across the session boundary but it required to launch
553 // a binary specifying uiAccess='true' in its manifest.
554 //
555 // Below we choose which implementation of |WorkerProcessLauncher::Delegate|
556 // to use. A single process is launched on XP (since uiAccess='true' does not
557 // have effect any way). Vista+ utilizes a helper process and assign it to
558 // a job object to control it.
559 if (base::win::GetVersion() == base::win::VERSION_XP) {
560 impl_ = new SingleProcessLauncher(host_binary, main_message_loop);
561 } else {
562 impl_ = new ElevatedProcessLauncher(host_binary, main_message_loop,
563 ipc_message_loop);
564 }
565 }
566
567 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
568 // Make sure that the object is completely stopped. The same check exists
569 // in Stoppable::~Stoppable() but this one helps us to fail early and
570 // predictably.
571 CHECK_EQ(stoppable_state(), Stoppable::kStopped);
572
573 monitor_->RemoveWtsConsoleObserver(this);
574
575 CHECK(!attached_);
576 CHECK(!timer_.IsRunning());
577 }
578
579 void WtsSessionProcessLauncher::OnChannelConnected() {
580 DCHECK(main_message_loop_->BelongsToCurrentThread());
581 }
582
583 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
584 DCHECK(main_message_loop_->BelongsToCurrentThread());
585
586 return false;
587 }
588
589 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
590 DCHECK(main_message_loop_->BelongsToCurrentThread());
591
592 if (stoppable_state() != Stoppable::kRunning) {
593 return;
594 }
595
596 DCHECK(!attached_);
597 DCHECK(!timer_.IsRunning());
598
599 attached_ = true;
600
601 // Create a session token and launch the host.
602 ScopedHandle user_token;
603 if (CreateSessionToken(session_id, &user_token)) {
604 impl_->ResetUserToken(&user_token);
605 LaunchProcess();
606 }
607 }
608
609 void WtsSessionProcessLauncher::OnSessionDetached() {
610 DCHECK(main_message_loop_->BelongsToCurrentThread());
611 DCHECK(attached_);
612
613 attached_ = false;
614 launch_backoff_ = base::TimeDelta();
615 timer_.Stop();
616
617 if (launcher_.get() != NULL) {
618 launcher_->Stop();
619 }
620 }
621
622 void WtsSessionProcessLauncher::DoStop() {
623 DCHECK(main_message_loop_->BelongsToCurrentThread());
624
625 if (attached_) {
626 OnSessionDetached();
627 }
628
629 // Don't complete shutdown if |launcher_| is not completely stopped.
630 if (launcher_.get() != NULL) {
631 return;
632 }
633
634 // Tear down the core asynchromously.
635 if (impl_.get()) {
636 impl_->Stop();
637 impl_ = NULL;
638 }
639
640 CompleteStopping();
641 }
642
369 void WtsSessionProcessLauncher::LaunchProcess() { 643 void WtsSessionProcessLauncher::LaunchProcess() {
370 DCHECK(main_message_loop_->BelongsToCurrentThread()); 644 DCHECK(main_message_loop_->BelongsToCurrentThread());
371 DCHECK(attached_); 645 DCHECK(attached_);
372 DCHECK(launcher_.get() == NULL); 646 DCHECK(launcher_.get() == NULL);
373 DCHECK(!timer_.IsRunning()); 647 DCHECK(!timer_.IsRunning());
374 648
375 launch_time_ = base::Time::Now(); 649 launch_time_ = base::Time::Now();
376 launcher_.reset(new WorkerProcessLauncher( 650 launcher_.reset(new WorkerProcessLauncher(
377 this, this, 651 impl_.get(), this,
378 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, 652 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
379 base::Unretained(this)), 653 base::Unretained(this)),
380 main_message_loop_, 654 main_message_loop_,
381 ipc_message_loop_)); 655 ipc_message_loop_));
382 launcher_->Start(kDaemonIpcSecurityDescriptor); 656 launcher_->Start(kDaemonIpcSecurityDescriptor);
383 } 657 }
384 658
385 void WtsSessionProcessLauncher::OnLauncherStopped() { 659 void WtsSessionProcessLauncher::OnLauncherStopped() {
386 DCHECK(main_message_loop_->BelongsToCurrentThread()); 660 DCHECK(main_message_loop_->BelongsToCurrentThread());
387 661
388 DWORD exit_code = CONTROL_C_EXIT; 662 // Retrieve the exit code of the worker process.
389 if (worker_process_.IsValid()) { 663 DWORD exit_code = impl_->GetExitCode();
390 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
391 LOG_GETLASTERROR(INFO)
392 << "Failed to query the exit code of the worker process";
393 exit_code = CONTROL_C_EXIT;
394 }
395
396 worker_process_.Close();
397 }
398 664
399 launcher_.reset(NULL); 665 launcher_.reset(NULL);
400 666
401 // Do not relaunch the worker process if the caller has asked us to stop. 667 // Do not relaunch the worker process if the caller has asked us to stop.
402 if (stoppable_state() != Stoppable::kRunning) { 668 if (stoppable_state() != Stoppable::kRunning) {
403 Stop(); 669 Stop();
404 return; 670 return;
405 } 671 }
406 672
407 // Stop trying to restart the worker process if its process exited due to 673 // Stop trying to restart the worker process if its process exited due to
(...skipping 19 matching lines...) Expand all
427 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); 693 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
428 } 694 }
429 695
430 // Try to launch the worker process. 696 // Try to launch the worker process.
431 timer_.Start(FROM_HERE, launch_backoff_, 697 timer_.Start(FROM_HERE, launch_backoff_,
432 this, &WtsSessionProcessLauncher::LaunchProcess); 698 this, &WtsSessionProcessLauncher::LaunchProcess);
433 } 699 }
434 } 700 }
435 701
436 } // namespace remoting 702 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/win/wts_session_process_launcher.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698