| 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_message_loop.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 AutoMessageLoop(main_task_runner_, |
| 237 io_thread.message_loop_proxy(), |
| 238 base::Closure())); |
| 227 | 239 |
| 228 // Run the service. | 240 // Run the service. |
| 229 message_loop->Run(); | 241 message_loop->Run(); |
| 230 | |
| 231 // Release the control handler. | |
| 232 stopped_event_.Signal(); | |
| 233 } | 242 } |
| 234 | 243 |
| 235 int HostService::Elevate() { | 244 int HostService::Elevate() { |
| 236 // Get the name of the binary to launch. | 245 // Get the name of the binary to launch. |
| 237 FilePath binary = | 246 FilePath binary = |
| 238 CommandLine::ForCurrentProcess()->GetSwitchValuePath(kElevateSwitchName); | 247 CommandLine::ForCurrentProcess()->GetSwitchValuePath(kElevateSwitchName); |
| 239 | 248 |
| 240 // Create the child process command line by copying known switches from our | 249 // Create the child process command line by copying known switches from our |
| 241 // command line. | 250 // command line. |
| 242 CommandLine command_line(CommandLine::NO_PROGRAM); | 251 CommandLine command_line(CommandLine::NO_PROGRAM); |
| (...skipping 29 matching lines...) Expand all Loading... |
| 272 << "Failed to connect to the service control manager"; | 281 << "Failed to connect to the service control manager"; |
| 273 return kErrorExitCode; | 282 return kErrorExitCode; |
| 274 } | 283 } |
| 275 | 284 |
| 276 return kSuccessExitCode; | 285 return kSuccessExitCode; |
| 277 } | 286 } |
| 278 | 287 |
| 279 int HostService::RunInConsole() { | 288 int HostService::RunInConsole() { |
| 280 MessageLoop message_loop(MessageLoop::TYPE_UI); | 289 MessageLoop message_loop(MessageLoop::TYPE_UI); |
| 281 | 290 |
| 282 // Allow other threads to post to our message loop. | 291 // Keep a reference to the main message loop while it is used. Once the last |
| 283 main_task_runner_ = message_loop.message_loop_proxy(); | 292 // reference is dropped, QuitClosure() will be posted to the loop. |
| 293 main_task_runner_ = |
| 294 new AutoMessageLoop(NULL, message_loop.message_loop_proxy(), |
| 295 base::Bind(&QuitMessageLoop, |
| 296 base::Unretained(&message_loop))); |
| 284 | 297 |
| 285 int result = kErrorExitCode; | 298 int result = kErrorExitCode; |
| 286 | 299 |
| 287 // Subscribe to Ctrl-C and other console events. | 300 // Subscribe to Ctrl-C and other console events. |
| 288 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { | 301 if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { |
| 289 LOG_GETLASTERROR(ERROR) | 302 LOG_GETLASTERROR(ERROR) |
| 290 << "Failed to set console control handler"; | 303 << "Failed to set console control handler"; |
| 291 return result; | 304 return result; |
| 292 } | 305 } |
| 293 | 306 |
| (...skipping 26 matching lines...) Expand all Loading... |
| 320 // session. | 333 // session. |
| 321 main_task_runner_->PostTask(FROM_HERE, base::Bind( | 334 main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 322 &HostService::OnSessionChange, base::Unretained(this))); | 335 &HostService::OnSessionChange, base::Unretained(this))); |
| 323 | 336 |
| 324 // Subscribe to session change notifications. | 337 // Subscribe to session change notifications. |
| 325 if (WTSRegisterSessionNotification(window, | 338 if (WTSRegisterSessionNotification(window, |
| 326 NOTIFY_FOR_ALL_SESSIONS) != FALSE) { | 339 NOTIFY_FOR_ALL_SESSIONS) != FALSE) { |
| 327 // Run the service. | 340 // Run the service. |
| 328 RunMessageLoop(&message_loop); | 341 RunMessageLoop(&message_loop); |
| 329 | 342 |
| 343 // Release the control handler. |
| 344 stopped_event_.Signal(); |
| 345 |
| 330 WTSUnRegisterSessionNotification(window); | 346 WTSUnRegisterSessionNotification(window); |
| 331 result = kSuccessExitCode; | 347 result = kSuccessExitCode; |
| 332 } | 348 } |
| 333 | 349 |
| 334 cleanup: | 350 cleanup: |
| 335 if (window != NULL) { | 351 if (window != NULL) { |
| 336 DestroyWindow(window); | 352 DestroyWindow(window); |
| 337 } | 353 } |
| 338 | 354 |
| 339 if (atom != 0) { | 355 if (atom != 0) { |
| (...skipping 28 matching lines...) Expand all Loading... |
| 368 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( | 384 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 369 &HostService::OnSessionChange, base::Unretained(self))); | 385 &HostService::OnSessionChange, base::Unretained(self))); |
| 370 return NO_ERROR; | 386 return NO_ERROR; |
| 371 | 387 |
| 372 default: | 388 default: |
| 373 return ERROR_CALL_NOT_IMPLEMENTED; | 389 return ERROR_CALL_NOT_IMPLEMENTED; |
| 374 } | 390 } |
| 375 } | 391 } |
| 376 | 392 |
| 377 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { | 393 VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { |
| 378 MessageLoop message_loop; | 394 MessageLoop message_loop(MessageLoop::TYPE_DEFAULT); |
| 379 | 395 |
| 380 // Allow other threads to post to our message loop. | 396 // Keep a reference to the main message loop while it is used. Once the last |
| 397 // reference is dropped QuitClosure() will be posted to the loop. |
| 381 HostService* self = HostService::GetInstance(); | 398 HostService* self = HostService::GetInstance(); |
| 382 self->main_task_runner_ = message_loop.message_loop_proxy(); | 399 self->main_task_runner_ = |
| 400 new AutoMessageLoop(NULL, message_loop.message_loop_proxy(), |
| 401 base::Bind(&QuitMessageLoop, |
| 402 base::Unretained(&message_loop))); |
| 383 | 403 |
| 384 // Register the service control handler. | 404 // Register the service control handler. |
| 385 self->service_status_handle_ = | 405 self->service_status_handle_ = |
| 386 RegisterServiceCtrlHandlerExW(kWindowsServiceName, | 406 RegisterServiceCtrlHandlerExW(kWindowsServiceName, |
| 387 &HostService::ServiceControlHandler, | 407 &HostService::ServiceControlHandler, |
| 388 self); | 408 self); |
| 389 if (self->service_status_handle_ == 0) { | 409 if (self->service_status_handle_ == 0) { |
| 390 LOG_GETLASTERROR(ERROR) | 410 LOG_GETLASTERROR(ERROR) |
| 391 << "Failed to register the service control handler"; | 411 << "Failed to register the service control handler"; |
| 392 return; | 412 return; |
| (...skipping 16 matching lines...) Expand all Loading... |
| 409 } | 429 } |
| 410 | 430 |
| 411 // Post a dummy session change notification to peek up the current console | 431 // Post a dummy session change notification to peek up the current console |
| 412 // session. | 432 // session. |
| 413 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( | 433 self->main_task_runner_->PostTask(FROM_HERE, base::Bind( |
| 414 &HostService::OnSessionChange, base::Unretained(self))); | 434 &HostService::OnSessionChange, base::Unretained(self))); |
| 415 | 435 |
| 416 // Run the service. | 436 // Run the service. |
| 417 self->RunMessageLoop(&message_loop); | 437 self->RunMessageLoop(&message_loop); |
| 418 | 438 |
| 439 // Release the control handler. |
| 440 self->stopped_event_.Signal(); |
| 441 |
| 419 // Tell SCM that the service is stopped. | 442 // Tell SCM that the service is stopped. |
| 420 service_status.dwCurrentState = SERVICE_STOPPED; | 443 service_status.dwCurrentState = SERVICE_STOPPED; |
| 421 service_status.dwControlsAccepted = 0; | 444 service_status.dwControlsAccepted = 0; |
| 422 | 445 |
| 423 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { | 446 if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
| 424 LOG_GETLASTERROR(ERROR) | 447 LOG_GETLASTERROR(ERROR) |
| 425 << "Failed to report service status to the service control manager"; | 448 << "Failed to report service status to the service control manager"; |
| 426 return; | 449 return; |
| 427 } | 450 } |
| 428 } | 451 } |
| (...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 480 } | 503 } |
| 481 | 504 |
| 482 remoting::HostService* service = remoting::HostService::GetInstance(); | 505 remoting::HostService* service = remoting::HostService::GetInstance(); |
| 483 if (!service->InitWithCommandLine(command_line)) { | 506 if (!service->InitWithCommandLine(command_line)) { |
| 484 usage(command_line->GetProgram()); | 507 usage(command_line->GetProgram()); |
| 485 return kUsageExitCode; | 508 return kUsageExitCode; |
| 486 } | 509 } |
| 487 | 510 |
| 488 return service->Run(); | 511 return service->Run(); |
| 489 } | 512 } |
| OLD | NEW |