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/host_service.h" | 8 #include "remoting/host/win/host_service.h" |
9 | 9 |
10 #include <windows.h> | 10 #include <windows.h> |
11 #include <wtsapi32.h> | 11 #include <wtsapi32.h> |
12 #include <stdio.h> | 12 #include <stdio.h> |
13 | 13 |
14 #include "base/at_exit.h" | 14 #include "base/at_exit.h" |
15 #include "base/base_paths.h" | 15 #include "base/base_paths.h" |
16 #include "base/bind.h" | 16 #include "base/bind.h" |
17 #include "base/command_line.h" | 17 #include "base/command_line.h" |
18 #include "base/file_util.h" | 18 #include "base/file_util.h" |
19 #include "base/logging.h" | 19 #include "base/logging.h" |
20 #include "base/message_loop.h" | 20 #include "base/message_loop.h" |
21 #include "base/path_service.h" | 21 #include "base/path_service.h" |
| 22 #include "base/single_thread_task_runner.h" |
22 #include "base/stringprintf.h" | 23 #include "base/stringprintf.h" |
23 #include "base/threading/thread.h" | 24 #include "base/threading/thread.h" |
24 #include "base/utf_string_conversions.h" | 25 #include "base/utf_string_conversions.h" |
25 #include "base/win/wrapped_window_proc.h" | 26 #include "base/win/wrapped_window_proc.h" |
26 #include "remoting/base/breakpad.h" | 27 #include "remoting/base/breakpad.h" |
27 #include "remoting/base/scoped_sc_handle_win.h" | 28 #include "remoting/base/scoped_sc_handle_win.h" |
| 29 #include "remoting/base/stoppable.h" |
28 #include "remoting/host/branding.h" | 30 #include "remoting/host/branding.h" |
29 #include "remoting/host/usage_stats_consent.h" | 31 #include "remoting/host/usage_stats_consent.h" |
30 #include "remoting/host/win/host_service_resource.h" | 32 #include "remoting/host/win/host_service_resource.h" |
31 #include "remoting/host/win/wts_console_observer.h" | 33 #include "remoting/host/win/wts_console_observer.h" |
32 #include "remoting/host/win/wts_session_process_launcher.h" | 34 #include "remoting/host/win/wts_session_process_launcher.h" |
33 | 35 |
34 using base::StringPrintf; | 36 using base::StringPrintf; |
35 | 37 |
36 namespace { | 38 namespace { |
37 | 39 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
75 void usage(const char* program_name) { | 77 void usage(const char* program_name) { |
76 fprintf(stderr, kUsageMessage, program_name); | 78 fprintf(stderr, kUsageMessage, program_name); |
77 } | 79 } |
78 | 80 |
79 } // namespace | 81 } // namespace |
80 | 82 |
81 namespace remoting { | 83 namespace remoting { |
82 | 84 |
83 HostService::HostService() : | 85 HostService::HostService() : |
84 console_session_id_(kInvalidSessionId), | 86 console_session_id_(kInvalidSessionId), |
85 message_loop_(NULL), | |
86 run_routine_(&HostService::RunAsService), | 87 run_routine_(&HostService::RunAsService), |
87 service_name_(kWindowsServiceName), | 88 service_name_(kWindowsServiceName), |
88 service_status_handle_(0), | 89 service_status_handle_(0), |
89 shutting_down_(false), | |
90 stopped_event_(true, false) { | 90 stopped_event_(true, false) { |
91 } | 91 } |
92 | 92 |
93 HostService::~HostService() { | 93 HostService::~HostService() { |
94 } | 94 } |
95 | 95 |
96 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { | 96 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { |
97 DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread()); | 97 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
98 | 98 |
99 console_observers_.AddObserver(observer); | 99 console_observers_.AddObserver(observer); |
100 } | 100 } |
101 | 101 |
102 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { | 102 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { |
103 DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread()); | 103 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
104 | 104 |
105 console_observers_.RemoveObserver(observer); | 105 console_observers_.RemoveObserver(observer); |
| 106 } |
106 | 107 |
107 // Stop the service if there are no more observers. | 108 void HostService::OnLauncherShutdown() { |
108 if (!console_observers_.might_have_observers()) { | 109 launcher_.reset(NULL); |
109 message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 110 main_task_runner_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
110 } | |
111 } | 111 } |
112 | 112 |
113 void HostService::OnSessionChange() { | 113 void HostService::OnSessionChange() { |
114 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads | 114 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads |
115 // a single value from shared memory. Therefore it is better to check if | 115 // a single value from shared memory. Therefore it is better to check if |
116 // the console session is still the same every time a session change | 116 // the console session is still the same every time a session change |
117 // notification event is posted. This also takes care of coalescing multiple | 117 // notification event is posted. This also takes care of coalescing multiple |
118 // events into one since we look at the latest state. | 118 // events into one since we look at the latest state. |
119 uint32 console_session_id = kInvalidSessionId; | 119 uint32 console_session_id = WTSGetActiveConsoleSessionId(); |
120 if (!shutting_down_) { | |
121 console_session_id = WTSGetActiveConsoleSessionId(); | |
122 } | |
123 if (console_session_id_ != console_session_id) { | 120 if (console_session_id_ != console_session_id) { |
124 if (console_session_id_ != kInvalidSessionId) { | 121 if (console_session_id_ != kInvalidSessionId) { |
125 FOR_EACH_OBSERVER(WtsConsoleObserver, | 122 FOR_EACH_OBSERVER(WtsConsoleObserver, |
126 console_observers_, | 123 console_observers_, |
127 OnSessionDetached()); | 124 OnSessionDetached()); |
128 } | 125 } |
129 | 126 |
130 console_session_id_ = console_session_id; | 127 console_session_id_ = console_session_id; |
131 | 128 |
132 if (console_session_id_ != kInvalidSessionId) { | 129 if (console_session_id_ != kInvalidSessionId) { |
133 FOR_EACH_OBSERVER(WtsConsoleObserver, | 130 FOR_EACH_OBSERVER(WtsConsoleObserver, |
134 console_observers_, | 131 console_observers_, |
135 OnSessionAttached(console_session_id_)); | 132 OnSessionAttached(console_session_id_)); |
136 } | 133 } |
137 } | 134 } |
138 } | 135 } |
139 | 136 |
140 BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) { | 137 BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) { |
141 HostService* self = HostService::GetInstance(); | 138 HostService* self = HostService::GetInstance(); |
142 switch (event) { | 139 switch (event) { |
143 case CTRL_C_EVENT: | 140 case CTRL_C_EVENT: |
144 case CTRL_BREAK_EVENT: | 141 case CTRL_BREAK_EVENT: |
145 case CTRL_CLOSE_EVENT: | 142 case CTRL_CLOSE_EVENT: |
146 case CTRL_LOGOFF_EVENT: | 143 case CTRL_LOGOFF_EVENT: |
147 case CTRL_SHUTDOWN_EVENT: | 144 case CTRL_SHUTDOWN_EVENT: |
148 self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 145 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 146 &WtsSessionProcessLauncher::Stop, |
| 147 base::Unretained(self->launcher_.get()))); |
149 self->stopped_event_.Wait(); | 148 self->stopped_event_.Wait(); |
150 return TRUE; | 149 return TRUE; |
151 | 150 |
152 default: | 151 default: |
153 return FALSE; | 152 return FALSE; |
154 } | 153 } |
155 } | 154 } |
156 | 155 |
157 HostService* HostService::GetInstance() { | 156 HostService* HostService::GetInstance() { |
158 return Singleton<HostService>::get(); | 157 return Singleton<HostService>::get(); |
(...skipping 29 matching lines...) Expand all Loading... |
188 run_routine_ = &HostService::RunInConsole; | 187 run_routine_ = &HostService::RunInConsole; |
189 } | 188 } |
190 | 189 |
191 return true; | 190 return true; |
192 } | 191 } |
193 | 192 |
194 int HostService::Run() { | 193 int HostService::Run() { |
195 return (this->*run_routine_)(); | 194 return (this->*run_routine_)(); |
196 } | 195 } |
197 | 196 |
198 void HostService::RunMessageLoop() { | 197 void HostService::RunMessageLoop(MessageLoop* message_loop) { |
199 // Launch the I/O thread. | 198 // Launch the I/O thread. |
200 base::Thread io_thread(kIoThreadName); | 199 base::Thread io_thread(kIoThreadName); |
201 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); | 200 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); |
202 if (!io_thread.StartWithOptions(io_thread_options)) { | 201 if (!io_thread.StartWithOptions(io_thread_options)) { |
203 LOG(FATAL) << "Failed to start the I/O thread"; | 202 LOG(FATAL) << "Failed to start the I/O thread"; |
204 shutting_down_ = true; | |
205 stopped_event_.Signal(); | 203 stopped_event_.Signal(); |
206 return; | 204 return; |
207 } | 205 } |
208 | 206 |
209 WtsSessionProcessLauncher launcher(this, host_binary_, | 207 // Create the session process launcher. |
210 message_loop_->message_loop_proxy(), | 208 launcher_.reset(new WtsSessionProcessLauncher( |
211 io_thread.message_loop_proxy()); | 209 base::Bind(&HostService::OnLauncherShutdown, base::Unretained(this)), |
| 210 this, |
| 211 host_binary_, |
| 212 main_task_runner_, |
| 213 io_thread.message_loop_proxy())); |
212 | 214 |
213 // Run the service. | 215 // Run the service. |
214 message_loop_->Run(); | 216 message_loop->Run(); |
215 | |
216 // Clean up the observers by emulating detaching from the console. | |
217 shutting_down_ = true; | |
218 OnSessionChange(); | |
219 | 217 |
220 // Release the control handler. | 218 // Release the control handler. |
221 stopped_event_.Signal(); | 219 stopped_event_.Signal(); |
222 } | 220 } |
223 | 221 |
224 int HostService::RunAsService() { | 222 int HostService::RunAsService() { |
225 SERVICE_TABLE_ENTRYW dispatch_table[] = { | 223 SERVICE_TABLE_ENTRYW dispatch_table[] = { |
226 { const_cast<LPWSTR>(service_name_.c_str()), &HostService::ServiceMain }, | 224 { const_cast<LPWSTR>(service_name_.c_str()), &HostService::ServiceMain }, |
227 { NULL, NULL } | 225 { NULL, NULL } |
228 }; | 226 }; |
229 | 227 |
230 if (!StartServiceCtrlDispatcherW(dispatch_table)) { | 228 if (!StartServiceCtrlDispatcherW(dispatch_table)) { |
231 LOG_GETLASTERROR(ERROR) | 229 LOG_GETLASTERROR(ERROR) |
232 << "Failed to connect to the service control manager"; | 230 << "Failed to connect to the service control manager"; |
233 return kErrorExitCode; | 231 return kErrorExitCode; |
234 } | 232 } |
235 | 233 |
236 return kSuccessExitCode; | 234 return kSuccessExitCode; |
237 } | 235 } |
238 | 236 |
239 int HostService::RunInConsole() { | 237 int HostService::RunInConsole() { |
240 MessageLoop message_loop(MessageLoop::TYPE_UI); | 238 MessageLoop message_loop(MessageLoop::TYPE_UI); |
241 | 239 |
242 // Allow other threads to post to our message loop. | 240 // Allow other threads to post to our message loop. |
243 message_loop_ = &message_loop; | 241 main_task_runner_ = message_loop.message_loop_proxy(); |
244 | 242 |
245 int result = kErrorExitCode; | 243 int result = kErrorExitCode; |
246 | 244 |
247 // Subscribe to Ctrl-C and other console events. | 245 // Subscribe to Ctrl-C and other console events. |
248 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { | 246 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { |
249 LOG_GETLASTERROR(ERROR) | 247 LOG_GETLASTERROR(ERROR) |
250 << "Failed to set console control handler"; | 248 << "Failed to set console control handler"; |
251 return result; | 249 return result; |
252 } | 250 } |
253 | 251 |
(...skipping 17 matching lines...) Expand all Loading... |
271 window = CreateWindowW(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, | 269 window = CreateWindowW(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, |
272 instance, 0); | 270 instance, 0); |
273 if (window == NULL) { | 271 if (window == NULL) { |
274 LOG_GETLASTERROR(ERROR) | 272 LOG_GETLASTERROR(ERROR) |
275 << "Failed to creat the session notificationwindow"; | 273 << "Failed to creat the session notificationwindow"; |
276 goto cleanup; | 274 goto cleanup; |
277 } | 275 } |
278 | 276 |
279 // Post a dummy session change notification to peek up the current console | 277 // Post a dummy session change notification to peek up the current console |
280 // session. | 278 // session. |
281 message_loop.PostTask(FROM_HERE, base::Bind( | 279 main_task_runner_->PostTask(FROM_HERE, base::Bind( |
282 &HostService::OnSessionChange, base::Unretained(this))); | 280 &HostService::OnSessionChange, base::Unretained(this))); |
283 | 281 |
284 // Subscribe to session change notifications. | 282 // Subscribe to session change notifications. |
285 if (WTSRegisterSessionNotification(window, | 283 if (WTSRegisterSessionNotification(window, |
286 NOTIFY_FOR_ALL_SESSIONS) != FALSE) { | 284 NOTIFY_FOR_ALL_SESSIONS) != FALSE) { |
287 // Run the service. | 285 // Run the service. |
288 RunMessageLoop(); | 286 RunMessageLoop(&message_loop); |
289 | 287 |
290 WTSUnRegisterSessionNotification(window); | 288 WTSUnRegisterSessionNotification(window); |
291 result = kSuccessExitCode; | 289 result = kSuccessExitCode; |
292 } | 290 } |
293 | 291 |
294 cleanup: | 292 cleanup: |
295 if (window != NULL) { | 293 if (window != NULL) { |
296 DestroyWindow(window); | 294 DestroyWindow(window); |
297 } | 295 } |
298 | 296 |
299 if (atom != 0) { | 297 if (atom != 0) { |
300 UnregisterClass(MAKEINTATOM(atom), instance); | 298 UnregisterClass(MAKEINTATOM(atom), instance); |
301 } | 299 } |
302 | 300 |
303 // Unsubscribe from console events. Ignore the exit code. There is nothing | 301 // Unsubscribe from console events. Ignore the exit code. There is nothing |
304 // we can do about it now and the program is about to exit anyway. Even if | 302 // we can do about it now and the program is about to exit anyway. Even if |
305 // it crashes nothing is going to be broken because of it. | 303 // it crashes nothing is going to be broken because of it. |
306 SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE); | 304 SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE); |
307 | 305 |
308 message_loop_ = NULL; | |
309 return result; | 306 return result; |
310 } | 307 } |
311 | 308 |
312 DWORD WINAPI HostService::ServiceControlHandler(DWORD control, | 309 DWORD WINAPI HostService::ServiceControlHandler(DWORD control, |
313 DWORD event_type, | 310 DWORD event_type, |
314 LPVOID event_data, | 311 LPVOID event_data, |
315 LPVOID context) { | 312 LPVOID context) { |
316 HostService* self = reinterpret_cast<HostService*>(context); | 313 HostService* self = reinterpret_cast<HostService*>(context); |
317 switch (control) { | 314 switch (control) { |
318 case SERVICE_CONTROL_INTERROGATE: | 315 case SERVICE_CONTROL_INTERROGATE: |
319 return NO_ERROR; | 316 return NO_ERROR; |
320 | 317 |
321 case SERVICE_CONTROL_SHUTDOWN: | 318 case SERVICE_CONTROL_SHUTDOWN: |
322 case SERVICE_CONTROL_STOP: | 319 case SERVICE_CONTROL_STOP: |
323 self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 320 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 321 &WtsSessionProcessLauncher::Stop, |
| 322 base::Unretained(self->launcher_.get()))); |
324 self->stopped_event_.Wait(); | 323 self->stopped_event_.Wait(); |
325 return NO_ERROR; | 324 return NO_ERROR; |
326 | 325 |
327 case SERVICE_CONTROL_SESSIONCHANGE: | 326 case SERVICE_CONTROL_SESSIONCHANGE: |
328 self->message_loop_->PostTask(FROM_HERE, base::Bind( | 327 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
329 &HostService::OnSessionChange, base::Unretained(self))); | 328 &HostService::OnSessionChange, base::Unretained(self))); |
330 return NO_ERROR; | 329 return NO_ERROR; |
331 | 330 |
332 default: | 331 default: |
333 return ERROR_CALL_NOT_IMPLEMENTED; | 332 return ERROR_CALL_NOT_IMPLEMENTED; |
334 } | 333 } |
335 } | 334 } |
336 | 335 |
337 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { | 336 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { |
338 MessageLoop message_loop; | 337 MessageLoop message_loop; |
339 | 338 |
340 // Allow other threads to post to our message loop. | 339 // Allow other threads to post to our message loop. |
341 HostService* self = HostService::GetInstance(); | 340 HostService* self = HostService::GetInstance(); |
342 self->message_loop_ = &message_loop; | 341 self->main_task_runner_ = message_loop.message_loop_proxy(); |
343 | 342 |
344 // Register the service control handler. | 343 // Register the service control handler. |
345 self->service_status_handle_ = | 344 self->service_status_handle_ = |
346 RegisterServiceCtrlHandlerExW(self->service_name_.c_str(), | 345 RegisterServiceCtrlHandlerExW(self->service_name_.c_str(), |
347 &HostService::ServiceControlHandler, | 346 &HostService::ServiceControlHandler, |
348 self); | 347 self); |
349 if (self->service_status_handle_ == 0) { | 348 if (self->service_status_handle_ == 0) { |
350 LOG_GETLASTERROR(ERROR) | 349 LOG_GETLASTERROR(ERROR) |
351 << "Failed to register the service control handler"; | 350 << "Failed to register the service control handler"; |
352 return; | 351 return; |
(...skipping 10 matching lines...) Expand all Loading... |
363 service_status.dwWin32ExitCode = kSuccessExitCode; | 362 service_status.dwWin32ExitCode = kSuccessExitCode; |
364 | 363 |
365 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { | 364 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
366 LOG_GETLASTERROR(ERROR) | 365 LOG_GETLASTERROR(ERROR) |
367 << "Failed to report service status to the service control manager"; | 366 << "Failed to report service status to the service control manager"; |
368 return; | 367 return; |
369 } | 368 } |
370 | 369 |
371 // Post a dummy session change notification to peek up the current console | 370 // Post a dummy session change notification to peek up the current console |
372 // session. | 371 // session. |
373 message_loop.PostTask(FROM_HERE, base::Bind( | 372 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
374 &HostService::OnSessionChange, base::Unretained(self))); | 373 &HostService::OnSessionChange, base::Unretained(self))); |
375 | 374 |
376 // Run the service. | 375 // Run the service. |
377 self->RunMessageLoop(); | 376 self->RunMessageLoop(&message_loop); |
378 | 377 |
379 // Tell SCM that the service is stopped. | 378 // Tell SCM that the service is stopped. |
380 service_status.dwCurrentState = SERVICE_STOPPED; | 379 service_status.dwCurrentState = SERVICE_STOPPED; |
381 service_status.dwControlsAccepted = 0; | 380 service_status.dwControlsAccepted = 0; |
382 | 381 |
383 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { | 382 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
384 LOG_GETLASTERROR(ERROR) | 383 LOG_GETLASTERROR(ERROR) |
385 << "Failed to report service status to the service control manager"; | 384 << "Failed to report service status to the service control manager"; |
386 return; | 385 return; |
387 } | 386 } |
388 | |
389 self->message_loop_ = NULL; | |
390 } | 387 } |
391 | 388 |
392 LRESULT CALLBACK HostService::SessionChangeNotificationProc(HWND hwnd, | 389 LRESULT CALLBACK HostService::SessionChangeNotificationProc(HWND hwnd, |
393 UINT message, | 390 UINT message, |
394 WPARAM wparam, | 391 WPARAM wparam, |
395 LPARAM lparam) { | 392 LPARAM lparam) { |
396 switch (message) { | 393 switch (message) { |
397 case WM_WTSSESSION_CHANGE: { | 394 case WM_WTSSESSION_CHANGE: { |
398 HostService* self = HostService::GetInstance(); | 395 HostService* self = HostService::GetInstance(); |
399 self->OnSessionChange(); | 396 self->OnSessionChange(); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
438 } | 435 } |
439 | 436 |
440 remoting::HostService* service = remoting::HostService::GetInstance(); | 437 remoting::HostService* service = remoting::HostService::GetInstance(); |
441 if (!service->InitWithCommandLine(command_line)) { | 438 if (!service->InitWithCommandLine(command_line)) { |
442 usage(argv[0]); | 439 usage(argv[0]); |
443 return kUsageExitCode; | 440 return kUsageExitCode; |
444 } | 441 } |
445 | 442 |
446 return service->Run(); | 443 return service->Run(); |
447 } | 444 } |
OLD | NEW |