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

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: CR feedback. 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
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 WtsProcessLauncherImpl
70 const base::Closure& stopped_callback, 71 : public base::RefCountedThreadSafe<WtsProcessLauncherImpl>,
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 bool SetSessionToken(HANDLE session_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<WtsProcessLauncherImpl>;
85 virtual ~WtsProcessLauncherImpl();
86 };
87
88 namespace {
89
90 // 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.
91 // in a different session via CreateProcessAsUser and uses the returned process
92 // 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.
93 class SingleProcessLauncher : public WtsProcessLauncherImpl {
94 public:
95 SingleProcessLauncher(
96 const FilePath& binary,
Wez 2012/09/14 23:36:18 nit: binary_path
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
97 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner);
98
99 // WorkerProcessLauncher::Delegate implementation.
100 virtual bool DoLaunchProcess(
101 const std::string& channel_name,
102 base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
103 virtual void DoKillProcess(DWORD exit_code) OVERRIDE;
104
105 // WtsProcessLauncherImpl implementation.
106 virtual DWORD GetExitCode() OVERRIDE;
107 virtual bool SetSessionToken(HANDLE session_token) OVERRIDE;
108 virtual void Stop() OVERRIDE;
109
110 private:
111 FilePath binary_;
112
113 // The main service message loop.
114 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
115
116 // 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.
117 // the job have been terminated.
118 base::win::ScopedHandle process_exit_event_;
119
120 // The token to be used to launch a process in a different session.
121 base::win::ScopedHandle session_token_;
122
123 base::win::ScopedHandle worker_process_;
124
125 DISALLOW_COPY_AND_ASSIGN(SingleProcessLauncher);
126 };
127
128 // Implements |WorkerProcessLauncher::Delegate| that starts the host process
129 // 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.
130 // utilizes a helper process to bypass limitations of ShellExecute() which
131 // cannot spawn processes across the session boundary. ShellExecute() in its
132 // turn is needed to start a binary specifying uiAccess='true' in its manifest.
133 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,
134 public base::MessagePumpForIO::IOHandler {
135 public:
136 InJobLauncher(
137 const FilePath& binary,
138 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
139 scoped_refptr<base::SingleThreadTaskRunner> ipc_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 base::win::ScopedHandle* process_exit_event_out) OVERRIDE;
150 virtual void DoKillProcess(DWORD exit_code) OVERRIDE;
151
152 // WtsProcessLauncherImpl implementation.
153 virtual DWORD GetExitCode() OVERRIDE;
154 virtual bool SetSessionToken(HANDLE session_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 has been received.
Wez 2012/09/14 23:36:18 typo: has -> have
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
160 void DrainJobNotifications();
161
162 // Notifies that the completion port queue has been drained.
163 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.
164
165 // Creates and initializes the job object that will sandbox the launched child
166 // processes.
167 void InitializeJob();
168
169 // 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.
170 void InitializeJobCompleted(scoped_ptr<base::win::ScopedHandle> job);
171
172 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.
173
174 FilePath binary_;
175
176 // 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.
177 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_;
178
179 // Message loop used by the IPC channel.
180 scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner_;
181
182 // The job object used to control the lifetime of child processes.
183 base::win::ScopedHandle job_;
184
185 // A waiting handle that becomes signalled once all process associated with
186 // the job have been terminated.
187 base::win::ScopedHandle process_exit_event_;
188
189 // The token to be used to launch a process in a different session.
190 base::win::ScopedHandle session_token_;
191
192 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.
193
194 DISALLOW_COPY_AND_ASSIGN(InJobLauncher);
195 };
196
197 SingleProcessLauncher::SingleProcessLauncher(
198 const FilePath& binary,
199 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner)
200 : binary_(binary),
201 main_task_runner_(main_task_runner) {
202 }
203
204 bool SingleProcessLauncher::DoLaunchProcess(
205 const std::string& channel_name,
206 base::win::ScopedHandle* process_exit_event_out) {
207 DCHECK(main_task_runner_->BelongsToCurrentThread());
208
209 // 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.
210 // 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.
211 CommandLine command_line(binary_);
212 command_line.AppendSwitchNative(kDaemonIpcSwitchName,
213 UTF8ToWide(channel_name));
214 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
215 kCopiedSwitchNames,
216 _countof(kCopiedSwitchNames));
217
218 // Try to launch the process and attach an object watcher to the returned
219 // handle so that we get notified when the process terminates.
220 base::win::ScopedHandle worker_thread;
221 worker_process_.Close();
222 if (!LaunchProcessWithToken(binary_,
223 command_line.GetCommandLineString(),
224 session_token_,
225 0,
226 &worker_process_,
227 &worker_thread)) {
228 return false;
229 }
230
231 ScopedHandle process_exit_event;
232 if (!DuplicateHandle(GetCurrentProcess(),
233 worker_process_,
234 GetCurrentProcess(),
235 process_exit_event.Receive(),
236 SYNCHRONIZE,
237 FALSE,
238 0)) {
239 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
240 DoKillProcess(CONTROL_C_EXIT);
241 return false;
242 }
243
244 *process_exit_event_out = process_exit_event.Pass();
245 return true;
246 }
247
248 void SingleProcessLauncher::DoKillProcess(DWORD exit_code) {
249 DCHECK(main_task_runner_->BelongsToCurrentThread());
250
251 if (worker_process_.IsValid()) {
252 TerminateProcess(worker_process_, exit_code);
253 }
254 }
255
256 DWORD SingleProcessLauncher::GetExitCode() {
257 DWORD exit_code = CONTROL_C_EXIT;
258 if (worker_process_.IsValid()) {
259 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
260 LOG_GETLASTERROR(INFO)
261 << "Failed to query the exit code of the worker process";
262 exit_code = CONTROL_C_EXIT;
263 }
264
265 worker_process_.Close();
266 }
267
268 return exit_code;
269 }
270
271 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.
272 session_token_.Close();
273 if (!DuplicateHandle(GetCurrentProcess(),
274 session_token,
275 GetCurrentProcess(),
276 session_token_.Receive(),
277 0,
278 FALSE,
279 DUPLICATE_SAME_ACCESS)) {
280 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
281 return false;
282 }
283
284 return true;
285 }
286
287 void SingleProcessLauncher::Stop() {
288 }
289
290 InJobLauncher::InJobLauncher(
291 const FilePath& binary,
292 scoped_refptr<base::SingleThreadTaskRunner> main_task_runner,
293 scoped_refptr<base::SingleThreadTaskRunner> ipc_task_runner)
294 : binary_(binary),
295 main_task_runner_(main_task_runner),
296 ipc_task_runner_(ipc_task_runner) {
82 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL)); 297 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
83 CHECK(process_exit_event_.IsValid()); 298 CHECK(process_exit_event_.IsValid());
84 299
85 // To receive job object notifications it is registered with the completion 300 // To receive job object notifications it is registered with the completion
Wez 2012/09/14 23:36:18 nit: What is "it"? You mean this object?
alexeypa (please no reviews) 2012/09/18 16:58:06 Done.
86 // port represented by |ipc_message_loop|. The registration has to be done on 301 // port represented by |ipc_message_loop|. The registration has to be done on
87 // the I/O thread because MessageLoopForIO::RegisterJobObject() can only be 302 // the I/O thread because MessageLoopForIO::RegisterJobObject() can only be
88 // called via MessageLoopForIO::current(). 303 // called via MessageLoopForIO::current().
89 ipc_message_loop_->PostTask(FROM_HERE, base::Bind( 304 ipc_task_runner_->PostTask(FROM_HERE,
90 &WtsSessionProcessLauncher::InitializeJob, 305 base::Bind(&InJobLauncher::InitializeJob, this));
91 base::Unretained(this)));
92 } 306 }
93 307
94 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { 308 void InJobLauncher::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, 309 base::MessagePumpForIO::IOContext* context,
108 DWORD bytes_transferred, 310 DWORD bytes_transferred,
109 DWORD error) { 311 DWORD error) {
110 DCHECK(ipc_message_loop_->BelongsToCurrentThread()); 312 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
111 313
112 // |bytes_transferred| is used in job object notifications to supply 314 // |bytes_transferred| is used in job object notifications to supply
113 // the message ID; |context| carries process ID. 315 // the message ID; |context| carries process ID.
114 main_message_loop_->PostTask(FROM_HERE, base::Bind( 316 main_task_runner_->PostTask(FROM_HERE, base::Bind(
115 &WtsSessionProcessLauncher::OnJobNotification, 317 &InJobLauncher::OnJobNotification, this, bytes_transferred,
116 base::Unretained(this), bytes_transferred,
117 reinterpret_cast<DWORD>(context))); 318 reinterpret_cast<DWORD>(context)));
118 } 319 }
119 320
120 bool WtsSessionProcessLauncher::DoLaunchProcess( 321 bool InJobLauncher::DoLaunchProcess(
121 const std::string& channel_name, 322 const std::string& channel_name,
122 ScopedHandle* process_exit_event_out) { 323 base::win::ScopedHandle* process_exit_event_out) {
123 DCHECK(main_message_loop_->BelongsToCurrentThread()); 324 DCHECK(main_task_runner_->BelongsToCurrentThread());
124 325
125 // The job object is not ready. Retry starting the host process later. 326 // The job object is not ready. Retry starting the host process later.
126 if (!job_.IsValid()) { 327 if (!job_.IsValid()) {
127 return false; 328 return false;
128 } 329 }
129 330
130 // Construct the host binary name. 331 // Construct the helper binary binary name.
131 FilePath dir_path; 332 FilePath dir_path;
132 if (!PathService::Get(base::DIR_EXE, &dir_path)) { 333 if (!PathService::Get(base::DIR_EXE, &dir_path)) {
133 LOG(ERROR) << "Failed to get the executable file name."; 334 LOG(ERROR) << "Failed to get the executable file name.";
134 return false; 335 return false;
135 } 336 }
136 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
137 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName); 337 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName);
138 338
139 // Create the host process command line passing the name of the IPC channel 339 // Create the host process command line passing the name of the IPC channel
140 // to use and copying known switches from the service's command line. 340 // to use and copying known switches from the service's command line.
141 CommandLine command_line(service_binary); 341 CommandLine command_line(service_binary);
142 command_line.AppendSwitchPath(kElevateSwitchName, host_binary); 342 command_line.AppendSwitchPath(kElevateSwitchName, binary_);
143 command_line.AppendSwitchNative(kDaemonIpcSwitchName, 343 command_line.AppendSwitchNative(kDaemonIpcSwitchName,
144 UTF8ToWide(channel_name)); 344 UTF8ToWide(channel_name));
145 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), 345 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
146 kCopiedSwitchNames, 346 kCopiedSwitchNames,
147 _countof(kCopiedSwitchNames)); 347 _countof(kCopiedSwitchNames));
148 348
149 CHECK(ResetEvent(process_exit_event_)); 349 CHECK(ResetEvent(process_exit_event_));
150 350
151 // Try to launch the process and attach an object watcher to the returned 351 // Try to launch the process and attach an object watcher to the returned
152 // handle so that we get notified when the process terminates. 352 // handle so that we get notified when the process terminates.
(...skipping 30 matching lines...) Expand all
183 0)) { 383 0)) {
184 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; 384 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
185 DoKillProcess(CONTROL_C_EXIT); 385 DoKillProcess(CONTROL_C_EXIT);
186 return false; 386 return false;
187 } 387 }
188 388
189 *process_exit_event_out = process_exit_event.Pass(); 389 *process_exit_event_out = process_exit_event.Pass();
190 return true; 390 return true;
191 } 391 }
192 392
193 void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) { 393 void InJobLauncher::DoKillProcess(DWORD exit_code) {
194 DCHECK(main_message_loop_->BelongsToCurrentThread()); 394 DCHECK(main_task_runner_->BelongsToCurrentThread());
195 395
196 if (job_.IsValid()) { 396 if (job_.IsValid()) {
197 TerminateJobObject(job_, exit_code); 397 TerminateJobObject(job_, exit_code);
198 } 398 }
199 } 399 }
200 400
201 void WtsSessionProcessLauncher::OnChannelConnected() { 401 DWORD InJobLauncher::GetExitCode() {
202 DCHECK(main_message_loop_->BelongsToCurrentThread()); 402 DWORD exit_code = CONTROL_C_EXIT;
403 if (worker_process_.IsValid()) {
404 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
405 LOG_GETLASTERROR(INFO)
406 << "Failed to query the exit code of the worker process";
407 exit_code = CONTROL_C_EXIT;
408 }
409
410 worker_process_.Close();
411 }
412
413 return exit_code;
203 } 414 }
204 415
205 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { 416 bool InJobLauncher::SetSessionToken(HANDLE session_token) {
206 DCHECK(main_message_loop_->BelongsToCurrentThread()); 417 session_token_.Close();
418 if (!DuplicateHandle(GetCurrentProcess(),
419 session_token,
420 GetCurrentProcess(),
421 session_token_.Receive(),
422 0,
423 FALSE,
424 DUPLICATE_SAME_ACCESS)) {
425 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
426 return false;
427 }
207 428
208 return false; 429 return true;
209 } 430 }
210 431
211 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { 432 void InJobLauncher::Stop() {
212 DCHECK(main_message_loop_->BelongsToCurrentThread()); 433 // Drain the completion queue to make sure all job object notification have
213 434 // been received.
214 if (stoppable_state() != Stoppable::kRunning) { 435 DrainJobNotificationsCompleted();
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 } 436 }
230 437
231 void WtsSessionProcessLauncher::OnSessionDetached() { 438 void InJobLauncher::DrainJobNotifications() {
232 DCHECK(main_message_loop_->BelongsToCurrentThread()); 439 DCHECK(ipc_task_runner_->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
254 // Drain the completion queue to make sure all job object notification have
255 // been received.
256 if (job_state_ == kJobRunning) {
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 }
275
276 void WtsSessionProcessLauncher::DrainJobNotifications() {
277 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
278 440
279 // DrainJobNotifications() is posted after the job object is destroyed, so 441 // DrainJobNotifications() is posted after the job object is destroyed, so
280 // by this time all notifications from the job object have been processed 442 // 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. 443 // already. Let the main thread know that the queue has been drained.
282 main_message_loop_->PostTask(FROM_HERE, base::Bind( 444 main_task_runner_->PostTask(FROM_HERE, base::Bind(
283 &WtsSessionProcessLauncher::DrainJobNotificationsCompleted, 445 &InJobLauncher::DrainJobNotificationsCompleted, this));
284 base::Unretained(this)));
285 } 446 }
286 447
287 void WtsSessionProcessLauncher::DrainJobNotificationsCompleted() { 448 void InJobLauncher::DrainJobNotificationsCompleted() {
288 DCHECK(main_message_loop_->BelongsToCurrentThread()); 449 DCHECK(main_task_runner_->BelongsToCurrentThread());
289 DCHECK_EQ(job_state_, kJobStopping);
290 450
291 job_state_ = kJobStopped; 451 if (job_.IsValid()) {
292 Stop(); 452 job_.Close();
453
454 // Drain the completion queue to make sure all job object notification have
455 // been received.
456 ipc_task_runner_->PostTask(FROM_HERE, base::Bind(
457 &InJobLauncher::DrainJobNotifications, this));
458 }
293 } 459 }
294 460
295 void WtsSessionProcessLauncher::InitializeJob() { 461 void InJobLauncher::InitializeJob() {
296 DCHECK(ipc_message_loop_->BelongsToCurrentThread()); 462 DCHECK(ipc_task_runner_->BelongsToCurrentThread());
297 463
298 ScopedHandle job; 464 ScopedHandle job;
299 job.Set(CreateJobObject(NULL, NULL)); 465 job.Set(CreateJobObject(NULL, NULL));
300 if (!job.IsValid()) { 466 if (!job.IsValid()) {
301 LOG_GETLASTERROR(ERROR) << "Failed to create a job object"; 467 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
302 return; 468 return;
303 } 469 }
304 470
305 // Limit the number of active processes in the job to two (the process 471 // 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 472 // 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"; 491 << "Failed to associate the job object with a completion port";
326 return; 492 return;
327 } 493 }
328 494
329 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped 495 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
330 // pointer. 496 // pointer.
331 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle()); 497 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
332 *job_wrapper = job.Pass(); 498 *job_wrapper = job.Pass();
333 499
334 // Let the main thread know that initialization is complete. 500 // Let the main thread know that initialization is complete.
335 main_message_loop_->PostTask(FROM_HERE, base::Bind( 501 main_task_runner_->PostTask(FROM_HERE, base::Bind(
336 &WtsSessionProcessLauncher::InitializeJobCompleted, 502 &InJobLauncher::InitializeJobCompleted, this,
337 base::Unretained(this), base::Passed(&job_wrapper))); 503 base::Passed(&job_wrapper)));
338 } 504 }
339 505
340 void WtsSessionProcessLauncher::InitializeJobCompleted( 506 void InJobLauncher::InitializeJobCompleted(
341 scoped_ptr<ScopedHandle> job) { 507 scoped_ptr<ScopedHandle> job) {
342 DCHECK(main_message_loop_->BelongsToCurrentThread()); 508 DCHECK(main_task_runner_->BelongsToCurrentThread());
343 DCHECK(!job_.IsValid()); 509 DCHECK(!job_.IsValid());
344 DCHECK_EQ(job_state_, kJobUninitialized);
345 510
346 if (stoppable_state() == Stoppable::kRunning) { 511 job_ = job->Pass();
347 job_ = job->Pass();
348 job_state_ = kJobRunning;
349 }
350 } 512 }
351 513
352 void WtsSessionProcessLauncher::OnJobNotification(DWORD message, DWORD pid) { 514 void InJobLauncher::OnJobNotification(DWORD message, DWORD pid) {
353 DCHECK(main_message_loop_->BelongsToCurrentThread()); 515 DCHECK(main_task_runner_->BelongsToCurrentThread());
354 516
355 switch (message) { 517 switch (message) {
356 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO: 518 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
357 CHECK(SetEvent(process_exit_event_)); 519 CHECK(SetEvent(process_exit_event_));
358 break; 520 break;
359 521
360 case JOB_OBJECT_MSG_NEW_PROCESS: 522 case JOB_OBJECT_MSG_NEW_PROCESS:
361 // We report the exit code of the worker process to be |CONTROL_C_EXIT| 523 // 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 524 // if we cannot get the actual exit code. So here we can safely ignore
363 // the error returned by OpenProcess(). 525 // the error returned by OpenProcess().
364 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid)); 526 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
365 break; 527 break;
366 } 528 }
367 } 529 }
368 530
531 } // namespace
532
533 WtsProcessLauncherImpl::~WtsProcessLauncherImpl() {
534 }
535
536 WtsSessionProcessLauncher::WtsSessionProcessLauncher(
537 const base::Closure& stopped_callback,
538 WtsConsoleMonitor* monitor,
539 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop,
540 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop)
541 : Stoppable(main_message_loop, stopped_callback),
542 attached_(false),
543 main_message_loop_(main_message_loop),
544 ipc_message_loop_(ipc_message_loop),
545 monitor_(monitor) {
546 monitor_->AddWtsConsoleObserver(this);
547
548 // Construct the host binary name.
549 FilePath dir_path;
550 if (!PathService::Get(base::DIR_EXE, &dir_path)) {
551 LOG(ERROR) << "Failed to get the executable file name.";
552 Stop();
553 return;
554 }
555 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
556
557 // The workaround we use to launch a process in a not yet logged in session
558 // (see CreateRemoteSessionProcess()) on Windows XP/2K3 does not let us to
559 // assign the started process to a job. However on Vista+ we have to start
560 // the host via a helper process. The helper process calls ShellExecute() that
561 // does not work across the session boundary but it required to launch
562 // a binary specifying uiAccess='true' in its manifest.
563 //
564 // Below we choose which implementation of |WorkerProcessLauncher::Delegate|
565 // to use. A single process is launched on XP (since uiAccess='true' does not
566 // have effect any way). Vista+ utilizes a helper process and assign it to
567 // a job object to control it.
568 if (base::win::GetVersion() == base::win::VERSION_XP) {
569 impl_ = new SingleProcessLauncher(host_binary, main_message_loop);
570 } else {
571 impl_ = new InJobLauncher(host_binary, main_message_loop, ipc_message_loop);
572 }
573 }
574
575 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
576 // Make sure that the object is completely stopped. The same check exists
577 // in Stoppable::~Stoppable() but this one helps us to fail early and
578 // predictably.
579 CHECK_EQ(stoppable_state(), Stoppable::kStopped);
580
581 monitor_->RemoveWtsConsoleObserver(this);
582
583 CHECK(!attached_);
584 CHECK(!timer_.IsRunning());
585 }
586
587 void WtsSessionProcessLauncher::OnChannelConnected() {
588 DCHECK(main_message_loop_->BelongsToCurrentThread());
589 }
590
591 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
592 DCHECK(main_message_loop_->BelongsToCurrentThread());
593
594 return false;
595 }
596
597 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
598 DCHECK(main_message_loop_->BelongsToCurrentThread());
599
600 if (stoppable_state() != Stoppable::kRunning) {
601 return;
602 }
603
604 DCHECK(!attached_);
605 DCHECK(!timer_.IsRunning());
606
607 attached_ = true;
608
609 // Create a session token and launch the host.
610 base::win::ScopedHandle session_token;
611 if (CreateSessionToken(session_id, &session_token) &&
612 impl_->SetSessionToken(session_token)) {
613 LaunchProcess();
614 }
615 }
616
617 void WtsSessionProcessLauncher::OnSessionDetached() {
618 DCHECK(main_message_loop_->BelongsToCurrentThread());
619 DCHECK(attached_);
620
621 attached_ = false;
622 launch_backoff_ = base::TimeDelta();
623 timer_.Stop();
624
625 if (launcher_.get() != NULL) {
626 launcher_->Stop();
627 }
628 }
629
630 void WtsSessionProcessLauncher::DoStop() {
631 DCHECK(main_message_loop_->BelongsToCurrentThread());
632
633 if (attached_) {
634 OnSessionDetached();
635 }
636
637 // Don't complete shutdown if |launcher_| is not completely stopped.
638 if (launcher_.get() != NULL) {
639 return;
640 }
641
642 // Tear down the core asynchromously.
643 if (impl_.get()) {
644 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
645 impl_ = NULL;
646 }
647
648 CompleteStopping();
649 }
650
369 void WtsSessionProcessLauncher::LaunchProcess() { 651 void WtsSessionProcessLauncher::LaunchProcess() {
370 DCHECK(main_message_loop_->BelongsToCurrentThread()); 652 DCHECK(main_message_loop_->BelongsToCurrentThread());
371 DCHECK(attached_); 653 DCHECK(attached_);
372 DCHECK(launcher_.get() == NULL); 654 DCHECK(launcher_.get() == NULL);
373 DCHECK(!timer_.IsRunning()); 655 DCHECK(!timer_.IsRunning());
374 656
375 launch_time_ = base::Time::Now(); 657 launch_time_ = base::Time::Now();
376 launcher_.reset(new WorkerProcessLauncher( 658 launcher_.reset(new WorkerProcessLauncher(
377 this, this, 659 impl_.get(), this,
378 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, 660 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
379 base::Unretained(this)), 661 base::Unretained(this)),
380 main_message_loop_, 662 main_message_loop_,
381 ipc_message_loop_)); 663 ipc_message_loop_));
382 launcher_->Start(kDaemonIpcSecurityDescriptor); 664 launcher_->Start(kDaemonIpcSecurityDescriptor);
383 } 665 }
384 666
385 void WtsSessionProcessLauncher::OnLauncherStopped() { 667 void WtsSessionProcessLauncher::OnLauncherStopped() {
386 DCHECK(main_message_loop_->BelongsToCurrentThread()); 668 DCHECK(main_message_loop_->BelongsToCurrentThread());
387 669
388 DWORD exit_code = CONTROL_C_EXIT; 670 // Retrieve the exit code of the worker process.
389 if (worker_process_.IsValid()) { 671 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 672
399 launcher_.reset(NULL); 673 launcher_.reset(NULL);
400 674
401 // Do not relaunch the worker process if the caller has asked us to stop. 675 // Do not relaunch the worker process if the caller has asked us to stop.
402 if (stoppable_state() != Stoppable::kRunning) { 676 if (stoppable_state() != Stoppable::kRunning) {
403 Stop(); 677 Stop();
404 return; 678 return;
405 } 679 }
406 680
407 // Stop trying to restart the worker process if its process exited due to 681 // 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)); 701 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
428 } 702 }
429 703
430 // Try to launch the worker process. 704 // Try to launch the worker process.
431 timer_.Start(FROM_HERE, launch_backoff_, 705 timer_.Start(FROM_HERE, launch_backoff_,
432 this, &WtsSessionProcessLauncher::LaunchProcess); 706 this, &WtsSessionProcessLauncher::LaunchProcess);
433 } 707 }
434 } 708 }
435 709
436 } // namespace remoting 710 } // namespace remoting
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698