This source file includes following definitions.
- GetInstance
- InitWithCommandLine
- Run
- AddWtsTerminalObserver
- RemoveWtsTerminalObserver
- weak_factory_
- OnSessionChange
- CreateLauncher
- RunAsService
- RunAsServiceImpl
- RunInConsole
- StopDaemonProcess
- HandleMessage
- ConsoleControlHandler
- ServiceControlHandler
- ServiceMain
- DaemonProcessMain
#include "remoting/host/win/host_service.h"
#include <sddl.h>
#include <windows.h>
#include <wtsapi32.h>
#include "base/base_paths.h"
#include "base/base_switches.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/message_loop/message_loop.h"
#include "base/run_loop.h"
#include "base/single_thread_task_runner.h"
#include "base/strings/utf_string_conversions.h"
#include "base/threading/thread.h"
#include "base/win/message_window.h"
#include "base/win/scoped_com_initializer.h"
#include "remoting/base/auto_thread.h"
#include "remoting/base/scoped_sc_handle_win.h"
#include "remoting/host/branding.h"
#include "remoting/host/daemon_process.h"
#include "remoting/host/host_exit_codes.h"
#include "remoting/host/logging.h"
#include "remoting/host/win/com_security.h"
#include "remoting/host/win/core_resource.h"
#include "remoting/host/win/wts_terminal_observer.h"
namespace remoting {
namespace {
const char kIoThreadName[] = "I/O thread";
const char kConsoleSwitchName[] = "console";
const wchar_t kComProcessSd[] =
SDDL_OWNER L":" SDDL_LOCAL_SYSTEM
SDDL_GROUP L":" SDDL_LOCAL_SYSTEM
SDDL_DACL L":"
SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_LOCAL_SYSTEM)
SDDL_ACE(SDDL_ACCESS_ALLOWED, SDDL_COM_EXECUTE_LOCAL, SDDL_LOCAL_SERVICE);
const wchar_t kComProcessMandatoryLabel[] =
SDDL_SACL L":"
SDDL_ACE(SDDL_MANDATORY_LABEL, SDDL_NO_EXECUTE_UP, SDDL_ML_MEDIUM);
}
HostService* HostService::GetInstance() {
return Singleton<HostService>::get();
}
bool HostService::InitWithCommandLine(const CommandLine* command_line) {
CommandLine::StringVector args = command_line->GetArgs();
if (!args.empty()) {
LOG(ERROR) << "No positional parameters expected.";
return false;
}
if (run_routine_ == &HostService::RunAsService &&
command_line->HasSwitch(kConsoleSwitchName)) {
run_routine_ = &HostService::RunInConsole;
}
return true;
}
int HostService::Run() {
return (this->*run_routine_)();
}
bool HostService::AddWtsTerminalObserver(const std::string& terminal_id,
WtsTerminalObserver* observer) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
RegisteredObserver registered_observer;
registered_observer.terminal_id = terminal_id;
registered_observer.session_id = kInvalidSessionId;
registered_observer.observer = observer;
bool session_id_found = false;
std::list<RegisteredObserver>::const_iterator i;
for (i = observers_.begin(); i != observers_.end(); ++i) {
if (i->terminal_id == terminal_id) {
registered_observer.session_id = i->session_id;
session_id_found = true;
}
if (i->observer == observer)
return false;
}
if (!session_id_found)
registered_observer.session_id = LookupSessionId(terminal_id);
observers_.push_back(registered_observer);
if (registered_observer.session_id != kInvalidSessionId) {
observer->OnSessionAttached(registered_observer.session_id);
}
return true;
}
void HostService::RemoveWtsTerminalObserver(WtsTerminalObserver* observer) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
std::list<RegisteredObserver>::const_iterator i;
for (i = observers_.begin(); i != observers_.end(); ++i) {
if (i->observer == observer) {
observers_.erase(i);
return;
}
}
}
HostService::HostService() :
run_routine_(&HostService::RunAsService),
service_status_handle_(0),
stopped_event_(true, false),
weak_factory_(this) {
}
HostService::~HostService() {
}
void HostService::OnSessionChange(uint32 event, uint32 session_id) {
DCHECK(main_task_runner_->BelongsToCurrentThread());
DCHECK_NE(session_id, kInvalidSessionId);
if (event != WTS_CONSOLE_CONNECT && event != WTS_CONSOLE_DISCONNECT &&
event != WTS_REMOTE_CONNECT && event != WTS_REMOTE_DISCONNECT) {
return;
}
std::string terminal_id;
bool attached = LookupTerminalId(session_id, &terminal_id);
std::list<RegisteredObserver>::iterator i = observers_.begin();
while (i != observers_.end()) {
std::list<RegisteredObserver>::iterator next = i;
++next;
if (i->session_id == session_id &&
(!attached || !(i->terminal_id == terminal_id))) {
i->session_id = kInvalidSessionId;
i->observer->OnSessionDetached();
i = next;
continue;
}
if (attached && i->terminal_id == terminal_id &&
i->session_id != session_id) {
WtsTerminalObserver* observer = i->observer;
if (i->session_id != kInvalidSessionId) {
i->session_id = kInvalidSessionId;
i->observer->OnSessionDetached();
}
std::list<RegisteredObserver>::iterator j = next;
--j;
if (j->observer == observer) {
j->session_id = session_id;
observer->OnSessionAttached(session_id);
}
}
i = next;
}
}
void HostService::CreateLauncher(
scoped_refptr<AutoThreadTaskRunner> task_runner) {
scoped_refptr<AutoThreadTaskRunner> io_task_runner =
AutoThread::CreateWithType(
kIoThreadName, task_runner, base::MessageLoop::TYPE_IO);
if (!io_task_runner) {
LOG(FATAL) << "Failed to start the I/O thread";
return;
}
daemon_process_ = DaemonProcess::Create(
task_runner,
io_task_runner,
base::Bind(&HostService::StopDaemonProcess, weak_ptr_));
}
int HostService::RunAsService() {
SERVICE_TABLE_ENTRYW dispatch_table[] = {
{ const_cast<LPWSTR>(kWindowsServiceName), &HostService::ServiceMain },
{ NULL, NULL }
};
if (!StartServiceCtrlDispatcherW(dispatch_table)) {
LOG_GETLASTERROR(ERROR)
<< "Failed to connect to the service control manager";
return kInitializationFailed;
}
stopped_event_.Wait();
return kSuccessExitCode;
}
void HostService::RunAsServiceImpl() {
base::MessageLoopForUI message_loop;
base::RunLoop run_loop;
main_task_runner_ = message_loop.message_loop_proxy();
weak_ptr_ = weak_factory_.GetWeakPtr();
service_status_handle_ = RegisterServiceCtrlHandlerExW(
kWindowsServiceName, &HostService::ServiceControlHandler, this);
if (service_status_handle_ == 0) {
LOG_GETLASTERROR(ERROR)
<< "Failed to register the service control handler";
return;
}
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(service_status_handle_, &service_status)) {
LOG_GETLASTERROR(ERROR)
<< "Failed to report service status to the service control manager";
return;
}
base::win::ScopedCOMInitializer com_initializer;
if (!com_initializer.succeeded())
return;
if (!InitializeComSecurity(base::WideToUTF8(kComProcessSd),
base::WideToUTF8(kComProcessMandatoryLabel),
false)) {
return;
}
CreateLauncher(scoped_refptr<AutoThreadTaskRunner>(
new AutoThreadTaskRunner(main_task_runner_,
run_loop.QuitClosure())));
run_loop.Run();
weak_factory_.InvalidateWeakPtrs();
service_status.dwCurrentState = SERVICE_STOPPED;
service_status.dwControlsAccepted = 0;
if (!SetServiceStatus(service_status_handle_, &service_status)) {
LOG_GETLASTERROR(ERROR)
<< "Failed to report service status to the service control manager";
return;
}
}
int HostService::RunInConsole() {
base::MessageLoopForUI message_loop;
base::RunLoop run_loop;
main_task_runner_ = message_loop.message_loop_proxy();
weak_ptr_ = weak_factory_.GetWeakPtr();
int result = kInitializationFailed;
base::win::ScopedCOMInitializer com_initializer;
if (!com_initializer.succeeded())
return result;
if (!InitializeComSecurity(base::WideToUTF8(kComProcessSd),
base::WideToUTF8(kComProcessMandatoryLabel),
false)) {
return result;
}
if (!SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, TRUE)) {
LOG_GETLASTERROR(ERROR)
<< "Failed to set console control handler";
return result;
}
base::win::MessageWindow window;
if (!window.Create(base::Bind(&HostService::HandleMessage,
base::Unretained(this)))) {
LOG_GETLASTERROR(ERROR)
<< "Failed to create the session notification window";
goto cleanup;
}
if (WTSRegisterSessionNotification(window.hwnd(),
NOTIFY_FOR_ALL_SESSIONS) != FALSE) {
CreateLauncher(scoped_refptr<AutoThreadTaskRunner>(
new AutoThreadTaskRunner(main_task_runner_,
run_loop.QuitClosure())));
run_loop.Run();
stopped_event_.Signal();
WTSUnRegisterSessionNotification(window.hwnd());
result = kSuccessExitCode;
}
cleanup:
weak_factory_.InvalidateWeakPtrs();
SetConsoleCtrlHandler(&HostService::ConsoleControlHandler, FALSE);
return result;
}
void HostService::StopDaemonProcess() {
DCHECK(main_task_runner_->BelongsToCurrentThread());
daemon_process_.reset();
}
bool HostService::HandleMessage(
UINT message, WPARAM wparam, LPARAM lparam, LRESULT* result) {
if (message == WM_WTSSESSION_CHANGE) {
OnSessionChange(wparam, lparam);
*result = 0;
return true;
}
return false;
}
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->main_task_runner_->PostTask(
FROM_HERE, base::Bind(&HostService::StopDaemonProcess,
self->weak_ptr_));
return TRUE;
default:
return FALSE;
}
}
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->main_task_runner_->PostTask(
FROM_HERE, base::Bind(&HostService::StopDaemonProcess,
self->weak_ptr_));
return NO_ERROR;
case SERVICE_CONTROL_SESSIONCHANGE:
self->main_task_runner_->PostTask(FROM_HERE, base::Bind(
&HostService::OnSessionChange, self->weak_ptr_, event_type,
reinterpret_cast<WTSSESSION_NOTIFICATION*>(event_data)->dwSessionId));
return NO_ERROR;
default:
return ERROR_CALL_NOT_IMPLEMENTED;
}
}
VOID WINAPI HostService::ServiceMain(DWORD argc, WCHAR* argv[]) {
HostService* self = HostService::GetInstance();
self->RunAsServiceImpl();
self->stopped_event_.Signal();
}
int DaemonProcessMain() {
HostService* service = HostService::GetInstance();
if (!service->InitWithCommandLine(CommandLine::ForCurrentProcess())) {
return kUsageExitCode;
}
return service->Run();
}
}