| 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 <shellapi.h> | 11 #include <shellapi.h> |
| 12 #include <wtsapi32.h> | 12 #include <wtsapi32.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/base_switches.h" | 16 #include "base/base_switches.h" |
| 17 #include "base/bind.h" | 17 #include "base/bind.h" |
| 18 #include "base/command_line.h" | 18 #include "base/command_line.h" |
| 19 #include "base/file_path.h" | 19 #include "base/file_path.h" |
| 20 #include "base/logging.h" | 20 #include "base/logging.h" |
| 21 #include "base/message_loop.h" | 21 #include "base/message_loop.h" |
| 22 #include "base/single_thread_task_runner.h" | 22 #include "base/single_thread_task_runner.h" |
| 23 #include "base/stringprintf.h" | 23 #include "base/stringprintf.h" |
| 24 #include "base/threading/thread.h" | 24 #include "base/threading/thread.h" |
| 25 #include "base/utf_string_conversions.h" | 25 #include "base/utf_string_conversions.h" |
| 26 #include "base/win/wrapped_window_proc.h" | 26 #include "base/win/wrapped_window_proc.h" |
| 27 #include "remoting/base/auto_thread_task_runner.h" |
| 27 #include "remoting/base/breakpad.h" | 28 #include "remoting/base/breakpad.h" |
| 28 #include "remoting/base/scoped_sc_handle_win.h" | 29 #include "remoting/base/scoped_sc_handle_win.h" |
| 29 #include "remoting/base/stoppable.h" | 30 #include "remoting/base/stoppable.h" |
| 30 #include "remoting/host/branding.h" | 31 #include "remoting/host/branding.h" |
| 31 | 32 |
| 32 #if defined(REMOTING_MULTI_PROCESS) | 33 #if defined(REMOTING_MULTI_PROCESS) |
| 33 #include "remoting/host/daemon_process.h" | 34 #include "remoting/host/daemon_process.h" |
| 34 #endif // defined(REMOTING_MULTI_PROCESS) | 35 #endif // defined(REMOTING_MULTI_PROCESS) |
| 35 | 36 |
| 36 #include "remoting/host/usage_stats_consent.h" | 37 #include "remoting/host/usage_stats_consent.h" |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 85 // Exit codes: | 86 // Exit codes: |
| 86 const int kSuccessExitCode = 0; | 87 const int kSuccessExitCode = 0; |
| 87 const int kUsageExitCode = 1; | 88 const int kUsageExitCode = 1; |
| 88 const int kErrorExitCode = 2; | 89 const int kErrorExitCode = 2; |
| 89 | 90 |
| 90 void usage(const FilePath& program_name) { | 91 void usage(const FilePath& program_name) { |
| 91 LOG(INFO) << StringPrintf(kUsageMessage, | 92 LOG(INFO) << StringPrintf(kUsageMessage, |
| 92 UTF16ToWide(program_name.value()).c_str()); | 93 UTF16ToWide(program_name.value()).c_str()); |
| 93 } | 94 } |
| 94 | 95 |
| 96 void QuitMessageLoop(MessageLoop* message_loop) { |
| 97 message_loop->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| 98 } |
| 99 |
| 95 } // namespace | 100 } // namespace |
| 96 | 101 |
| 97 namespace remoting { | 102 namespace remoting { |
| 98 | 103 |
| 99 HostService::HostService() : | 104 HostService::HostService() : |
| 100 console_session_id_(kInvalidSessionId), | 105 console_session_id_(kInvalidSessionId), |
| 101 run_routine_(&HostService::RunAsService), | 106 run_routine_(&HostService::RunAsService), |
| 102 service_status_handle_(0), | 107 service_status_handle_(0), |
| 103 stopped_event_(true, false) { | 108 stopped_event_(true, false) { |
| 104 } | 109 } |
| 105 | 110 |
| 106 HostService::~HostService() { | 111 HostService::~HostService() { |
| 107 } | 112 } |
| 108 | 113 |
| 109 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { | 114 void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { |
| 110 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 115 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 111 | 116 |
| 112 console_observers_.AddObserver(observer); | 117 console_observers_.AddObserver(observer); |
| 113 } | 118 } |
| 114 | 119 |
| 115 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { | 120 void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { |
| 116 DCHECK(main_task_runner_->BelongsToCurrentThread()); | 121 DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| 117 | 122 |
| 118 console_observers_.RemoveObserver(observer); | 123 console_observers_.RemoveObserver(observer); |
| 119 } | 124 } |
| 120 | 125 |
| 121 void HostService::OnChildStopped() { | 126 void HostService::OnChildStopped() { |
| 122 child_.reset(NULL); | 127 child_.reset(NULL); |
| 123 main_task_runner_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); | 128 main_task_runner_ = NULL; |
| 124 } | 129 } |
| 125 | 130 |
| 126 void HostService::OnSessionChange() { | 131 void HostService::OnSessionChange() { |
| 127 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads | 132 // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads |
| 128 // a single value from shared memory. Therefore it is better to check if | 133 // a single value from shared memory. Therefore it is better to check if |
| 129 // the console session is still the same every time a session change | 134 // the console session is still the same every time a session change |
| 130 // notification event is posted. This also takes care of coalescing multiple | 135 // notification event is posted. This also takes care of coalescing multiple |
| 131 // events into one since we look at the latest state. | 136 // events into one since we look at the latest state. |
| 132 uint32 console_session_id = WTSGetActiveConsoleSessionId(); | 137 uint32 console_session_id = WTSGetActiveConsoleSessionId(); |
| 133 if (console_session_id_ != console_session_id) { | 138 if (console_session_id_ != console_session_id) { |
| (...skipping 55 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 189 run_routine_ = &HostService::RunInConsole; | 194 run_routine_ = &HostService::RunInConsole; |
| 190 } | 195 } |
| 191 | 196 |
| 192 return true; | 197 return true; |
| 193 } | 198 } |
| 194 | 199 |
| 195 int HostService::Run() { | 200 int HostService::Run() { |
| 196 return (this->*run_routine_)(); | 201 return (this->*run_routine_)(); |
| 197 } | 202 } |
| 198 | 203 |
| 199 void HostService::RunMessageLoop(MessageLoop* message_loop) { | 204 void HostService::CreateLauncher( |
| 200 // Launch the I/O thread. | 205 scoped_refptr<base::SingleThreadTaskRunner> io_task_runner) { |
| 201 base::Thread io_thread(kIoThreadName); | |
| 202 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); | |
| 203 if (!io_thread.StartWithOptions(io_thread_options)) { | |
| 204 LOG(ERROR) << "Failed to start the I/O thread"; | |
| 205 stopped_event_.Signal(); | |
| 206 return; | |
| 207 } | |
| 208 | 206 |
| 209 #if defined(REMOTING_MULTI_PROCESS) | 207 #if defined(REMOTING_MULTI_PROCESS) |
| 210 | 208 |
| 211 child_ = DaemonProcess::Create( | 209 child_ = DaemonProcess::Create( |
| 212 main_task_runner_, | 210 main_task_runner_, |
| 213 io_thread.message_loop_proxy(), | 211 io_task_runner, |
| 214 base::Bind(&HostService::OnChildStopped, | 212 base::Bind(&HostService::OnChildStopped, |
| 215 base::Unretained(this))).PassAs<Stoppable>(); | 213 base::Unretained(this))).PassAs<Stoppable>(); |
| 216 | 214 |
| 217 #else // !defined(REMOTING_MULTI_PROCESS) | 215 #else // !defined(REMOTING_MULTI_PROCESS) |
| 218 | 216 |
| 219 // Create the session process launcher. | 217 // Create the session process launcher. |
| 220 child_.reset(new WtsSessionProcessLauncher( | 218 child_.reset(new WtsSessionProcessLauncher( |
| 221 base::Bind(&HostService::OnChildStopped, base::Unretained(this)), | 219 base::Bind(&HostService::OnChildStopped, base::Unretained(this)), |
| 222 this, | 220 this, |
| 223 main_task_runner_, | 221 main_task_runner_, |
| 224 io_thread.message_loop_proxy())); | 222 io_task_runner)); |
| 225 | 223 |
| 226 #endif // !defined(REMOTING_MULTI_PROCESS) | 224 #endif // !defined(REMOTING_MULTI_PROCESS) |
| 225 } |
| 226 |
| 227 void HostService::RunMessageLoop(MessageLoop* message_loop) { |
| 228 // Launch the I/O thread. |
| 229 base::Thread io_thread(kIoThreadName); |
| 230 base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); |
| 231 if (!io_thread.StartWithOptions(io_thread_options)) { |
| 232 LOG(FATAL) << "Failed to start the I/O thread"; |
| 233 return; |
| 234 } |
| 235 |
| 236 CreateLauncher(new AutoThreadTaskRunner(io_thread.message_loop_proxy(), |
| 237 main_task_runner_)); |
| 227 | 238 |
| 228 // Run the service. | 239 // Run the service. |
| 229 message_loop->Run(); | 240 message_loop->Run(); |
| 230 | |
| 231 // Release the control handler. | |
| 232 stopped_event_.Signal(); | |
| 233 } | 241 } |
| 234 | 242 |
| 235 int HostService::Elevate() { | 243 int HostService::Elevate() { |
| 236 // Get the name of the binary to launch. | 244 // Get the name of the binary to launch. |
| 237 FilePath binary = | 245 FilePath binary = |
| 238 CommandLine::ForCurrentProcess()->GetSwitchValuePath(kElevateSwitchName); | 246 CommandLine::ForCurrentProcess()->GetSwitchValuePath(kElevateSwitchName); |
| 239 | 247 |
| 240 // Create the child process command line by copying known switches from our | 248 // Create the child process command line by copying known switches from our |
| 241 // command line. | 249 // command line. |
| 242 CommandLine command_line(CommandLine::NO_PROGRAM); | 250 CommandLine command_line(CommandLine::NO_PROGRAM); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 272 << "Failed to connect to the service control manager"; | 280 << "Failed to connect to the service control manager"; |
| 273 return kErrorExitCode; | 281 return kErrorExitCode; |
| 274 } | 282 } |
| 275 | 283 |
| 276 return kSuccessExitCode; | 284 return kSuccessExitCode; |
| 277 } | 285 } |
| 278 | 286 |
| 279 int HostService::RunInConsole() { | 287 int HostService::RunInConsole() { |
| 280 MessageLoop message_loop(MessageLoop::TYPE_UI); | 288 MessageLoop message_loop(MessageLoop::TYPE_UI); |
| 281 | 289 |
| 282 // Allow other threads to post to our message loop. | 290 // Keep a reference to the main message loop while it is used. Once the last |
| 283 main_task_runner_ = message_loop.message_loop_proxy(); | 291 // reference is dropped, QuitClosure() will be posted to the loop. |
| 292 main_task_runner_ = |
| 293 new AutoThreadTaskRunner(message_loop.message_loop_proxy(), |
| 294 base::Bind(&QuitMessageLoop, |
| 295 base::Unretained(&message_loop))); |
| 284 | 296 |
| 285 int result = kErrorExitCode; | 297 int result = kErrorExitCode; |
| 286 | 298 |
| 287 // Subscribe to Ctrl-C and other console events. | 299 // Subscribe to Ctrl-C and other console events. |
| 288 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { | 300 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { |
| 289 LOG_GETLASTERROR(ERROR) | 301 LOG_GETLASTERROR(ERROR) |
| 290 << "Failed to set console control handler"; | 302 << "Failed to set console control handler"; |
| 291 return result; | 303 return result; |
| 292 } | 304 } |
| 293 | 305 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 320 // session. | 332 // session. |
| 321 main_task_runner_->PostTask(FROM_HERE, base::Bind( | 333 main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 322 &HostService::OnSessionChange, base::Unretained(this))); | 334 &HostService::OnSessionChange, base::Unretained(this))); |
| 323 | 335 |
| 324 // Subscribe to session change notifications. | 336 // Subscribe to session change notifications. |
| 325 if (WTSRegisterSessionNotification(window, | 337 if (WTSRegisterSessionNotification(window, |
| 326 NOTIFY_FOR_ALL_SESSIONS) != FALSE) { | 338 NOTIFY_FOR_ALL_SESSIONS) != FALSE) { |
| 327 // Run the service. | 339 // Run the service. |
| 328 RunMessageLoop(&message_loop); | 340 RunMessageLoop(&message_loop); |
| 329 | 341 |
| 342 // Release the control handler. |
| 343 stopped_event_.Signal(); |
| 344 |
| 330 WTSUnRegisterSessionNotification(window); | 345 WTSUnRegisterSessionNotification(window); |
| 331 result = kSuccessExitCode; | 346 result = kSuccessExitCode; |
| 332 } | 347 } |
| 333 | 348 |
| 334 cleanup: | 349 cleanup: |
| 335 if (window != NULL) { | 350 if (window != NULL) { |
| 336 DestroyWindow(window); | 351 DestroyWindow(window); |
| 337 } | 352 } |
| 338 | 353 |
| 339 if (atom != 0) { | 354 if (atom != 0) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 368 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( | 383 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 369 &HostService::OnSessionChange, base::Unretained(self))); | 384 &HostService::OnSessionChange, base::Unretained(self))); |
| 370 return NO_ERROR; | 385 return NO_ERROR; |
| 371 | 386 |
| 372 default: | 387 default: |
| 373 return ERROR_CALL_NOT_IMPLEMENTED; | 388 return ERROR_CALL_NOT_IMPLEMENTED; |
| 374 } | 389 } |
| 375 } | 390 } |
| 376 | 391 |
| 377 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { | 392 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { |
| 378 MessageLoop message_loop; | 393 MessageLoop message_loop(MessageLoop::TYPE_DEFAULT); |
| 379 | 394 |
| 380 // Allow other threads to post to our message loop. | 395 // Keep a reference to the main message loop while it is used. Once the last |
| 396 // reference is dropped QuitClosure() will be posted to the loop. |
| 381 HostService* self = HostService::GetInstance(); | 397 HostService* self = HostService::GetInstance(); |
| 382 self->main_task_runner_ = message_loop.message_loop_proxy(); | 398 self->main_task_runner_ = |
| 399 new AutoThreadTaskRunner(message_loop.message_loop_proxy(), |
| 400 base::Bind(&QuitMessageLoop, |
| 401 base::Unretained(&message_loop))); |
| 383 | 402 |
| 384 // Register the service control handler. | 403 // Register the service control handler. |
| 385 self->service_status_handle_ = | 404 self->service_status_handle_ = |
| 386 RegisterServiceCtrlHandlerExW(kWindowsServiceName, | 405 RegisterServiceCtrlHandlerExW(kWindowsServiceName, |
| 387 &HostService::ServiceControlHandler, | 406 &HostService::ServiceControlHandler, |
| 388 self); | 407 self); |
| 389 if (self->service_status_handle_ == 0) { | 408 if (self->service_status_handle_ == 0) { |
| 390 LOG_GETLASTERROR(ERROR) | 409 LOG_GETLASTERROR(ERROR) |
| 391 << "Failed to register the service control handler"; | 410 << "Failed to register the service control handler"; |
| 392 return; | 411 return; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 409 } | 428 } |
| 410 | 429 |
| 411 // Post a dummy session change notification to peek up the current console | 430 // Post a dummy session change notification to peek up the current console |
| 412 // session. | 431 // session. |
| 413 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( | 432 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 414 &HostService::OnSessionChange, base::Unretained(self))); | 433 &HostService::OnSessionChange, base::Unretained(self))); |
| 415 | 434 |
| 416 // Run the service. | 435 // Run the service. |
| 417 self->RunMessageLoop(&message_loop); | 436 self->RunMessageLoop(&message_loop); |
| 418 | 437 |
| 438 // Release the control handler. |
| 439 self->stopped_event_.Signal(); |
| 440 |
| 419 // Tell SCM that the service is stopped. | 441 // Tell SCM that the service is stopped. |
| 420 service_status.dwCurrentState = SERVICE_STOPPED; | 442 service_status.dwCurrentState = SERVICE_STOPPED; |
| 421 service_status.dwControlsAccepted = 0; | 443 service_status.dwControlsAccepted = 0; |
| 422 | 444 |
| 423 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { | 445 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
| 424 LOG_GETLASTERROR(ERROR) | 446 LOG_GETLASTERROR(ERROR) |
| 425 << "Failed to report service status to the service control manager"; | 447 << "Failed to report service status to the service control manager"; |
| 426 return; | 448 return; |
| 427 } | 449 } |
| 428 } | 450 } |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 } | 502 } |
| 481 | 503 |
| 482 remoting::HostService* service = remoting::HostService::GetInstance(); | 504 remoting::HostService* service = remoting::HostService::GetInstance(); |
| 483 if (!service->InitWithCommandLine(command_line)) { | 505 if (!service->InitWithCommandLine(command_line)) { |
| 484 usage(command_line->GetProgram()); | 506 usage(command_line->GetProgram()); |
| 485 return kUsageExitCode; | 507 return kUsageExitCode; |
| 486 } | 508 } |
| 487 | 509 |
| 488 return service->Run(); | 510 return service->Run(); |
| 489 } | 511 } |
| OLD | NEW |