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

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

Issue 10831271: [Chromoting] Adding uiAccess='true' to the remoting_me2me_host.exe's manifest as it is required to … (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: rebased + a couple of merge fixes. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « remoting/host/win/wts_session_process_launcher.h ('k') | remoting/remoting.gyp » ('j') | 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>
(...skipping 29 matching lines...) Expand all
40 namespace { 40 namespace {
41 41
42 // The minimum and maximum delays between attempts to inject host process into 42 // The minimum and maximum delays between attempts to inject host process into
43 // a session. 43 // a session.
44 const int kMaxLaunchDelaySeconds = 60; 44 const int kMaxLaunchDelaySeconds = 60;
45 const int kMinLaunchDelaySeconds = 1; 45 const int kMinLaunchDelaySeconds = 1;
46 46
47 const FilePath::CharType kMe2meHostBinaryName[] = 47 const FilePath::CharType kMe2meHostBinaryName[] =
48 FILE_PATH_LITERAL("remoting_me2me_host.exe"); 48 FILE_PATH_LITERAL("remoting_me2me_host.exe");
49 49
50 const FilePath::CharType kMe2meServiceBinaryName[] =
51 FILE_PATH_LITERAL("remoting_service.exe");
52
50 // The IPC channel name is passed to the host in the command line. 53 // The IPC channel name is passed to the host in the command line.
51 const char kChromotingIpcSwitchName[] = "chromoting-ipc"; 54 const char kChromotingIpcSwitchName[] = "chromoting-ipc";
52 55
56 const char kElevateSwitchName[] = "elevate";
57
53 // The command line parameters that should be copied from the service's command 58 // The command line parameters that should be copied from the service's command
54 // line to the host process. 59 // line to the host process.
55 const char* kCopiedSwitchNames[] = { 60 const char* kCopiedSwitchNames[] = {
56 "auth-config", "host-config", switches::kV, switches::kVModule }; 61 "auth-config", "host-config", switches::kV, switches::kVModule };
57 62
58 // The security descriptor of the Chromoting IPC channel. It gives full access 63 // The security descriptor of the Chromoting IPC channel. It gives full access
59 // to LocalSystem and denies access by anyone else. 64 // to LocalSystem and denies access by anyone else.
60 const char kChromotingChannelSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; 65 const char kChromotingChannelSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)";
61 66
62 } // namespace 67 } // namespace
63 68
64 namespace remoting { 69 namespace remoting {
65 70
66 WtsSessionProcessLauncher::WtsSessionProcessLauncher( 71 WtsSessionProcessLauncher::WtsSessionProcessLauncher(
67 const base::Closure& stopped_callback, 72 const base::Closure& stopped_callback,
68 WtsConsoleMonitor* monitor, 73 WtsConsoleMonitor* monitor,
69 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop, 74 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop,
70 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop) 75 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop)
71 : Stoppable(main_message_loop, stopped_callback), 76 : Stoppable(main_message_loop, stopped_callback),
72 attached_(false), 77 attached_(false),
73 main_message_loop_(main_message_loop), 78 main_message_loop_(main_message_loop),
74 ipc_message_loop_(ipc_message_loop), 79 ipc_message_loop_(ipc_message_loop),
75 monitor_(monitor) { 80 monitor_(monitor),
81 job_state_(kJobUninitialized) {
76 monitor_->AddWtsConsoleObserver(this); 82 monitor_->AddWtsConsoleObserver(this);
83
84 process_exit_event_.Set(CreateEvent(NULL, TRUE, FALSE, NULL));
85 CHECK(process_exit_event_.IsValid());
86
87 // To receive job object notifications it is registered with the completion
88 // port represented by |ipc_message_loop|. The registration has to be done on
89 // the I/O thread because MessageLoopForIO::RegisterJobObject() can only be
90 // called via MessageLoopForIO::current().
91 ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
92 &WtsSessionProcessLauncher::InitializeJob,
93 base::Unretained(this)));
77 } 94 }
78 95
79 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { 96 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() {
97 // Make sure that the object is completely stopped. The same check exists
98 // in Stoppable::~Stoppable() but this one allows us to examine the state of
99 // the object before destruction.
100 CHECK_EQ(stoppable_state(), Stoppable::kStopped);
101
80 monitor_->RemoveWtsConsoleObserver(this); 102 monitor_->RemoveWtsConsoleObserver(this);
81 103
82 DCHECK(!attached_); 104 CHECK(!attached_);
83 DCHECK(!timer_.IsRunning()); 105 CHECK(!timer_.IsRunning());
84 } 106 }
85 107
86 void WtsSessionProcessLauncher::LaunchProcess() { 108 void WtsSessionProcessLauncher::OnIOCompleted(
87 DCHECK(main_message_loop_->BelongsToCurrentThread()); 109 base::MessagePumpForIO::IOContext* context,
88 DCHECK(attached_); 110 DWORD bytes_transferred,
89 DCHECK(launcher_.get() == NULL); 111 DWORD error) {
90 DCHECK(!timer_.IsRunning()); 112 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
91 DCHECK(!worker_process_.IsValid());
92 113
93 launch_time_ = base::Time::Now(); 114 // |bytes_transferred| is used in job object notifications to supply
94 launcher_.reset(new WorkerProcessLauncher( 115 // the message ID; |context| carries process ID.
95 this, 116 main_message_loop_->PostTask(FROM_HERE, base::Bind(
96 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, 117 &WtsSessionProcessLauncher::OnJobNotification,
97 base::Unretained(this)), 118 base::Unretained(this), bytes_transferred,
98 main_message_loop_, 119 reinterpret_cast<DWORD>(context)));
99 ipc_message_loop_));
100 launcher_->Start(kChromotingChannelSecurityDescriptor);
101 }
102
103 void WtsSessionProcessLauncher::OnLauncherStopped() {
104 DCHECK(main_message_loop_->BelongsToCurrentThread());
105
106 DWORD exit_code;
107 if (!::GetExitCodeProcess(worker_process_, &exit_code)) {
108 LOG_GETLASTERROR(INFO)
109 << "Failed to query the exit code of the worker process";
110 exit_code = CONTROL_C_EXIT;
111 }
112
113 launcher_.reset(NULL);
114 worker_process_.Close();
115
116 // Do not relaunch the worker process if the caller has asked us to stop.
117 if (stoppable_state() != Stoppable::kRunning) {
118 CompleteStopping();
119 return;
120 }
121
122 // Stop trying to restart the worker process if its process exited due to
123 // misconfiguration.
124 if (kMinPermanentErrorExitCode <= exit_code &&
125 exit_code <= kMaxPermanentErrorExitCode) {
126 Stop();
127 return;
128 }
129
130 // Try to restart the worker process if we are still attached to a session.
131 if (attached_) {
132 // Expand the backoff interval if the process has died quickly or reset it
133 // if it was up longer than the maximum backoff delay.
134 base::TimeDelta delta = base::Time::Now() - launch_time_;
135 if (delta < base::TimeDelta() ||
136 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) {
137 launch_backoff_ = base::TimeDelta();
138 } else {
139 launch_backoff_ = std::max(
140 launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
141 launch_backoff_ = std::min(
142 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
143 }
144
145 // Try to launch the worker process.
146 timer_.Start(FROM_HERE, launch_backoff_,
147 this, &WtsSessionProcessLauncher::LaunchProcess);
148 }
149 } 120 }
150 121
151 bool WtsSessionProcessLauncher::DoLaunchProcess( 122 bool WtsSessionProcessLauncher::DoLaunchProcess(
152 const std::string& channel_name, 123 const std::string& channel_name,
153 ScopedHandle* process_exit_event_out) { 124 ScopedHandle* process_exit_event_out) {
154 DCHECK(main_message_loop_->BelongsToCurrentThread()); 125 DCHECK(main_message_loop_->BelongsToCurrentThread());
155 DCHECK(!worker_process_.IsValid()); 126
127 // The job object is not ready. Retry starting the host process later.
128 if (!job_.IsValid()) {
129 return false;
130 }
156 131
157 // Construct the host binary name. 132 // Construct the host binary name.
158 FilePath dir_path; 133 FilePath dir_path;
159 if (!PathService::Get(base::DIR_EXE, &dir_path)) { 134 if (!PathService::Get(base::DIR_EXE, &dir_path)) {
160 LOG(ERROR) << "Failed to get the executable file name."; 135 LOG(ERROR) << "Failed to get the executable file name.";
161 return false; 136 return false;
162 } 137 }
163 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); 138 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName);
139 FilePath service_binary = dir_path.Append(kMe2meServiceBinaryName);
164 140
165 // Create the host process command line passing the name of the IPC channel 141 // Create the host process command line passing the name of the IPC channel
166 // to use and copying known switches from the service's command line. 142 // to use and copying known switches from the service's command line.
167 CommandLine command_line(host_binary); 143 CommandLine command_line(service_binary);
144 command_line.AppendSwitchPath(kElevateSwitchName, host_binary);
168 command_line.AppendSwitchNative(kChromotingIpcSwitchName, 145 command_line.AppendSwitchNative(kChromotingIpcSwitchName,
169 UTF8ToWide(channel_name)); 146 UTF8ToWide(channel_name));
170 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), 147 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(),
171 kCopiedSwitchNames, 148 kCopiedSwitchNames,
172 _countof(kCopiedSwitchNames)); 149 _countof(kCopiedSwitchNames));
173 150
151 CHECK(ResetEvent(process_exit_event_));
152
174 // Try to launch the process and attach an object watcher to the returned 153 // Try to launch the process and attach an object watcher to the returned
175 // handle so that we get notified when the process terminates. 154 // handle so that we get notified when the process terminates.
176 if (!LaunchProcessWithToken(host_binary, 155 base::win::ScopedHandle worker_process;
156 base::win::ScopedHandle worker_thread;
157 if (!LaunchProcessWithToken(service_binary,
177 command_line.GetCommandLineString(), 158 command_line.GetCommandLineString(),
178 session_token_, 159 session_token_,
179 &worker_process_)) { 160 CREATE_SUSPENDED,
161 &worker_process,
162 &worker_thread)) {
163 return false;
164 }
165
166 if (!AssignProcessToJobObject(job_, worker_process)) {
167 LOG_GETLASTERROR(ERROR) << "Failed to assign the worker to the job object";
168 TerminateProcess(worker_process, CONTROL_C_EXIT);
169 return false;
170 }
171
172 if (!ResumeThread(worker_thread)) {
173 LOG_GETLASTERROR(ERROR) << "Failed to resume the worker thread";
174 DoKillProcess(CONTROL_C_EXIT);
180 return false; 175 return false;
181 } 176 }
182 177
183 ScopedHandle process_exit_event; 178 ScopedHandle process_exit_event;
184 if (!DuplicateHandle(GetCurrentProcess(), 179 if (!DuplicateHandle(GetCurrentProcess(),
185 worker_process_, 180 process_exit_event_,
186 GetCurrentProcess(), 181 GetCurrentProcess(),
187 process_exit_event.Receive(), 182 process_exit_event.Receive(),
188 SYNCHRONIZE, 183 SYNCHRONIZE,
189 FALSE, 184 FALSE,
190 0)) { 185 0)) {
191 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; 186 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle";
192 DoKillProcess(CONTROL_C_EXIT); 187 DoKillProcess(CONTROL_C_EXIT);
193 return false; 188 return false;
194 } 189 }
195 190
196 *process_exit_event_out = process_exit_event.Pass(); 191 *process_exit_event_out = process_exit_event.Pass();
197 return true; 192 return true;
198 } 193 }
199 194
200 void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) { 195 void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) {
201 DCHECK(main_message_loop_->BelongsToCurrentThread()); 196 DCHECK(main_message_loop_->BelongsToCurrentThread());
202 197
203 if (worker_process_.IsValid()) { 198 if (job_.IsValid()) {
204 TerminateProcess(worker_process_, exit_code); 199 TerminateJobObject(job_, exit_code);
205 } 200 }
206 } 201 }
207 202
208 void WtsSessionProcessLauncher::OnChannelConnected() { 203 void WtsSessionProcessLauncher::OnChannelConnected() {
209 DCHECK(main_message_loop_->BelongsToCurrentThread()); 204 DCHECK(main_message_loop_->BelongsToCurrentThread());
210 } 205 }
211 206
212 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { 207 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) {
213 DCHECK(main_message_loop_->BelongsToCurrentThread()); 208 DCHECK(main_message_loop_->BelongsToCurrentThread());
214 209
215 bool handled = true; 210 bool handled = true;
216 IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message) 211 IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message)
217 IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole, 212 IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole,
218 OnSendSasToConsole) 213 OnSendSasToConsole)
219 IPC_MESSAGE_UNHANDLED(handled = false) 214 IPC_MESSAGE_UNHANDLED(handled = false)
220 IPC_END_MESSAGE_MAP() 215 IPC_END_MESSAGE_MAP()
221 return handled; 216 return handled;
222 } 217 }
223 218
224 void WtsSessionProcessLauncher::OnSendSasToConsole() {
225 DCHECK(main_message_loop_->BelongsToCurrentThread());
226
227 if (attached_) {
228 if (sas_injector_.get() == NULL) {
229 sas_injector_ = SasInjector::Create();
230 }
231
232 if (sas_injector_.get() != NULL) {
233 sas_injector_->InjectSas();
234 }
235 }
236 }
237
238 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { 219 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) {
239 DCHECK(main_message_loop_->BelongsToCurrentThread()); 220 DCHECK(main_message_loop_->BelongsToCurrentThread());
240 221
241 if (stoppable_state() != Stoppable::kRunning) { 222 if (stoppable_state() != Stoppable::kRunning) {
242 return; 223 return;
243 } 224 }
244 225
245 DCHECK(!attached_); 226 DCHECK(!attached_);
246 DCHECK(!timer_.IsRunning()); 227 DCHECK(!timer_.IsRunning());
247 228
(...skipping 21 matching lines...) Expand all
269 } 250 }
270 } 251 }
271 252
272 void WtsSessionProcessLauncher::DoStop() { 253 void WtsSessionProcessLauncher::DoStop() {
273 DCHECK(main_message_loop_->BelongsToCurrentThread()); 254 DCHECK(main_message_loop_->BelongsToCurrentThread());
274 255
275 if (attached_) { 256 if (attached_) {
276 OnSessionDetached(); 257 OnSessionDetached();
277 } 258 }
278 259
279 if (launcher_.get() == NULL) { 260 job_.Close();
280 CompleteStopping(); 261
281 } 262 // Drain the completion queue to make sure all job object notification have
282 } 263 // been received.
283 264 if (job_state_ == kJobRunning) {
265 job_state_ = kJobStopping;
266 ipc_message_loop_->PostTask(FROM_HERE, base::Bind(
267 &WtsSessionProcessLauncher::DrainJobNotifications,
268 base::Unretained(this)));
269 }
270
271 // Don't complete shutdown if |launcher_| is not completely stopped.
272 if (launcher_.get() != NULL) {
273 return;
274 }
275
276 // Don't complete shutdown if the completion queue hasn't been drained.
277 if (job_state_ != kJobUninitialized && job_state_ != kJobStopped) {
278 return;
279 }
280
281 CompleteStopping();
282 }
283
284 void WtsSessionProcessLauncher::DrainJobNotifications() {
285 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
286
287 // DrainJobNotifications() is posted after the job object is destroyed, so
288 // by this time all notifications from the job object have been processed
289 // already. Let the main thread know that the queue has been drained.
290 main_message_loop_->PostTask(FROM_HERE, base::Bind(
291 &WtsSessionProcessLauncher::DrainJobNotificationsCompleted,
292 base::Unretained(this)));
293 }
294
295 void WtsSessionProcessLauncher::DrainJobNotificationsCompleted() {
296 DCHECK(main_message_loop_->BelongsToCurrentThread());
297 DCHECK_EQ(job_state_, kJobStopping);
298
299 job_state_ = kJobStopped;
300 Stop();
301 }
302
303 void WtsSessionProcessLauncher::InitializeJob() {
304 DCHECK(ipc_message_loop_->BelongsToCurrentThread());
305
306 ScopedHandle job;
307 job.Set(CreateJobObject(NULL, NULL));
308 if (!job.IsValid()) {
309 LOG_GETLASTERROR(ERROR) << "Failed to create a job object";
310 return;
311 }
312
313 // Limit the number of active processes in the job to two (the process
314 // performing elevation and the host) and make sure that all processes will be
315 // killed once the job object is destroyed.
316 JOBOBJECT_EXTENDED_LIMIT_INFORMATION info;
317 memset(&info, 0, sizeof(info));
318 info.BasicLimitInformation.LimitFlags = JOB_OBJECT_LIMIT_ACTIVE_PROCESS |
319 JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE;
320 info.BasicLimitInformation.ActiveProcessLimit = 2;
321 if (!SetInformationJobObject(job,
322 JobObjectExtendedLimitInformation,
323 &info,
324 sizeof(info))) {
325 LOG_GETLASTERROR(ERROR) << "Failed to set limits on the job object";
326 return;
327 }
328
329 // Register the job object with the completion port in the I/O thread to
330 // receive job notifications.
331 if (!MessageLoopForIO::current()->RegisterJobObject(job, this)) {
332 LOG_GETLASTERROR(ERROR)
333 << "Failed to associate the job object with a completion port";
334 return;
335 }
336
337 // ScopedHandle is not compatible with base::Passed, so we wrap it to a scoped
338 // pointer.
339 scoped_ptr<ScopedHandle> job_wrapper(new ScopedHandle());
340 *job_wrapper = job.Pass();
341
342 // Let the main thread know that initialization is complete.
343 main_message_loop_->PostTask(FROM_HERE, base::Bind(
344 &WtsSessionProcessLauncher::InitializeJobCompleted,
345 base::Unretained(this), base::Passed(&job_wrapper)));
346 }
347
348 void WtsSessionProcessLauncher::InitializeJobCompleted(
349 scoped_ptr<ScopedHandle> job) {
350 DCHECK(main_message_loop_->BelongsToCurrentThread());
351 DCHECK(!job_.IsValid());
352 DCHECK_EQ(job_state_, kJobUninitialized);
353
354 if (stoppable_state() == Stoppable::kRunning) {
355 job_ = job->Pass();
356 job_state_ = kJobRunning;
357 }
358 }
359
360 void WtsSessionProcessLauncher::OnJobNotification(DWORD message, DWORD pid) {
361 DCHECK(main_message_loop_->BelongsToCurrentThread());
362
363 switch (message) {
364 case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
365 CHECK(SetEvent(process_exit_event_));
366 break;
367
368 case JOB_OBJECT_MSG_NEW_PROCESS:
369 // We report the exit code of the worker process to be |CONTROL_C_EXIT|
370 // if we cannot get the actual exit code. So here we can safely ignore
371 // the error returned by OpenProcess().
372 worker_process_.Set(OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, pid));
373 break;
374 }
375 }
376
377 void WtsSessionProcessLauncher::LaunchProcess() {
378 DCHECK(main_message_loop_->BelongsToCurrentThread());
379 DCHECK(attached_);
380 DCHECK(launcher_.get() == NULL);
381 DCHECK(!timer_.IsRunning());
382
383 launch_time_ = base::Time::Now();
384 launcher_.reset(new WorkerProcessLauncher(
385 this,
386 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped,
387 base::Unretained(this)),
388 main_message_loop_,
389 ipc_message_loop_));
390 launcher_->Start(kChromotingChannelSecurityDescriptor);
391 }
392
393 void WtsSessionProcessLauncher::OnLauncherStopped() {
394 DCHECK(main_message_loop_->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 launcher_.reset(NULL);
408
409 // Do not relaunch the worker process if the caller has asked us to stop.
410 if (stoppable_state() != Stoppable::kRunning) {
411 Stop();
412 return;
413 }
414
415 // Stop trying to restart the worker process if its process exited due to
416 // misconfiguration.
417 if (kMinPermanentErrorExitCode <= exit_code &&
418 exit_code <= kMaxPermanentErrorExitCode) {
419 Stop();
420 return;
421 }
422
423 // Try to restart the worker process if we are still attached to a session.
424 if (attached_) {
425 // Expand the backoff interval if the process has died quickly or reset it
426 // if it was up longer than the maximum backoff delay.
427 base::TimeDelta delta = base::Time::Now() - launch_time_;
428 if (delta < base::TimeDelta() ||
429 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) {
430 launch_backoff_ = base::TimeDelta();
431 } else {
432 launch_backoff_ = std::max(
433 launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds));
434 launch_backoff_ = std::min(
435 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds));
436 }
437
438 // Try to launch the worker process.
439 timer_.Start(FROM_HERE, launch_backoff_,
440 this, &WtsSessionProcessLauncher::LaunchProcess);
441 }
442 }
443
444 void WtsSessionProcessLauncher::OnSendSasToConsole() {
445 DCHECK(main_message_loop_->BelongsToCurrentThread());
446
447 if (attached_) {
448 if (sas_injector_.get() == NULL) {
449 sas_injector_ = SasInjector::Create();
450 }
451
452 if (sas_injector_.get() != NULL) {
453 sas_injector_->InjectSas();
454 }
455 }
456 }
457
284 } // namespace remoting 458 } // namespace remoting
OLDNEW
« no previous file with comments | « remoting/host/win/wts_session_process_launcher.h ('k') | remoting/remoting.gyp » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698