Index: remoting/host/host_service_win.cc |
=================================================================== |
--- remoting/host/host_service_win.cc (revision 149037) |
+++ remoting/host/host_service_win.cc (working copy) |
@@ -1,447 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
-// |
-// This file implements the Windows service controlling Me2Me host processes |
-// running within user sessions. |
- |
-#include "remoting/host/host_service_win.h" |
- |
-#include <windows.h> |
-#include <wtsapi32.h> |
-#include <stdio.h> |
- |
-#include "base/at_exit.h" |
-#include "base/base_paths.h" |
-#include "base/bind.h" |
-#include "base/command_line.h" |
-#include "base/file_util.h" |
-#include "base/logging.h" |
-#include "base/message_loop.h" |
-#include "base/path_service.h" |
-#include "base/stringprintf.h" |
-#include "base/threading/thread.h" |
-#include "base/utf_string_conversions.h" |
-#include "base/win/wrapped_window_proc.h" |
-#include "remoting/base/breakpad.h" |
-#include "remoting/base/scoped_sc_handle_win.h" |
-#include "remoting/host/branding.h" |
-#include "remoting/host/host_service_resource.h" |
-#include "remoting/host/usage_stats_consent.h" |
-#include "remoting/host/wts_console_observer_win.h" |
-#include "remoting/host/wts_session_process_launcher_win.h" |
- |
-using base::StringPrintf; |
- |
-namespace { |
- |
-const char kIoThreadName[] = "I/O thread"; |
- |
-// A window class for the session change notifications window. |
-const wchar_t kSessionNotificationWindowClass[] = |
- L"Chromoting_SessionNotificationWindow"; |
- |
-// Command line actions and switches: |
-// "run" sumply runs the service as usual. |
-const wchar_t kRunActionName[] = L"run"; |
- |
-// "--console" runs the service interactively for debugging purposes. |
-const char kConsoleSwitchName[] = "console"; |
- |
-// "--host-binary" specifies the host binary to run in console session. |
-const char kHostBinarySwitchName[] = "host-binary"; |
- |
-// "--help" or "--?" prints the usage message. |
-const char kHelpSwitchName[] = "help"; |
-const char kQuestionSwitchName[] = "?"; |
- |
-const char kUsageMessage[] = |
- "\n" |
- "Usage: %s [action] [options]\n" |
- "\n" |
- "Actions:\n" |
- " run - Run the service (default if no action was specified).\n" |
- "\n" |
- "Options:\n" |
- " --console - Run the service interactively for debugging purposes.\n" |
- " --host-binary - Specifies the host binary to run.\n" |
- " --help, --? - Print this message.\n"; |
- |
-// Exit codes: |
-const int kSuccessExitCode = 0; |
-const int kUsageExitCode = 1; |
-const int kErrorExitCode = 2; |
- |
-void usage(const char* program_name) { |
- fprintf(stderr, kUsageMessage, program_name); |
-} |
- |
-} // namespace |
- |
-namespace remoting { |
- |
-HostService::HostService() : |
- console_session_id_(kInvalidSessionId), |
- message_loop_(NULL), |
- run_routine_(&HostService::RunAsService), |
- service_name_(kWindowsServiceName), |
- service_status_handle_(0), |
- shutting_down_(false), |
- stopped_event_(true, false) { |
-} |
- |
-HostService::~HostService() { |
-} |
- |
-void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { |
- DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread()); |
- |
- console_observers_.AddObserver(observer); |
-} |
- |
-void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { |
- DCHECK(message_loop_->message_loop_proxy()->BelongsToCurrentThread()); |
- |
- console_observers_.RemoveObserver(observer); |
- |
- // Stop the service if there are no more observers. |
- if (!console_observers_.might_have_observers()) { |
- message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
- } |
-} |
- |
-void HostService::OnSessionChange() { |
- // WTSGetActiveConsoleSessionId is a very cheap API. It basically reads |
- // a single value from shared memory. Therefore it is better to check if |
- // the console session is still the same every time a session change |
- // notification event is posted. This also takes care of coalescing multiple |
- // events into one since we look at the latest state. |
- uint32 console_session_id = kInvalidSessionId; |
- if (!shutting_down_) { |
- console_session_id = WTSGetActiveConsoleSessionId(); |
- } |
- if (console_session_id_ != console_session_id) { |
- if (console_session_id_ != kInvalidSessionId) { |
- FOR_EACH_OBSERVER(WtsConsoleObserver, |
- console_observers_, |
- OnSessionDetached()); |
- } |
- |
- console_session_id_ = console_session_id; |
- |
- if (console_session_id_ != kInvalidSessionId) { |
- FOR_EACH_OBSERVER(WtsConsoleObserver, |
- console_observers_, |
- OnSessionAttached(console_session_id_)); |
- } |
- } |
-} |
- |
-BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) { |
- HostService* self = HostService::GetInstance(); |
- switch (event) { |
- case CTRL_C_EVENT: |
- case CTRL_BREAK_EVENT: |
- case CTRL_CLOSE_EVENT: |
- case CTRL_LOGOFF_EVENT: |
- case CTRL_SHUTDOWN_EVENT: |
- self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
- self->stopped_event_.Wait(); |
- return TRUE; |
- |
- default: |
- return FALSE; |
- } |
-} |
- |
-HostService* HostService::GetInstance() { |
- return Singleton<HostService>::get(); |
-} |
- |
-bool HostService::InitWithCommandLine(const CommandLine* command_line) { |
- CommandLine::StringVector args = command_line->GetArgs(); |
- |
- // Choose the action to perform. |
- if (!args.empty()) { |
- if (args.size() > 1) { |
- LOG(ERROR) << "Invalid command line: more than one action requested."; |
- return false; |
- } |
- if (args[0] != kRunActionName) { |
- LOG(ERROR) << "Invalid command line: invalid action specified: " |
- << args[0]; |
- return false; |
- } |
- } |
- |
- if (command_line->HasSwitch(kHostBinarySwitchName)) { |
- host_binary_ = command_line->GetSwitchValuePath(kHostBinarySwitchName); |
- } else { |
- LOG(ERROR) << "Invalid command line: --" << kHostBinarySwitchName |
- << " is required."; |
- return false; |
- } |
- |
- // Run interactively if needed. |
- if (run_routine_ == &HostService::RunAsService && |
- command_line->HasSwitch(kConsoleSwitchName)) { |
- run_routine_ = &HostService::RunInConsole; |
- } |
- |
- return true; |
-} |
- |
-int HostService::Run() { |
- return (this->*run_routine_)(); |
-} |
- |
-void HostService::RunMessageLoop() { |
- // Launch the I/O thread. |
- base::Thread io_thread(kIoThreadName); |
- base::Thread::Options io_thread_options(MessageLoop::TYPE_IO, 0); |
- if (!io_thread.StartWithOptions(io_thread_options)) { |
- LOG(FATAL) << "Failed to start the I/O thread"; |
- shutting_down_ = true; |
- stopped_event_.Signal(); |
- return; |
- } |
- |
- WtsSessionProcessLauncher launcher(this, host_binary_, |
- message_loop_->message_loop_proxy(), |
- io_thread.message_loop_proxy()); |
- |
- // Run the service. |
- message_loop_->Run(); |
- |
- // Clean up the observers by emulating detaching from the console. |
- shutting_down_ = true; |
- OnSessionChange(); |
- |
- // Release the control handler. |
- stopped_event_.Signal(); |
-} |
- |
-int HostService::RunAsService() { |
- SERVICE_TABLE_ENTRYW dispatch_table[] = { |
- { const_cast<LPWSTR>(service_name_.c_str()), &HostService::ServiceMain }, |
- { NULL, NULL } |
- }; |
- |
- if (!StartServiceCtrlDispatcherW(dispatch_table)) { |
- LOG_GETLASTERROR(ERROR) |
- << "Failed to connect to the service control manager"; |
- return kErrorExitCode; |
- } |
- |
- return kSuccessExitCode; |
-} |
- |
-int HostService::RunInConsole() { |
- MessageLoop message_loop(MessageLoop::TYPE_UI); |
- |
- // Allow other threads to post to our message loop. |
- message_loop_ = &message_loop; |
- |
- int result = kErrorExitCode; |
- |
- // Subscribe to Ctrl-C and other console events. |
- if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) { |
- LOG_GETLASTERROR(ERROR) |
- << "Failed to set console control handler"; |
- return result; |
- } |
- |
- // Create a window for receiving session change notifications. |
- HWND window = NULL; |
- WNDCLASSEX window_class; |
- base::win::InitializeWindowClass( |
- kSessionNotificationWindowClass, |
- &base::win::WrappedWindowProc<SessionChangeNotificationProc>, |
- 0, 0, 0, NULL, NULL, NULL, NULL, NULL, |
- &window_class); |
- HINSTANCE instance = window_class.hInstance; |
- ATOM atom = RegisterClassExW(&window_class); |
- if (atom == 0) { |
- LOG_GETLASTERROR(ERROR) |
- << "Failed to register the window class '" |
- << kSessionNotificationWindowClass << "'"; |
- goto cleanup; |
- } |
- |
- window = CreateWindowW(MAKEINTATOM(atom), 0, 0, 0, 0, 0, 0, HWND_MESSAGE, 0, |
- instance, 0); |
- if (window == NULL) { |
- LOG_GETLASTERROR(ERROR) |
- << "Failed to creat the session notificationwindow"; |
- goto cleanup; |
- } |
- |
- // Post a dummy session change notification to peek up the current console |
- // session. |
- message_loop.PostTask(FROM_HERE, base::Bind( |
- &HostService::OnSessionChange, base::Unretained(this))); |
- |
- // Subscribe to session change notifications. |
- if (WTSRegisterSessionNotification(window, |
- NOTIFY_FOR_ALL_SESSIONS) != FALSE) { |
- // Run the service. |
- RunMessageLoop(); |
- |
- WTSUnRegisterSessionNotification(window); |
- result = kSuccessExitCode; |
- } |
- |
-cleanup: |
- if (window != NULL) { |
- DestroyWindow(window); |
- } |
- |
- if (atom != 0) { |
- UnregisterClass(MAKEINTATOM(atom), instance); |
- } |
- |
- // Unsubscribe from console events. Ignore the exit code. There is nothing |
- // we can do about it now and the program is about to exit anyway. Even if |
- // it crashes nothing is going to be broken because of it. |
- SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE); |
- |
- message_loop_ = NULL; |
- return result; |
-} |
- |
-DWORD WINAPI HostService::ServiceControlHandler(DWORD control, |
- DWORD event_type, |
- LPVOID event_data, |
- LPVOID context) { |
- HostService* self = reinterpret_cast<HostService*>(context); |
- switch (control) { |
- case SERVICE_CONTROL_INTERROGATE: |
- return NO_ERROR; |
- |
- case SERVICE_CONTROL_SHUTDOWN: |
- case SERVICE_CONTROL_STOP: |
- self->message_loop_->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
- self->stopped_event_.Wait(); |
- return NO_ERROR; |
- |
- case SERVICE_CONTROL_SESSIONCHANGE: |
- self->message_loop_->PostTask(FROM_HERE, base::Bind( |
- &HostService::OnSessionChange, base::Unretained(self))); |
- return NO_ERROR; |
- |
- default: |
- return ERROR_CALL_NOT_IMPLEMENTED; |
- } |
-} |
- |
-VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { |
- MessageLoop message_loop; |
- |
- // Allow other threads to post to our message loop. |
- HostService* self = HostService::GetInstance(); |
- self->message_loop_ = &message_loop; |
- |
- // Register the service control handler. |
- self->service_status_handle_ = |
- RegisterServiceCtrlHandlerExW(self->service_name_.c_str(), |
- &HostService::ServiceControlHandler, |
- self); |
- if (self->service_status_handle_ == 0) { |
- LOG_GETLASTERROR(ERROR) |
- << "Failed to register the service control handler"; |
- return; |
- } |
- |
- // Report running status of the service. |
- SERVICE_STATUS service_status; |
- ZeroMemory(&service_status, sizeof(service_status)); |
- service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; |
- service_status.dwCurrentState = SERVICE_RUNNING; |
- service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | |
- SERVICE_ACCEPT_STOP | |
- SERVICE_ACCEPT_SESSIONCHANGE; |
- service_status.dwWin32ExitCode = kSuccessExitCode; |
- |
- if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
- LOG_GETLASTERROR(ERROR) |
- << "Failed to report service status to the service control manager"; |
- return; |
- } |
- |
- // Post a dummy session change notification to peek up the current console |
- // session. |
- message_loop.PostTask(FROM_HERE, base::Bind( |
- &HostService::OnSessionChange, base::Unretained(self))); |
- |
- // Run the service. |
- self->RunMessageLoop(); |
- |
- // Tell SCM that the service is stopped. |
- service_status.dwCurrentState = SERVICE_STOPPED; |
- service_status.dwControlsAccepted = 0; |
- |
- if (!SetServiceStatus(self->service_status_handle_, &service_status)) { |
- LOG_GETLASTERROR(ERROR) |
- << "Failed to report service status to the service control manager"; |
- return; |
- } |
- |
- self->message_loop_ = NULL; |
-} |
- |
-LRESULT CALLBACK HostService::SessionChangeNotificationProc(HWND hwnd, |
- UINT message, |
- WPARAM wparam, |
- LPARAM lparam) { |
- switch (message) { |
- case WM_WTSSESSION_CHANGE: { |
- HostService* self = HostService::GetInstance(); |
- self->OnSessionChange(); |
- return 0; |
- } |
- |
- default: |
- return DefWindowProc(hwnd, message, wparam, lparam); |
- } |
-} |
- |
-} // namespace remoting |
- |
-int main(int argc, char** argv) { |
-#ifdef OFFICIAL_BUILD |
- if (remoting::IsUsageStatsAllowed()) { |
- remoting::InitializeCrashReporting(); |
- } |
-#endif // OFFICIAL_BUILD |
- |
- CommandLine::Init(argc, argv); |
- |
- // This object instance is required by Chrome code (for example, |
- // FilePath, LazyInstance, MessageLoop). |
- base::AtExitManager exit_manager; |
- |
- // Write logs to the application profile directory. |
- FilePath debug_log = remoting::GetConfigDir(). |
- Append(FILE_PATH_LITERAL("debug.log")); |
- InitLogging(debug_log.value().c_str(), |
- logging::LOG_ONLY_TO_FILE, |
- logging::DONT_LOCK_LOG_FILE, |
- logging::APPEND_TO_OLD_LOG_FILE, |
- logging::DISABLE_DCHECK_FOR_NON_OFFICIAL_RELEASE_BUILDS); |
- |
- const CommandLine* command_line = CommandLine::ForCurrentProcess(); |
- |
- if (command_line->HasSwitch(kHelpSwitchName) || |
- command_line->HasSwitch(kQuestionSwitchName)) { |
- usage(argv[0]); |
- return kSuccessExitCode; |
- } |
- |
- remoting::HostService* service = remoting::HostService::GetInstance(); |
- if (!service->InitWithCommandLine(command_line)) { |
- usage(argv[0]); |
- return kUsageExitCode; |
- } |
- |
- return service->Run(); |
-} |