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> |
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/process_util.h" | 23 #include "base/process_util.h" |
24 #include "base/rand_util.h" | 24 #include "base/rand_util.h" |
25 #include "base/stringprintf.h" | 25 #include "base/stringprintf.h" |
26 #include "base/utf_string_conversions.h" | |
26 #include "base/win/scoped_handle.h" | 27 #include "base/win/scoped_handle.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/sas_injector.h" | 33 #include "remoting/host/sas_injector.h" |
33 #include "remoting/host/win/launch_process_with_token.h" | 34 #include "remoting/host/win/launch_process_with_token.h" |
34 #include "remoting/host/win/wts_console_monitor.h" | 35 #include "remoting/host/win/wts_console_monitor.h" |
35 | 36 |
36 using base::win::ScopedHandle; | 37 using base::win::ScopedHandle; |
37 using base::TimeDelta; | 38 using base::TimeDelta; |
38 | 39 |
39 namespace { | 40 namespace { |
40 | 41 |
41 // 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 |
42 // a session. | 43 // a session. |
43 const int kMaxLaunchDelaySeconds = 60; | 44 const int kMaxLaunchDelaySeconds = 60; |
44 const int kMinLaunchDelaySeconds = 1; | 45 const int kMinLaunchDelaySeconds = 1; |
45 | 46 |
46 const FilePath::CharType kMe2meHostBinaryName[] = | 47 const FilePath::CharType kMe2meHostBinaryName[] = |
47 FILE_PATH_LITERAL("remoting_me2me_host.exe"); | 48 FILE_PATH_LITERAL("remoting_me2me_host.exe"); |
48 | 49 |
49 // Match the pipe name prefix used by Chrome IPC channels. | |
50 const wchar_t kChromePipeNamePrefix[] = L"\\\\.\\pipe\\chrome."; | |
51 | |
52 // The IPC channel name is passed to the host in the command line. | 50 // The IPC channel name is passed to the host in the command line. |
53 const char kChromotingIpcSwitchName[] = "chromoting-ipc"; | 51 const char kChromotingIpcSwitchName[] = "chromoting-ipc"; |
54 | 52 |
55 // The command line parameters that should be copied from the service's command | 53 // The command line parameters that should be copied from the service's command |
56 // line to the host process. | 54 // line to the host process. |
57 const char* kCopiedSwitchNames[] = { | 55 const char* kCopiedSwitchNames[] = { |
58 "auth-config", "host-config", switches::kV, switches::kVModule }; | 56 "auth-config", "host-config", switches::kV, switches::kVModule }; |
59 | 57 |
60 // The security descriptor of the Chromoting IPC channel. It gives full access | 58 // The security descriptor of the Chromoting IPC channel. It gives full access |
61 // to LocalSystem and denies access by anyone else. | 59 // to LocalSystem and denies access by anyone else. |
62 const wchar_t kChromotingChannelSecurityDescriptor[] = | 60 const char kChromotingChannelSecurityDescriptor[] = "O:SYG:SYD:(A;;GA;;;SY)"; |
63 L"O:SYG:SYD:(A;;GA;;;SY)"; | |
64 | |
65 // Generates random channel ID. | |
66 // N.B. Stolen from src/content/common/child_process_host_impl.cc | |
67 std::wstring GenerateRandomChannelId(void* instance) { | |
68 return base::StringPrintf(L"%d.%p.%d", | |
69 base::GetCurrentProcId(), instance, | |
70 base::RandInt(0, std::numeric_limits<int>::max())); | |
71 } | |
72 | |
73 // Creates the server end of the Chromoting IPC channel. | |
74 // N.B. This code is based on IPC::Channel's implementation. | |
75 bool CreatePipeForIpcChannel(void* instance, | |
76 std::wstring* channel_name_out, | |
77 ScopedHandle* pipe_out) { | |
78 // Create security descriptor for the channel. | |
79 SECURITY_ATTRIBUTES security_attributes; | |
80 security_attributes.nLength = sizeof(security_attributes); | |
81 security_attributes.bInheritHandle = FALSE; | |
82 | |
83 ULONG security_descriptor_length = 0; | |
84 if (!ConvertStringSecurityDescriptorToSecurityDescriptorW( | |
85 kChromotingChannelSecurityDescriptor, | |
86 SDDL_REVISION_1, | |
87 reinterpret_cast<PSECURITY_DESCRIPTOR*>( | |
88 &security_attributes.lpSecurityDescriptor), | |
89 &security_descriptor_length)) { | |
90 LOG_GETLASTERROR(ERROR) << | |
91 "Failed to create a security descriptor for the Chromoting IPC channel"; | |
92 return false; | |
93 } | |
94 | |
95 // Generate a random channel name. | |
96 std::wstring channel_name(GenerateRandomChannelId(instance)); | |
97 | |
98 // Convert it to the pipe name. | |
99 std::wstring pipe_name(kChromePipeNamePrefix); | |
100 pipe_name.append(channel_name); | |
101 | |
102 // Create the server end of the pipe. This code should match the code in | |
103 // IPC::Channel with exception of passing a non-default security descriptor. | |
104 HANDLE pipe = CreateNamedPipeW(pipe_name.c_str(), | |
105 PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | | |
106 FILE_FLAG_FIRST_PIPE_INSTANCE, | |
107 PIPE_TYPE_BYTE | PIPE_READMODE_BYTE, | |
108 1, | |
109 IPC::Channel::kReadBufferSize, | |
110 IPC::Channel::kReadBufferSize, | |
111 5000, | |
112 &security_attributes); | |
113 if (pipe == INVALID_HANDLE_VALUE) { | |
114 LOG_GETLASTERROR(ERROR) << | |
115 "Failed to create the server end of the Chromoting IPC channel"; | |
116 LocalFree(security_attributes.lpSecurityDescriptor); | |
117 return false; | |
118 } | |
119 | |
120 LocalFree(security_attributes.lpSecurityDescriptor); | |
121 | |
122 *channel_name_out = channel_name; | |
123 pipe_out->Set(pipe); | |
124 return true; | |
125 } | |
126 | 61 |
127 } // namespace | 62 } // namespace |
128 | 63 |
129 namespace remoting { | 64 namespace remoting { |
130 | 65 |
131 WtsSessionProcessLauncher::WtsSessionProcessLauncher( | 66 WtsSessionProcessLauncher::WtsSessionProcessLauncher( |
132 const base::Closure& stopped_callback, | 67 const base::Closure& stopped_callback, |
133 WtsConsoleMonitor* monitor, | 68 WtsConsoleMonitor* monitor, |
134 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop, | 69 scoped_refptr<base::SingleThreadTaskRunner> main_message_loop, |
135 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop) | 70 scoped_refptr<base::SingleThreadTaskRunner> ipc_message_loop) |
136 : Stoppable(main_message_loop, stopped_callback), | 71 : Stoppable(main_message_loop, stopped_callback), |
72 attached_(false), | |
137 main_message_loop_(main_message_loop), | 73 main_message_loop_(main_message_loop), |
138 ipc_message_loop_(ipc_message_loop), | 74 ipc_message_loop_(ipc_message_loop), |
139 monitor_(monitor), | 75 monitor_(monitor) { |
140 state_(StateDetached) { | |
141 monitor_->AddWtsConsoleObserver(this); | 76 monitor_->AddWtsConsoleObserver(this); |
142 } | 77 } |
143 | 78 |
144 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { | 79 WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { |
145 monitor_->RemoveWtsConsoleObserver(this); | 80 monitor_->RemoveWtsConsoleObserver(this); |
146 if (state_ != StateDetached) { | |
147 OnSessionDetached(); | |
148 } | |
149 | 81 |
150 DCHECK(state_ == StateDetached); | 82 DCHECK(!attached_); |
151 DCHECK(!timer_.IsRunning()); | 83 DCHECK(!timer_.IsRunning()); |
152 DCHECK(process_.handle() == NULL); | |
153 DCHECK(process_watcher_.GetWatchedObject() == NULL); | |
154 DCHECK(chromoting_channel_.get() == NULL); | |
155 } | 84 } |
156 | 85 |
157 void WtsSessionProcessLauncher::LaunchProcess() { | 86 void WtsSessionProcessLauncher::LaunchProcess() { |
158 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 87 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
159 DCHECK(state_ == StateStarting); | 88 DCHECK(attached_); |
89 DCHECK(launcher_.get() == NULL); | |
160 DCHECK(!timer_.IsRunning()); | 90 DCHECK(!timer_.IsRunning()); |
161 DCHECK(process_.handle() == NULL); | 91 DCHECK(!worker_process_.IsValid()); |
162 DCHECK(process_watcher_.GetWatchedObject() == NULL); | |
163 DCHECK(chromoting_channel_.get() == NULL); | |
164 | 92 |
165 launch_time_ = base::Time::Now(); | 93 launch_time_ = base::Time::Now(); |
94 launcher_.reset(new WorkerProcessLauncher( | |
95 base::Bind(&WtsSessionProcessLauncher::OnLauncherStopped, | |
96 base::Unretained(this)), | |
97 main_message_loop_, | |
98 ipc_message_loop_)); | |
99 launcher_->Start(this, kChromotingChannelSecurityDescriptor); | |
100 } | |
101 | |
102 void WtsSessionProcessLauncher::OnLauncherStopped() { | |
103 DCHECK(main_message_loop_->BelongsToCurrentThread()); | |
104 | |
105 DWORD exit_code; | |
106 if (!::GetExitCodeProcess(worker_process_, &exit_code)) { | |
Wez
2012/08/08 20:13:41
nit: Info-log GetLastError(), in case we hit some
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
107 exit_code = CONTROL_C_EXIT; | |
108 } | |
109 | |
110 launcher_.reset(NULL); | |
111 worker_process_.Close(); | |
112 | |
113 // Complete stoppping is shutdown is in progress. | |
Wez
2012/08/08 20:13:41
typo: stopping
typo: is
Wez
2012/08/08 20:13:41
nit: We use "Finish" rather than "Complete" elsewh
Wez
2012/08/08 20:13:41
nit: Reword the comment to clarify _why_ we call C
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
114 if (stoppable_state() != Stoppable::kRunning) { | |
115 CompleteStopping(); | |
116 return; | |
117 } | |
118 | |
119 // Stop trying to restart the host if its process exited due to | |
120 // misconfiguration. | |
121 if (kMinPermanentErrorExitCode <= exit_code && | |
122 exit_code <= kMaxPermanentErrorExitCode) { | |
123 Stop(); | |
124 return; | |
125 } | |
126 | |
127 // Try to restart the process if we are still attached to the session. | |
Wez
2012/08/08 20:13:41
nit: the session -> a session
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
128 if (attached_) { | |
129 // Expand the backoff interval if the process has died quickly or reset it | |
130 // if it was up longer than the maximum backoff delay. | |
131 base::TimeDelta delta = base::Time::Now() - launch_time_; | |
132 if (delta < base::TimeDelta() || | |
133 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) { | |
134 launch_backoff_ = base::TimeDelta(); | |
135 } else { | |
136 launch_backoff_ = std::max( | |
137 launch_backoff_ * 2, TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); | |
138 launch_backoff_ = std::min( | |
139 launch_backoff_, TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); | |
140 } | |
141 | |
142 // Try to restart the host. | |
Wez
2012/08/08 20:13:41
nit: "Try to launch the session process."
alexeypa (please no reviews)
2012/08/08 21:49:58
Done.
| |
143 timer_.Start(FROM_HERE, launch_backoff_, | |
144 this, &WtsSessionProcessLauncher::LaunchProcess); | |
145 } | |
146 } | |
147 | |
148 bool WtsSessionProcessLauncher::DoLaunchProcess(const std::string& channel_name, | |
149 ScopedHandle* wait_out) { | |
150 DCHECK(main_message_loop_->BelongsToCurrentThread()); | |
151 DCHECK(!worker_process_.IsValid()); | |
166 | 152 |
167 // Construct the host binary name. | 153 // Construct the host binary name. |
168 FilePath dir_path; | 154 FilePath dir_path; |
169 if (!PathService::Get(base::DIR_EXE, &dir_path)) { | 155 if (!PathService::Get(base::DIR_EXE, &dir_path)) { |
170 LOG(ERROR) << "Failed to get the executable file name."; | 156 LOG(ERROR) << "Failed to get the executable file name."; |
171 Stop(); | 157 return false; |
172 return; | |
173 } | 158 } |
174 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); | 159 FilePath host_binary = dir_path.Append(kMe2meHostBinaryName); |
175 | 160 |
176 std::wstring channel_name; | 161 // Create the host process command line passing the name of the IPC channel |
177 ScopedHandle pipe; | 162 // to use and copying known switches from the service's command line. |
178 if (CreatePipeForIpcChannel(this, &channel_name, &pipe)) { | 163 CommandLine command_line(host_binary); |
179 // Wrap the pipe into an IPC channel. | 164 command_line.AppendSwitchNative(kChromotingIpcSwitchName, |
180 chromoting_channel_.reset(new IPC::ChannelProxy( | 165 UTF8ToWide(channel_name)); |
181 IPC::ChannelHandle(pipe.Get()), | 166 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), |
182 IPC::Channel::MODE_SERVER, | 167 kCopiedSwitchNames, |
183 this, | 168 _countof(kCopiedSwitchNames)); |
184 ipc_message_loop_)); | |
185 | 169 |
186 // Create the host process command line passing the name of the IPC channel | 170 // Try to launch the process and attach an object watcher to the returned |
187 // to use and copying known switches from the service's command line. | 171 // handle so that we get notified when the process terminates. |
188 CommandLine command_line(host_binary); | 172 if (!LaunchProcessWithToken(host_binary, |
189 command_line.AppendSwitchNative(kChromotingIpcSwitchName, channel_name); | 173 command_line.GetCommandLineString(), |
190 command_line.CopySwitchesFrom(*CommandLine::ForCurrentProcess(), | 174 session_token_, |
191 kCopiedSwitchNames, | 175 &worker_process_)) { |
192 _countof(kCopiedSwitchNames)); | 176 return false; |
193 | |
194 // Try to launch the process and attach an object watcher to the returned | |
195 // handle so that we get notified when the process terminates. | |
196 if (LaunchProcessWithToken(host_binary, | |
197 command_line.GetCommandLineString(), | |
198 session_token_, | |
199 &process_)) { | |
200 if (process_watcher_.StartWatching(process_.handle(), this)) { | |
201 state_ = StateAttached; | |
202 return; | |
203 } else { | |
204 LOG(ERROR) << "Failed to arm the process watcher."; | |
205 process_.Terminate(0); | |
206 process_.Close(); | |
207 } | |
208 } | |
209 | |
210 chromoting_channel_.reset(); | |
211 } | 177 } |
212 | 178 |
213 // Something went wrong. Try to launch the host again later. The attempts rate | 179 ScopedHandle wait; |
214 // is limited by exponential backoff. | 180 if (!DuplicateHandle(GetCurrentProcess(), |
215 launch_backoff_ = std::max(launch_backoff_ * 2, | 181 worker_process_, |
216 TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); | 182 GetCurrentProcess(), |
217 launch_backoff_ = std::min(launch_backoff_, | 183 wait.Receive(), |
218 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); | 184 SYNCHRONIZE, |
219 timer_.Start(FROM_HERE, launch_backoff_, | 185 FALSE, |
220 this, &WtsSessionProcessLauncher::LaunchProcess); | 186 0)) { |
187 LOG_GETLASTERROR(ERROR) << "Failed to duplicate a handle"; | |
188 DoKillProcess(CONTROL_C_EXIT); | |
189 return false; | |
190 } | |
191 | |
192 *wait_out = wait.Pass(); | |
193 return true; | |
221 } | 194 } |
222 | 195 |
223 void WtsSessionProcessLauncher::OnObjectSignaled(HANDLE object) { | 196 void WtsSessionProcessLauncher::DoKillProcess(DWORD exit_code) { |
224 if (!main_message_loop_->BelongsToCurrentThread()) { | 197 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
225 main_message_loop_->PostTask( | 198 |
226 FROM_HERE, base::Bind(&WtsSessionProcessLauncher::OnObjectSignaled, | 199 if (worker_process_.IsValid()) { |
227 base::Unretained(this), object)); | 200 TerminateProcess(worker_process_, exit_code); |
228 return; | |
229 } | 201 } |
202 } | |
230 | 203 |
231 // It is possible that OnObjectSignaled() task will be queued by another | 204 void WtsSessionProcessLauncher::OnChannelConnected( |
232 // thread right before |process_watcher_| was stopped. It such a case it is | 205 base::win::ScopedHandle peer) { |
233 // safe to ignore this notification. | 206 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
234 if (state_ != StateAttached) { | 207 |
235 return; | 208 DWORD client_process_id = GetProcessId(peer); |
209 DWORD expected_process_id = GetProcessId(worker_process_); | |
210 if (client_process_id != expected_process_id) { | |
211 LOG(ERROR) | |
212 << "Unexpected client connected: expected=" << expected_process_id | |
213 << ", actual=" << client_process_id; | |
214 Stop(); | |
236 } | 215 } |
237 | |
238 DCHECK(!timer_.IsRunning()); | |
239 DCHECK(process_.handle() != NULL); | |
240 DCHECK(process_watcher_.GetWatchedObject() == NULL); | |
241 DCHECK(chromoting_channel_.get() != NULL); | |
242 | |
243 // Stop trying to restart the host if its process exited due to | |
244 // misconfiguration. | |
245 int exit_code; | |
246 bool stop_trying = | |
247 base::WaitForExitCodeWithTimeout( | |
248 process_.handle(), &exit_code, base::TimeDelta()) && | |
249 kMinPermanentErrorExitCode <= exit_code && | |
250 exit_code <= kMaxPermanentErrorExitCode; | |
251 | |
252 // The host process has been terminated for some reason. The handle can now be | |
253 // closed. | |
254 process_.Close(); | |
255 chromoting_channel_.reset(); | |
256 state_ = StateStarting; | |
257 | |
258 if (stop_trying) { | |
259 Stop(); | |
260 return; | |
261 } | |
262 | |
263 // Expand the backoff interval if the process has died quickly or reset it if | |
264 // it was up longer than the maximum backoff delay. | |
265 base::TimeDelta delta = base::Time::Now() - launch_time_; | |
266 if (delta < base::TimeDelta() || | |
267 delta >= base::TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)) { | |
268 launch_backoff_ = base::TimeDelta(); | |
269 } else { | |
270 launch_backoff_ = std::max(launch_backoff_ * 2, | |
271 TimeDelta::FromSeconds(kMinLaunchDelaySeconds)); | |
272 launch_backoff_ = std::min(launch_backoff_, | |
273 TimeDelta::FromSeconds(kMaxLaunchDelaySeconds)); | |
274 } | |
275 | |
276 // Try to restart the host. | |
277 timer_.Start(FROM_HERE, launch_backoff_, | |
278 this, &WtsSessionProcessLauncher::LaunchProcess); | |
279 } | 216 } |
280 | 217 |
281 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { | 218 bool WtsSessionProcessLauncher::OnMessageReceived(const IPC::Message& message) { |
219 DCHECK(main_message_loop_->BelongsToCurrentThread()); | |
220 | |
282 bool handled = true; | 221 bool handled = true; |
283 IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message) | 222 IPC_BEGIN_MESSAGE_MAP(WtsSessionProcessLauncher, message) |
284 IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole, | 223 IPC_MESSAGE_HANDLER(ChromotingHostMsg_SendSasToConsole, |
285 OnSendSasToConsole) | 224 OnSendSasToConsole) |
286 IPC_MESSAGE_UNHANDLED(handled = false) | 225 IPC_MESSAGE_UNHANDLED(handled = false) |
287 IPC_END_MESSAGE_MAP() | 226 IPC_END_MESSAGE_MAP() |
288 return handled; | 227 return handled; |
289 } | 228 } |
290 | 229 |
291 void WtsSessionProcessLauncher::OnSendSasToConsole() { | 230 void WtsSessionProcessLauncher::OnSendSasToConsole() { |
292 if (!main_message_loop_->BelongsToCurrentThread()) { | 231 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
293 main_message_loop_->PostTask( | |
294 FROM_HERE, base::Bind(&WtsSessionProcessLauncher::OnSendSasToConsole, | |
295 base::Unretained(this))); | |
296 return; | |
297 } | |
298 | 232 |
299 if (state_ == StateAttached) { | 233 if (attached_) { |
300 if (sas_injector_.get() == NULL) { | 234 if (sas_injector_.get() == NULL) { |
301 sas_injector_ = SasInjector::Create(); | 235 sas_injector_ = SasInjector::Create(); |
302 } | 236 } |
303 | 237 |
304 if (sas_injector_.get() != NULL) { | 238 if (sas_injector_.get() != NULL) { |
305 sas_injector_->InjectSas(); | 239 sas_injector_->InjectSas(); |
306 } | 240 } |
307 } | 241 } |
308 } | 242 } |
309 | 243 |
310 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { | 244 void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { |
311 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 245 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
312 | 246 |
313 if (stoppable_state() != Stoppable::kRunning) { | 247 if (stoppable_state() != Stoppable::kRunning) { |
314 return; | 248 return; |
315 } | 249 } |
316 | 250 |
317 DCHECK(state_ == StateDetached); | 251 DCHECK(!attached_); |
318 DCHECK(!timer_.IsRunning()); | 252 DCHECK(!timer_.IsRunning()); |
319 DCHECK(process_.handle() == NULL); | 253 |
320 DCHECK(process_watcher_.GetWatchedObject() == NULL); | 254 attached_ = true; |
321 DCHECK(chromoting_channel_.get() == NULL); | |
322 | 255 |
323 // Create a session token for the launched process. | 256 // Create a session token for the launched process. |
324 if (!CreateSessionToken(session_id, &session_token_)) | 257 if (!CreateSessionToken(session_id, &session_token_)) |
325 return; | 258 return; |
326 | 259 |
327 // Now try to launch the host. | 260 // Now try to launch the host. |
328 state_ = StateStarting; | |
329 LaunchProcess(); | 261 LaunchProcess(); |
330 } | 262 } |
331 | 263 |
332 void WtsSessionProcessLauncher::OnSessionDetached() { | 264 void WtsSessionProcessLauncher::OnSessionDetached() { |
333 DCHECK(main_message_loop_->BelongsToCurrentThread()); | 265 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
334 DCHECK(state_ == StateDetached || | 266 DCHECK(attached_); |
335 state_ == StateStarting || | |
336 state_ == StateAttached); | |
337 | 267 |
338 switch (state_) { | 268 attached_ = false; |
339 case StateDetached: | 269 launch_backoff_ = base::TimeDelta(); |
340 DCHECK(!timer_.IsRunning()); | 270 session_token_.Close(); |
341 DCHECK(process_.handle() == NULL); | 271 timer_.Stop(); |
342 DCHECK(process_watcher_.GetWatchedObject() == NULL); | |
343 DCHECK(chromoting_channel_.get() == NULL); | |
344 break; | |
345 | 272 |
346 case StateStarting: | 273 if (launcher_.get() != NULL) { |
347 DCHECK(process_.handle() == NULL); | 274 launcher_->Stop(); |
348 DCHECK(process_watcher_.GetWatchedObject() == NULL); | |
349 DCHECK(chromoting_channel_.get() == NULL); | |
350 | |
351 timer_.Stop(); | |
352 launch_backoff_ = base::TimeDelta(); | |
353 state_ = StateDetached; | |
354 break; | |
355 | |
356 case StateAttached: | |
357 DCHECK(!timer_.IsRunning()); | |
358 DCHECK(process_.handle() != NULL); | |
359 DCHECK(process_watcher_.GetWatchedObject() != NULL); | |
360 DCHECK(chromoting_channel_.get() != NULL); | |
361 | |
362 process_watcher_.StopWatching(); | |
363 process_.Terminate(0); | |
364 process_.Close(); | |
365 chromoting_channel_.reset(); | |
366 state_ = StateDetached; | |
367 break; | |
368 } | 275 } |
369 | |
370 session_token_.Close(); | |
371 } | 276 } |
372 | 277 |
373 void WtsSessionProcessLauncher::DoStop() { | 278 void WtsSessionProcessLauncher::DoStop() { |
374 if (state_ != StateDetached) { | 279 DCHECK(main_message_loop_->BelongsToCurrentThread()); |
280 | |
281 if (attached_) { | |
375 OnSessionDetached(); | 282 OnSessionDetached(); |
376 } | 283 } |
377 | 284 |
378 CompleteStopping(); | 285 if (launcher_.get() == NULL) { |
286 CompleteStopping(); | |
287 } | |
379 } | 288 } |
380 | 289 |
381 } // namespace remoting | 290 } // namespace remoting |
OLD | NEW |