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/host_service_win.h" | 8 #include "remoting/host/host_service_win.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/stringprintf.h" | 22 #include "base/stringprintf.h" |
23 #include "base/threading/thread.h" | 23 #include "base/threading/thread.h" |
24 #include "base/utf_string_conversions.h" | 24 #include "base/utf_string_conversions.h" |
25 #include "base/win/wrapped_window_proc.h" | 25 #include "base/win/wrapped_window_proc.h" |
26 #include "remoting/base/auto_message_loop.h" | |
27 #include "remoting/base/auto_thread.h" | |
26 #include "remoting/base/breakpad.h" | 28 #include "remoting/base/breakpad.h" |
27 #include "remoting/base/scoped_sc_handle_win.h" | 29 #include "remoting/base/scoped_sc_handle_win.h" |
30 #include "remoting/base/shutdownable.h" | |
28 #include "remoting/host/branding.h" | 31 #include "remoting/host/branding.h" |
29 #include "remoting/host/host_service_resource.h" | 32 #include "remoting/host/host_service_resource.h" |
30 #include "remoting/host/usage_stats_consent.h" | 33 #include "remoting/host/usage_stats_consent.h" |
31 #include "remoting/host/wts_console_observer_win.h" | 34 #include "remoting/host/wts_console_observer_win.h" |
32 #include "remoting/host/wts_session_process_launcher_win.h" | 35 #include "remoting/host/wts_session_process_launcher_win.h" |
33 | 36 |
34 using base::StringPrintf; | 37 using base::StringPrintf; |
35 | 38 |
36 namespace { | 39 namespace { |
37 | 40 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
75 void usage(const char* program_name) { | 78 void usage(const char* program_name) { |
76 fprintf(stderr, kUsageMessage, program_name); | 79 fprintf(stderr, kUsageMessage, program_name); |
77 } | 80 } |
78 | 81 |
79 } // namespace | 82 } // namespace |
80 | 83 |
81 namespace remoting { | 84 namespace remoting { |
82 | 85 |
83 HostService::HostService() : | 86 HostService::HostService() : |
84 console_session_id_(kInvalidSessionId), | 87 console_session_id_(kInvalidSessionId), |
85 message_loop_(NULL), | |
86 run_routine_(&HostService::RunAsService), | 88 run_routine_(&HostService::RunAsService), |
87 service_name_(kWindowsServiceName), | 89 service_name_(kWindowsServiceName), |
88 service_status_handle_(0), | 90 service_status_handle_(0), |
89 shutting_down_(false), | |
90 stopped_event_(true, false) { | 91 stopped_event_(true, false) { |
91 } | 92 } |
92 | 93 |
93 HostService::~HostService() { | 94 HostService::~HostService() { |
94 } | 95 } |
95 | 96 |
96 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { | 97 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { |
97 DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread()); | 98 DCHECK(message_loop_->BelongsToCurrentThread()); |
98 | 99 |
99 console_observers_.AddObserver(observer); | 100 console_observers_.AddObserver(observer); |
100 } | 101 } |
101 | 102 |
102 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { | 103 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { |
103 DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread()); | 104 DCHECK(message_loop_->BelongsToCurrentThread()); |
104 | 105 |
105 console_observers_.RemoveObserver(observer); | 106 console_observers_.RemoveObserver(observer); |
107 } | |
106 | 108 |
107 // Stop the service if there are no more observers. | 109 void HostService::OnLauncherShutdown(Shutdownable* /* launcher */) { |
108 if (!console_observers_.might_have_observers()) { | 110 launcher_.reset(NULL); |
109 message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 111 message_loop_ = NULL; |
110 } | |
111 } | 112 } |
112 | 113 |
113 void HostService::OnSessionChange() { | 114 void HostService::OnSessionChange() { |
114 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads | 115 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads |
115 // a single value from shared memory. Therefore it is better to check if | 116 // 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 | 117 // the console session is still the same every time a session change |
117 // notification event is posted. This also takes care of coalescing multiple | 118 // notification event is posted. This also takes care of coalescing multiple |
118 // events into one since we look at the latest state. | 119 // events into one since we look at the latest state. |
119 uint32 console_session_id = kInvalidSessionId; | 120 uint32 console_session_id = WTSGetActiveConsoleSessionId(); |
120 if (!shutting_down_) { | |
121 console_session_id = WTSGetActiveConsoleSessionId(); | |
122 } | |
123 if (console_session_id_ != console_session_id) { | 121 if (console_session_id_ != console_session_id) { |
124 if (console_session_id_ != kInvalidSessionId) { | 122 if (console_session_id_ != kInvalidSessionId) { |
125 FOR_EACH_OBSERVER(WtsConsoleObserver, | 123 FOR_EACH_OBSERVER(WtsConsoleObserver, |
126 console_observers_, | 124 console_observers_, |
127 OnSessionDetached()); | 125 OnSessionDetached()); |
128 } | 126 } |
129 | 127 |
130 console_session_id_ = console_session_id; | 128 console_session_id_ = console_session_id; |
131 | 129 |
132 if (console_session_id_ != kInvalidSessionId) { | 130 if (console_session_id_ != kInvalidSessionId) { |
133 FOR_EACH_OBSERVER(WtsConsoleObserver, | 131 FOR_EACH_OBSERVER(WtsConsoleObserver, |
134 console_observers_, | 132 console_observers_, |
135 OnSessionAttached(console_session_id_)); | 133 OnSessionAttached(console_session_id_)); |
136 } | 134 } |
137 } | 135 } |
138 } | 136 } |
139 | 137 |
140 BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) { | 138 BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) { |
141 HostService* self = HostService::GetInstance(); | 139 HostService* self = HostService::GetInstance(); |
142 switch (event) { | 140 switch (event) { |
143 case CTRL_C_EVENT: | 141 case CTRL_C_EVENT: |
144 case CTRL_BREAK_EVENT: | 142 case CTRL_BREAK_EVENT: |
145 case CTRL_CLOSE_EVENT: | 143 case CTRL_CLOSE_EVENT: |
146 case CTRL_LOGOFF_EVENT: | 144 case CTRL_LOGOFF_EVENT: |
147 case CTRL_SHUTDOWN_EVENT: | 145 case CTRL_SHUTDOWN_EVENT: |
148 self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 146 self->message_loop_->PostTask(FROM_HERE, base::Bind( |
147 &WtsSessionProcessLauncher::Shutdown, | |
148 base::Unretained(self->launcher_.get()))); | |
149 self->stopped_event_.Wait(); | 149 self->stopped_event_.Wait(); |
150 return TRUE; | 150 return TRUE; |
151 | 151 |
152 default: | 152 default: |
153 return FALSE; | 153 return FALSE; |
154 } | 154 } |
155 } | 155 } |
156 | 156 |
157 HostService* HostService::GetInstance() { | 157 HostService* HostService::GetInstance() { |
158 return Singleton<HostService>::get(); | 158 return Singleton<HostService>::get(); |
(...skipping 29 matching lines...) Expand all Loading... | |
188 run_routine_ = &HostService::RunInConsole; | 188 run_routine_ = &HostService::RunInConsole; |
189 } | 189 } |
190 | 190 |
191 return true; | 191 return true; |
192 } | 192 } |
193 | 193 |
194 int HostService::Run() { | 194 int HostService::Run() { |
195 return (this->*run_routine_)(); | 195 return (this->*run_routine_)(); |
196 } | 196 } |
197 | 197 |
198 void HostService::RunMessageLoop() { | 198 bool HostService::BeforeMessageLoop() { |
199 // Launch the I/O thread. | 199 // Launch the I/O thread. |
200 base::Thread io_thread(kIoThreadName); | 200 scoped_refptr<AutoThread> io_thread( |
201 new AutoThread(kIoThreadName, message_loop_)); | |
Sergey Ulanov
2012/07/23 22:44:37
Why do we need thread to be refcounted?
Can we us
alexeypa (please no reviews)
2012/07/23 23:44:53
To make sure it is alive until all objects dependi
| |
202 | |
201 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); | 203 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); |
202 if (!io_thread.StartWithOptions(io_thread_options)) { | 204 if (!io_thread->StartWithOptions(io_thread_options)) { |
203 LOG(FATAL) << "Failed to start the I/O thread"; | 205 LOG(FATAL) << "Failed to start the I/O thread"; |
204 shutting_down_ = true; | 206 return false; |
205 stopped_event_.Signal(); | |
206 return; | |
207 } | 207 } |
208 | 208 |
209 WtsSessionProcessLauncher launcher(this, host_binary_, | 209 // Create the session process launcher. |
210 message_loop_->message_loop_proxy(), | 210 launcher_.reset(new WtsSessionProcessLauncher( |
211 io_thread.message_loop_proxy()); | 211 base::Bind(&HostService::OnLauncherShutdown, base::Unretained(this)), |
212 | 212 this, |
213 host_binary_, | |
214 message_loop_, | |
215 io_thread)); | |
216 return true; | |
217 } | |
218 void HostService::RunMessageLoop() { | |
213 // Run the service. | 219 // Run the service. |
214 message_loop_->Run(); | 220 if (BeforeMessageLoop()) { |
215 | 221 message_loop_->Run(); |
216 // Clean up the observers by emulating detaching from the console. | 222 } |
217 shutting_down_ = true; | |
218 OnSessionChange(); | |
219 | 223 |
220 // Release the control handler. | 224 // Release the control handler. |
221 stopped_event_.Signal(); | 225 stopped_event_.Signal(); |
222 } | 226 } |
223 | 227 |
224 int HostService::RunAsService() { | 228 int HostService::RunAsService() { |
225 SERVICE_TABLE_ENTRYW dispatch_table[] = { | 229 SERVICE_TABLE_ENTRYW dispatch_table[] = { |
226 { const_cast<LPWSTR>(service_name_.c_str()), &HostService::ServiceMain }, | 230 { const_cast<LPWSTR>(service_name_.c_str()), &HostService::ServiceMain }, |
227 { NULL, NULL } | 231 { NULL, NULL } |
228 }; | 232 }; |
229 | 233 |
230 if (!StartServiceCtrlDispatcherW(dispatch_table)) { | 234 if (!StartServiceCtrlDispatcherW(dispatch_table)) { |
231 LOG_GETLASTERROR(ERROR) | 235 LOG_GETLASTERROR(ERROR) |
232 << "Failed to connect to the service control manager"; | 236 << "Failed to connect to the service control manager"; |
233 return kErrorExitCode; | 237 return kErrorExitCode; |
234 } | 238 } |
235 | 239 |
236 return kSuccessExitCode; | 240 return kSuccessExitCode; |
237 } | 241 } |
238 | 242 |
239 int HostService::RunInConsole() { | 243 int HostService::RunInConsole() { |
240 MessageLoop message_loop(MessageLoop::TYPE_UI); | 244 AutoMessageLoop message_loop(MessageLoop::TYPE_UI); |
Sergey Ulanov
2012/07/23 22:44:37
You are creating ref-counted object on stack. That
alexeypa (please no reviews)
2012/07/23 23:44:53
I control the destruction logic for this object. A
| |
241 | 245 |
242 // Allow other threads to post to our message loop. | 246 // Allow other threads to post to our message loop. |
243 message_loop_ = &message_loop; | 247 message_loop_ = scoped_refptr<AutoMessageLoop>(&message_loop); |
244 | 248 |
245 int result = kErrorExitCode; | 249 int result = kErrorExitCode; |
246 | 250 |
247 // Subscribe to Ctrl-C and other console events. | 251 // Subscribe to Ctrl-C and other console events. |
248 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { | 252 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { |
249 LOG_GETLASTERROR(ERROR) | 253 LOG_GETLASTERROR(ERROR) |
250 << "Failed to set console control handler"; | 254 << "Failed to set console control handler"; |
251 return result; | 255 return result; |
252 } | 256 } |
253 | 257 |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
297 } | 301 } |
298 | 302 |
299 if (atom != 0) { | 303 if (atom != 0) { |
300 UnregisterClass(MAKEINTATOM(atom), instance); | 304 UnregisterClass(MAKEINTATOM(atom), instance); |
301 } | 305 } |
302 | 306 |
303 // Unsubscribe from console events. Ignore the exit code. There is nothing | 307 // 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 | 308 // 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. | 309 // it crashes nothing is going to be broken because of it. |
306 SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE); | 310 SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE); |
307 | |
308 message_loop_ = NULL; | |
309 return result; | 311 return result; |
310 } | 312 } |
311 | 313 |
312 DWORD WINAPI HostService::ServiceControlHandler(DWORD control, | 314 DWORD WINAPI HostService::ServiceControlHandler(DWORD control, |
313 DWORD event_type, | 315 DWORD event_type, |
314 LPVOID event_data, | 316 LPVOID event_data, |
315 LPVOID context) { | 317 LPVOID context) { |
316 HostService* self = reinterpret_cast<HostService*>(context); | 318 HostService* self = reinterpret_cast<HostService*>(context); |
317 switch (control) { | 319 switch (control) { |
318 case SERVICE_CONTROL_INTERROGATE: | 320 case SERVICE_CONTROL_INTERROGATE: |
319 return NO_ERROR; | 321 return NO_ERROR; |
320 | 322 |
321 case SERVICE_CONTROL_SHUTDOWN: | 323 case SERVICE_CONTROL_SHUTDOWN: |
322 case SERVICE_CONTROL_STOP: | 324 case SERVICE_CONTROL_STOP: |
323 self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 325 self->message_loop_->PostTask(FROM_HERE, base::Bind( |
326 &WtsSessionProcessLauncher::Shutdown, | |
327 base::Unretained(self->launcher_.get()))); | |
324 self->stopped_event_.Wait(); | 328 self->stopped_event_.Wait(); |
325 return NO_ERROR; | 329 return NO_ERROR; |
326 | 330 |
327 case SERVICE_CONTROL_SESSIONCHANGE: | 331 case SERVICE_CONTROL_SESSIONCHANGE: |
328 self->message_loop_->PostTask(FROM_HERE, base::Bind( | 332 self->message_loop_->PostTask(FROM_HERE, base::Bind( |
329 &HostService::OnSessionChange, base::Unretained(self))); | 333 &HostService::OnSessionChange, base::Unretained(self))); |
330 return NO_ERROR; | 334 return NO_ERROR; |
331 | 335 |
332 default: | 336 default: |
333 return ERROR_CALL_NOT_IMPLEMENTED; | 337 return ERROR_CALL_NOT_IMPLEMENTED; |
334 } | 338 } |
335 } | 339 } |
336 | 340 |
337 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { | 341 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { |
338 MessageLoop message_loop; | 342 AutoMessageLoop message_loop(MessageLoop::TYPE_DEFAULT); |
Sergey Ulanov
2012/07/23 22:44:37
Why do we need MessageLoop to be refcounted here?
alexeypa (please no reviews)
2012/07/23 23:44:53
It doe snot have to outlive ServiceMain(). It is r
| |
339 | 343 |
340 // Allow other threads to post to our message loop. | 344 // Allow other threads to post to our message loop. |
341 HostService* self = HostService::GetInstance(); | 345 HostService* self = HostService::GetInstance(); |
342 self->message_loop_ = &message_loop; | 346 self->message_loop_ = scoped_refptr<AutoMessageLoop>(&message_loop); |
343 | 347 |
344 // Register the service control handler. | 348 // Register the service control handler. |
345 self->service_status_handle_ = | 349 self->service_status_handle_ = |
346 RegisterServiceCtrlHandlerExW(self->service_name_.c_str(), | 350 RegisterServiceCtrlHandlerExW(self->service_name_.c_str(), |
347 &HostService::ServiceControlHandler, | 351 &HostService::ServiceControlHandler, |
348 self); | 352 self); |
349 if (self->service_status_handle_ == 0) { | 353 if (self->service_status_handle_ == 0) { |
350 LOG_GETLASTERROR(ERROR) | 354 LOG_GETLASTERROR(ERROR) |
351 << "Failed to register the service control handler"; | 355 << "Failed to register the service control handler"; |
352 return; | 356 return; |
(...skipping 25 matching lines...) Expand all Loading... | |
378 | 382 |
379 // Tell SCM that the service is stopped. | 383 // Tell SCM that the service is stopped. |
380 service_status.dwCurrentState = SERVICE_STOPPED; | 384 service_status.dwCurrentState = SERVICE_STOPPED; |
381 service_status.dwControlsAccepted = 0; | 385 service_status.dwControlsAccepted = 0; |
382 | 386 |
383 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { | 387 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
384 LOG_GETLASTERROR(ERROR) | 388 LOG_GETLASTERROR(ERROR) |
385 << "Failed to report service status to the service control manager"; | 389 << "Failed to report service status to the service control manager"; |
386 return; | 390 return; |
387 } | 391 } |
388 | |
389 self->message_loop_ = NULL; | |
390 } | 392 } |
391 | 393 |
392 LRESULT CALLBACK HostService::SessionChangeNotificationProc(HWND hwnd, | 394 LRESULT CALLBACK HostService::SessionChangeNotificationProc(HWND hwnd, |
393 UINT message, | 395 UINT message, |
394 WPARAM wparam, | 396 WPARAM wparam, |
395 LPARAM lparam) { | 397 LPARAM lparam) { |
396 switch (message) { | 398 switch (message) { |
397 case WM_WTSSESSION_CHANGE: { | 399 case WM_WTSSESSION_CHANGE: { |
398 HostService* self = HostService::GetInstance(); | 400 HostService* self = HostService::GetInstance(); |
399 self->OnSessionChange(); | 401 self->OnSessionChange(); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
438 } | 440 } |
439 | 441 |
440 remoting::HostService* service = remoting::HostService::GetInstance(); | 442 remoting::HostService* service = remoting::HostService::GetInstance(); |
441 if (!service->InitWithCommandLine(command_line)) { | 443 if (!service->InitWithCommandLine(command_line)) { |
442 usage(argv[0]); | 444 usage(argv[0]); |
443 return kUsageExitCode; | 445 return kUsageExitCode; |
444 } | 446 } |
445 | 447 |
446 return service->Run(); | 448 return service->Run(); |
447 } | 449 } |
OLD | NEW |