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

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

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

Powered by Google App Engine
This is Rietveld 408576698