OLD | NEW |
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 Loading... |
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 Loading... |
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 |
OLD | NEW |