diff options
-rw-r--r-- | remoting/host/remoting_host_service_win.cc | 148 | ||||
-rw-r--r-- | remoting/host/remoting_host_service_win.h | 34 | ||||
-rw-r--r-- | remoting/host/wts_console_monitor_win.h | 36 | ||||
-rw-r--r-- | remoting/host/wts_console_observer_win.h | 34 | ||||
-rw-r--r-- | remoting/host/wts_session_process_launcher_win.cc | 35 | ||||
-rw-r--r-- | remoting/host/wts_session_process_launcher_win.h | 38 | ||||
-rw-r--r-- | remoting/remoting.gyp | 11 |
7 files changed, 324 insertions, 12 deletions
diff --git a/remoting/host/remoting_host_service_win.cc b/remoting/host/remoting_host_service_win.cc index 68d9e05..e0ac078 100644 --- a/remoting/host/remoting_host_service_win.cc +++ b/remoting/host/remoting_host_service_win.cc @@ -8,6 +8,7 @@ #include "remoting/host/remoting_host_service_win.h" #include <windows.h> +#include <wtsapi32.h> #include <stdio.h> #include "base/at_exit.h" @@ -20,9 +21,12 @@ #include "base/path_service.h" #include "base/stringprintf.h" #include "base/utf_string_conversions.h" +#include "base/win/wrapped_window_proc.h" -#include "remoting/base/scoped_sc_handle_win.h" #include "remoting/host/remoting_host_service_resource.h" +#include "remoting/base/scoped_sc_handle_win.h" +#include "remoting/host/wts_console_observer_win.h" +#include "remoting/host/wts_session_process_launcher_win.h" using base::StringPrintf; @@ -36,6 +40,13 @@ const char kServiceDependencies[] = ""; const DWORD kServiceStopTimeoutMs = 30 * 1000; +// Session id that does not represent any session. +const uint32 kInvalidSession = 0xffffffff; + +// A window class for the session change notifications window. +static const char kSessionNotificationWindowClass[] = + "Chromoting_SessionNotificationWindow"; + // Command line actions and switches: // "run" sumply runs the service as usual. const char kRunActionName[] = "run"; @@ -80,16 +91,53 @@ void usage(const char* program_name) { namespace remoting { HostService::HostService() : + console_session_id_(kInvalidSession), + message_loop_(NULL), run_routine_(&HostService::RunAsService), service_name_(ASCIIToUTF16(kServiceName)), service_status_handle_(0), - stopped_event_(true, false), - message_loop_(NULL) { + shutting_down_(false), + stopped_event_(true, false) { } HostService::~HostService() { } +void HostService::AddWtsConsoleObserver(WtsConsoleObserver* observer) { + console_observers_.AddObserver(observer); +} + +void HostService::RemoveWtsConsoleObserver(WtsConsoleObserver* observer) { + console_observers_.RemoveObserver(observer); +} + +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 = kInvalidSession; + if (!shutting_down_) { + console_session_id = WTSGetActiveConsoleSessionId(); + } + if (console_session_id_ != console_session_id) { + if (console_session_id_ != kInvalidSession) { + FOR_EACH_OBSERVER(WtsConsoleObserver, + console_observers_, + OnSessionDetached()); + } + + console_session_id_ = console_session_id; + + if (console_session_id_ != kInvalidSession) { + FOR_EACH_OBSERVER(WtsConsoleObserver, + console_observers_, + OnSessionAttached(console_session_id_)); + } + } +} + BOOL WINAPI HostService::ConsoleControlHandler(DWORD event) { HostService* self = HostService::GetInstance(); switch (event) { @@ -274,9 +322,15 @@ int HostService::Run() { } void HostService::RunMessageLoop() { + WtsSessionProcessLauncher launcher(this); + // 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(); } @@ -297,20 +351,69 @@ int HostService::RunAsService() { } int HostService::RunInConsole() { - MessageLoop message_loop; + 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 kErrorExitCode; + return result; } - // Run the service. - RunMessageLoop(); + // Create a window for receiving session change notifications. + LPCWSTR atom = NULL; + HWND window = NULL; + HINSTANCE instance = GetModuleHandle(NULL); + string16 window_class = ASCIIToUTF16(kSessionNotificationWindowClass); + + WNDCLASSEX wc = {0}; + wc.cbSize = sizeof(wc); + wc.lpfnWndProc = base::win::WrappedWindowProc<SessionChangeNotificationProc>; + wc.hInstance = instance; + wc.lpszClassName = window_class.c_str(); + atom = reinterpret_cast<LPCWSTR>(RegisterClassExW(&wc)); + if (atom == NULL) { + LOG_GETLASTERROR(ERROR) + << "Failed to register the window class '" + << kSessionNotificationWindowClass << "'"; + goto cleanup; + } + + window = CreateWindowW(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(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 @@ -318,7 +421,7 @@ int HostService::RunInConsole() { SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE); message_loop_ = NULL; - return kSuccessExitCode; + return result; } DWORD WINAPI HostService::ServiceControlHandler(DWORD control, @@ -336,6 +439,11 @@ DWORD WINAPI HostService::ServiceControlHandler(DWORD control, 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; } @@ -365,7 +473,8 @@ VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { service_status.dwServiceType = SERVICE_WIN32_OWN_PROCESS; service_status.dwCurrentState = SERVICE_RUNNING; service_status.dwControlsAccepted = SERVICE_ACCEPT_SHUTDOWN | - SERVICE_ACCEPT_STOP; + SERVICE_ACCEPT_STOP | + SERVICE_ACCEPT_SESSIONCHANGE; service_status.dwWin32ExitCode = kSuccessExitCode; if (!SetServiceStatus(self->service_status_handle_, &service_status)) { @@ -374,6 +483,11 @@ VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { 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(); @@ -390,6 +504,22 @@ VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) { 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) { diff --git a/remoting/host/remoting_host_service_win.h b/remoting/host/remoting_host_service_win.h index e1e59b2..0317f93 100644 --- a/remoting/host/remoting_host_service_win.h +++ b/remoting/host/remoting_host_service_win.h @@ -8,15 +8,20 @@ #include <windows.h> #include "base/memory/singleton.h" +#include "base/observer_list.h" #include "base/string16.h" #include "base/synchronization/waitable_event.h" +#include "remoting/host/wts_console_monitor_win.h" + class CommandLine; class MessageLoop; namespace remoting { -class HostService { +class WtsConsoleObserver; + +class HostService : public WtsConsoleMonitor { public: static HostService* GetInstance(); @@ -26,10 +31,18 @@ class HostService { // Invoke the choosen action routine. int Run(); + // WtsConsoleMonitor implementation + virtual void AddWtsConsoleObserver(WtsConsoleObserver* observer) OVERRIDE; + virtual void RemoveWtsConsoleObserver( + WtsConsoleObserver* observer) OVERRIDE; + private: HostService(); ~HostService(); + // Notifies the service of changes in session state. + void OnSessionChange(); + // This routine registers the service with the service control manager. int Install(); @@ -60,6 +73,21 @@ class HostService { // The main service entry point. static VOID WINAPI ServiceMain(DWORD argc, WCHAR* argv[]); + static LRESULT CALLBACK SessionChangeNotificationProc(HWND hwnd, + UINT message, + WPARAM wparam, + LPARAM lparam); + + // Current physical console session id. + uint32 console_session_id_; + + // The list of observers receiving notifications about any session attached + // to the physical console. + ObserverList<WtsConsoleObserver> console_observers_; + + // Service message loop. + MessageLoop* message_loop_; + // The action routine to be executed. int (HostService::*run_routine_)(); @@ -72,8 +100,8 @@ class HostService { // The service status handle. SERVICE_STATUS_HANDLE service_status_handle_; - // Service message loop. - MessageLoop* message_loop_; + // True if the service is being stopped. + bool shutting_down_; // A waitable event that is used to wait until the service is stopped. base::WaitableEvent stopped_event_; diff --git a/remoting/host/wts_console_monitor_win.h b/remoting/host/wts_console_monitor_win.h new file mode 100644 index 0000000..10a24da --- /dev/null +++ b/remoting/host/wts_console_monitor_win.h @@ -0,0 +1,36 @@ +// 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. + +#ifndef REMOTING_HOST_WTS_CONSOLE_MONITOR_WIN_H_ +#define REMOTING_HOST_WTS_CONSOLE_MONITOR_WIN_H_ + +#include <windows.h> + +#include "base/basictypes.h" + +namespace remoting { + +class WtsConsoleObserver; + +class WtsConsoleMonitor { + public: + virtual ~WtsConsoleMonitor() {} + + // Registers an observer to receive notifications about a particular WTS + // console (i.e. the physical console or a remote console). + virtual void AddWtsConsoleObserver(WtsConsoleObserver* observer) = 0; + + // Unregisters a previously registered observer. + virtual void RemoveWtsConsoleObserver(WtsConsoleObserver* observer) = 0; + + protected: + WtsConsoleMonitor() {} + + private: + DISALLOW_COPY_AND_ASSIGN(WtsConsoleMonitor); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_WTS_CONSOLE_MONITOR_WIN_H_ diff --git a/remoting/host/wts_console_observer_win.h b/remoting/host/wts_console_observer_win.h new file mode 100644 index 0000000..7f4d8475 --- /dev/null +++ b/remoting/host/wts_console_observer_win.h @@ -0,0 +1,34 @@ +// 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. + +#ifndef REMOTING_HOST_wts_console_observer_win_H_ +#define REMOTING_HOST_wts_console_observer_win_H_ + +#include <windows.h> + +#include "base/basictypes.h" + +namespace remoting { + +// Provides callbacks for monitoring events on a WTS terminal. +class WtsConsoleObserver { + public: + virtual ~WtsConsoleObserver() {} + + // Called when |session_id| attaches to the console. + virtual void OnSessionAttached(uint32 session_id) = 0; + + // Called when a session detaches from the console. + virtual void OnSessionDetached() = 0; + + protected: + WtsConsoleObserver() {} + + private: + DISALLOW_COPY_AND_ASSIGN(WtsConsoleObserver); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_wts_console_observer_win_H_ diff --git a/remoting/host/wts_session_process_launcher_win.cc b/remoting/host/wts_session_process_launcher_win.cc new file mode 100644 index 0000000..119b564 --- /dev/null +++ b/remoting/host/wts_session_process_launcher_win.cc @@ -0,0 +1,35 @@ +// 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/wts_session_process_launcher_win.h" + +#include <windows.h> + +#include "base/logging.h" + +#include "remoting/host/wts_console_monitor_win.h" + +namespace remoting { + +WtsSessionProcessLauncher::WtsSessionProcessLauncher( + WtsConsoleMonitor* monitor) : monitor_(monitor) { + monitor_->AddWtsConsoleObserver(this); +} + +WtsSessionProcessLauncher::~WtsSessionProcessLauncher() { + monitor_->RemoveWtsConsoleObserver(this); +} + +void WtsSessionProcessLauncher::OnSessionAttached(uint32 session_id) { + // TODO(alexeypa): The code injecting the host process to a session goes here. +} + +void WtsSessionProcessLauncher::OnSessionDetached() { + // TODO(alexeypa): The code terminating the host process goes here. +} + +} // namespace remoting diff --git a/remoting/host/wts_session_process_launcher_win.h b/remoting/host/wts_session_process_launcher_win.h new file mode 100644 index 0000000..b1c9104 --- /dev/null +++ b/remoting/host/wts_session_process_launcher_win.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef REMOTING_HOST_WTS_SESSION_PROCESS_LAUNCHER_WIN_H_ +#define REMOTING_HOST_WTS_SESSION_PROCESS_LAUNCHER_WIN_H_ + +#include <windows.h> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" + +#include "remoting/host/wts_console_observer_win.h" + +namespace remoting { + +class WtsConsoleMonitor; + +class WtsSessionProcessLauncher : public WtsConsoleObserver { + public: + // Constructs a WtsSessionProcessLauncher object. |monitor| must outlive this + // class. + WtsSessionProcessLauncher(WtsConsoleMonitor* monitor); + virtual ~WtsSessionProcessLauncher(); + + // WtsConsoleObserver implementation + virtual void OnSessionAttached(uint32 session_id) OVERRIDE; + virtual void OnSessionDetached() OVERRIDE; + + private: + WtsConsoleMonitor* monitor_; + + DISALLOW_COPY_AND_ASSIGN(WtsSessionProcessLauncher); +}; + +} // namespace remoting + +#endif // REMOTING_HOST_WTS_SESSION_PROCESS_LAUNCHER_WIN_H_ diff --git a/remoting/remoting.gyp b/remoting/remoting.gyp index 0ac2b50..6207cc7 100644 --- a/remoting/remoting.gyp +++ b/remoting/remoting.gyp @@ -236,7 +236,18 @@ 'host/remoting_host_service_resource.h', 'host/remoting_host_service_win.cc', 'host/remoting_host_service_win.h', + 'host/wts_console_monitor_win.h', + 'host/wts_console_observer_win.h', + 'host/wts_session_process_launcher_win.cc', + 'host/wts_session_process_launcher_win.h', ], + 'msvs_settings': { + 'VCLinkerTool': { + 'AdditionalDependencies': [ + 'wtsapi32.lib', + ], + }, + }, }, # end of target 'remoting_service' ], }], # 'OS=="win"' |