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

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

Issue 10796099: Introducing remoting::Stoppable helper base class implementing asynchronous shutdown on a specific t (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Fixing case of the include file name. 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
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/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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698