Chromium Code Reviews| 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 |