/*
Copyright (C) 2009 Red Hat, Inc.
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License as
published by the Free Software Foundation; either version 2 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
*/
#include
#include
#include
#include
#include
#include
#include "vdcommon.h"
//#define DEBUG_VDSERVICE
#define VD_SERVICE_DISPLAY_NAME TEXT("RHEV Spice Agent")
#define VD_SERVICE_NAME TEXT("vdservice")
#define VD_SERVICE_DESCRIPTION TEXT("Enables Spice event injection and display configuration.")
#define VD_SERVICE_LOG_PATH TEXT("%svdservice.log")
#define VD_SERVICE_LOAD_ORDER_GROUP TEXT("Pointer Port")
#define VD_AGENT_PATH TEXT("%s\\vdagent.exe")
#define VD_AGENT_TIMEOUT 10000
#define VD_AGENT_MAX_RESTARTS 10
#define VD_AGENT_RESTART_INTERVAL 3000
#define VD_AGENT_RESTART_COUNT_RESET_INTERVAL 60000
#define WINLOGON_FILENAME TEXT("winlogon.exe")
#define CREATE_PROC_MAX_RETRIES 10
#define CREATE_PROC_INTERVAL_MS 500
// This enum simplifies WaitForMultipleEvents for static
// events, that is handles that are guranteed non NULL.
// It doesn't include:
// VDAgent handle - this can be 1 or 0 (NULL or not), so it is also added at
// the end of VDService::_events
enum {
VD_EVENT_CONTROL = 0,
VD_STATIC_EVENTS_COUNT // Must be last
};
enum {
VD_CONTROL_IDLE = 0,
VD_CONTROL_STOP,
VD_CONTROL_RESTART_AGENT,
};
typedef std::queue VDControlQueue;
class VDService {
public:
static VDService* get();
~VDService();
bool run();
bool install();
bool uninstall();
private:
VDService();
bool execute();
void stop();
static DWORD WINAPI control_handler(DWORD control, DWORD event_type,
LPVOID event_data, LPVOID context);
static VOID WINAPI main(DWORD argc, TCHAR * argv[]);
void set_control_event(int control_command);
void handle_control_event();
bool restart_agent(bool normal_restart);
bool launch_agent();
bool kill_agent();
unsigned fill_agent_event() {
ASSERT(_events);
if (_agent_proc_info.hProcess) {
_events[_events_count - 1] = _agent_proc_info.hProcess;
return _events_count;
} else {
return _events_count - 1;
}
}
private:
static VDService* _singleton;
SERVICE_STATUS _status;
SERVICE_STATUS_HANDLE _status_handle;
PROCESS_INFORMATION _agent_proc_info;
HANDLE _control_event;
HANDLE _agent_stop_event;
HANDLE* _events;
TCHAR _agent_path[MAX_PATH];
VDControlQueue _control_queue;
mutex_t _control_mutex;
mutex_t _agent_mutex;
uint32_t _connection_id;
DWORD _session_id;
DWORD _last_agent_restart_time;
int _agent_restarts;
int _system_version;
bool _agent_alive;
bool _running;
VDLog* _log;
unsigned _events_count;
};
VDService* VDService::_singleton = NULL;
VDService* VDService::get()
{
if (!_singleton) {
_singleton = new VDService();
}
return (VDService*)_singleton;
}
VDService::VDService()
: _status_handle (0)
, _events (NULL)
, _connection_id (0)
, _session_id (0)
, _last_agent_restart_time (0)
, _agent_restarts (0)
, _agent_alive (false)
, _running (false)
, _log (NULL)
, _events_count(0)
{
ZeroMemory(&_agent_proc_info, sizeof(_agent_proc_info));
_system_version = supported_system_version();
_control_event = CreateEvent(NULL, FALSE, FALSE, NULL);
_agent_stop_event = CreateEvent(NULL, FALSE, FALSE, VD_AGENT_STOP_EVENT);
_agent_path[0] = wchar_t('\0');
MUTEX_INIT(_agent_mutex);
MUTEX_INIT(_control_mutex);
_singleton = this;
}
VDService::~VDService()
{
CloseHandle(_agent_stop_event);
CloseHandle(_control_event);
delete _events;
delete _log;
}
bool VDService::run()
{
#ifndef DEBUG_VDSERVICE
SERVICE_TABLE_ENTRY service_table[] = {
{const_cast(VD_SERVICE_NAME), main}, {0, 0}};
return !!StartServiceCtrlDispatcher(service_table);
#else
main(0, NULL);
return true;
#endif
}
bool VDService::install()
{
bool ret = false;
SC_HANDLE service_control_manager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE);
if (!service_control_manager) {
printf("OpenSCManager failed\n");
return false;
}
TCHAR path[_MAX_PATH + 2];
DWORD len = GetModuleFileName(0, path + 1, _MAX_PATH);
if (len == 0 || len == _MAX_PATH) {
printf("GetModuleFileName failed\n");
CloseServiceHandle(service_control_manager);
return false;
}
// add quotes for the case path contains a space (see CreateService doc)
path[0] = path[len + 1] = TEXT('\"');
path[len + 2] = 0;
SC_HANDLE service = CreateService(service_control_manager, VD_SERVICE_NAME,
VD_SERVICE_DISPLAY_NAME, SERVICE_ALL_ACCESS,
SERVICE_WIN32_OWN_PROCESS, SERVICE_AUTO_START,
SERVICE_ERROR_IGNORE, path, VD_SERVICE_LOAD_ORDER_GROUP,
0, 0, 0, 0);
if (service) {
SERVICE_DESCRIPTION descr;
descr.lpDescription = const_cast(VD_SERVICE_DESCRIPTION);
if (!ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &descr)) {
printf("ChangeServiceConfig2 failed\n");
}
CloseServiceHandle(service);
printf("Service installed successfully\n");
ret = true;
} else if (GetLastError() == ERROR_SERVICE_EXISTS) {
printf("Service already exists\n");
ret = true;
} else {
printf("Service not installed successfully, error %lu\n", GetLastError());
}
CloseServiceHandle(service_control_manager);
return ret;
}
bool VDService::uninstall()
{
bool ret = false;
SC_HANDLE service_control_manager = OpenSCManager(0, 0, SC_MANAGER_CONNECT);
if (!service_control_manager) {
printf("OpenSCManager failed\n");
return false;
}
SC_HANDLE service = OpenService(service_control_manager, VD_SERVICE_NAME,
SERVICE_QUERY_STATUS | DELETE);
if (!service) {
printf("OpenService failed\n");
CloseServiceHandle(service_control_manager);
return false;
}
SERVICE_STATUS status;
if (!QueryServiceStatus(service, &status)) {
printf("QueryServiceStatus failed\n");
} else if (status.dwCurrentState != SERVICE_STOPPED) {
printf("Service is still running\n");
} else if (DeleteService(service)) {
printf("Service removed successfully\n");
ret = true;
} else {
switch (GetLastError()) {
case ERROR_ACCESS_DENIED:
printf("Access denied while trying to remove service\n");
break;
case ERROR_INVALID_HANDLE:
printf("Handle invalid while trying to remove service\n");
break;
case ERROR_SERVICE_MARKED_FOR_DELETE:
printf("Service already marked for deletion\n");
break;
}
}
CloseServiceHandle(service);
CloseServiceHandle(service_control_manager);
return ret;
}
const char* session_events[] = {
"INVALID", "CONNECT", "DISCONNECT", "REMOTE_CONNECT", "REMOTE_DISCONNECT", "LOGON", "LOGOFF",
"LOCK", "UNLOCK", "REMOTE_CONTROL"
};
void VDService::set_control_event(int control_command)
{
MUTEX_LOCK(_control_mutex);
_control_queue.push(control_command);
if (_control_event && !SetEvent(_control_event)) {
vd_printf("SetEvent() failed: %lu", GetLastError());
}
MUTEX_UNLOCK(_control_mutex);
}
void VDService::handle_control_event()
{
MUTEX_LOCK(_control_mutex);
while (_control_queue.size()) {
int control_command = _control_queue.front();
_control_queue.pop();
switch (control_command) {
case VD_CONTROL_STOP:
_running = false;
break;
case VD_CONTROL_RESTART_AGENT:
_running = restart_agent(true);
break;
default:
vd_printf("Unsupported control command %u", control_command);
}
}
MUTEX_UNLOCK(_control_mutex);
}
DWORD WINAPI VDService::control_handler(DWORD control, DWORD event_type, LPVOID event_data,
LPVOID context)
{
VDService* s = _singleton;
DWORD ret = NO_ERROR;
ASSERT(s);
switch (control) {
case SERVICE_CONTROL_STOP:
case SERVICE_CONTROL_SHUTDOWN:
vd_printf("Stop service");
s->_status.dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(s->_status_handle, &s->_status);
s->stop();
break;
case SERVICE_CONTROL_INTERROGATE:
vd_printf("Interrogate service");
SetServiceStatus(s->_status_handle, &s->_status);
break;
case SERVICE_CONTROL_SESSIONCHANGE: {
DWORD session_id = ((WTSSESSION_NOTIFICATION*)event_data)->dwSessionId;
vd_printf("Session %lu %s", session_id, session_events[event_type]);
SetServiceStatus(s->_status_handle, &s->_status);
if (event_type == WTS_CONSOLE_CONNECT) {
s->_session_id = session_id;
s->set_control_event(VD_CONTROL_RESTART_AGENT);
}
break;
}
default:
vd_printf("Unsupported control %lu", control);
ret = ERROR_CALL_NOT_IMPLEMENTED;
}
return ret;
}
#define VDSERVICE_ACCEPTED_CONTROLS \
(SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_SHUTDOWN | SERVICE_ACCEPT_SESSIONCHANGE)
VOID WINAPI VDService::main(DWORD argc, TCHAR* argv[])
{
VDService* s = _singleton;
SERVICE_STATUS* status;
TCHAR log_path[MAX_PATH];
TCHAR full_path[MAX_PATH];
TCHAR temp_path[MAX_PATH];
TCHAR* slash;
ASSERT(s);
if (GetModuleFileName(NULL, full_path, MAX_PATH) && (slash = wcsrchr(full_path, TCHAR('\\'))) &&
GetTempPath(MAX_PATH, temp_path)) {
*slash = TCHAR('\0');
swprintf_s(s->_agent_path, MAX_PATH, VD_AGENT_PATH, full_path);
swprintf_s(log_path, MAX_PATH, VD_SERVICE_LOG_PATH, temp_path);
s->_log = VDLog::get(log_path);
}
vd_printf("***Service started***");
log_version();
if (!SetPriorityClass(GetCurrentProcess(), ABOVE_NORMAL_PRIORITY_CLASS)) {
vd_printf("SetPriorityClass failed %lu", GetLastError());
}
status = &s->_status;
status->dwServiceType = SERVICE_WIN32;
status->dwCurrentState = SERVICE_STOPPED;
status->dwControlsAccepted = 0;
status->dwWin32ExitCode = NO_ERROR;
status->dwServiceSpecificExitCode = NO_ERROR;
status->dwCheckPoint = 0;
status->dwWaitHint = 0;
#ifndef DEBUG_VDSERVICE
s->_status_handle = RegisterServiceCtrlHandlerEx(VD_SERVICE_NAME, &VDService::control_handler,
NULL);
if (!s->_status_handle) {
vd_printf("RegisterServiceCtrlHandler failed\n");
return;
}
// service is starting
status->dwCurrentState = SERVICE_START_PENDING;
SetServiceStatus(s->_status_handle, status);
// service running
status->dwControlsAccepted |= VDSERVICE_ACCEPTED_CONTROLS;
status->dwCurrentState = SERVICE_RUNNING;
SetServiceStatus(s->_status_handle, status);
#endif //DEBUG_VDSERVICE
s->_running = true;
s->execute();
#ifndef DEBUG_VDSERVICE
// service was stopped
status->dwCurrentState = SERVICE_STOP_PENDING;
SetServiceStatus(s->_status_handle, status);
// service is stopped
status->dwControlsAccepted &= ~VDSERVICE_ACCEPTED_CONTROLS;
status->dwCurrentState = SERVICE_STOPPED;
SetServiceStatus(s->_status_handle, status);
#endif //DEBUG_VDSERVICE
vd_printf("***Service stopped***");
}
bool VDService::execute()
{
INT* con_state = NULL;
bool con_state_active = false;
DWORD bytes;
_session_id = WTSGetActiveConsoleSessionId();
if (_session_id == 0xFFFFFFFF) {
vd_printf("WTSGetActiveConsoleSessionId() failed");
_running = false;
}
vd_printf("Active console session id: %lu", _session_id);
if (WTSQuerySessionInformation(WTS_CURRENT_SERVER_HANDLE, _session_id,
WTSConnectState, (LPTSTR *)&con_state, &bytes)) {
vd_printf("Connect state: %d", *con_state);
con_state_active = (*con_state == WTSActive);
WTSFreeMemory(con_state);
}
if (_running && !launch_agent()) {
// In case of agent launch failure: if connection state is not active(*), wait for agent
// launch on the next session connection. Otherwise, the service is stopped.
// (*) The failure was due to system startup timings and logon settings, causing the first
// agent instance lifetime (before session connect) to be too short to connect the service.
_running = !con_state_active && (GetLastError() != ERROR_FILE_NOT_FOUND);
if (_running) {
vd_printf("Failed launching vdagent instance, waiting for session connection");
}
while (_running) {
if (WaitForSingleObject(_control_event, INFINITE) == WAIT_OBJECT_0) {
handle_control_event();
}
}
}
if (!_running) {
return false;
}
_events_count = VD_STATIC_EVENTS_COUNT + 1 /*for agent*/;
_events = new HANDLE[_events_count];
ZeroMemory(_events, _events_count);
_events[VD_EVENT_CONTROL] = _control_event;
while (_running) {
unsigned actual_events = fill_agent_event();
DWORD wait_ret = WaitForMultipleObjects(actual_events, _events, FALSE, INFINITE);
switch (wait_ret) {
case WAIT_OBJECT_0 + VD_EVENT_CONTROL:
handle_control_event();
break;
case WAIT_OBJECT_0 + VD_STATIC_EVENTS_COUNT:
vd_printf("Agent killed");
if (_system_version == SYS_VER_WIN_XP_CLASS) {
restart_agent(false);
} else if (_system_version == SYS_VER_WIN_7_CLASS) {
kill_agent();
// Assume agent was killed due to console disconnect, and wait for agent
// normal restart due to console connect. If the agent is not alive yet,
// it was killed manually (or crashed), so let's restart it.
if (WaitForSingleObject(_control_event, VD_AGENT_RESTART_INTERVAL) ==
WAIT_OBJECT_0) {
handle_control_event();
}
if (_running && !_agent_alive) {
restart_agent(false);
}
}
break;
case WAIT_TIMEOUT:
break;
default:
vd_printf("WaitForMultipleObjects failed %lu", GetLastError());
_running = false;
}
}
kill_agent();
return true;
}
DWORD64 marshall_string(LPCWSTR str, DWORD max_size, LPBYTE* next_buf, DWORD* used_bytes)
{
DWORD offset = *used_bytes;
if (!str) {
return 0;
}
DWORD len = (DWORD)(wcslen(str) + 1) * sizeof(WCHAR);
if (*used_bytes + len > max_size) {
return 0;
}
memmove(*next_buf, str, len);
*used_bytes += len;
*next_buf += len;
return offset;
}
typedef struct CreateProcessParams {
DWORD size;
DWORD process_id;
BOOL use_default_token;
HANDLE token;
LPWSTR application_name;
LPWSTR command_line;
SECURITY_ATTRIBUTES process_attributes;
SECURITY_ATTRIBUTES thread_attributes;
BOOL inherit_handles;
DWORD creation_flags;
LPVOID environment;
LPWSTR current_directory;
STARTUPINFOW startup_info;
PROCESS_INFORMATION process_information;
BYTE data[0x2000];
} CreateProcessParams;
typedef struct CreateProcessRet {
DWORD size;
BOOL ret_value;
DWORD last_error;
PROCESS_INFORMATION process_information;
} CreateProcessRet;
BOOL create_session_process_as_user(IN DWORD session_id, IN BOOL use_default_token, IN HANDLE token,
IN LPCWSTR application_name, IN LPWSTR command_line,
IN LPSECURITY_ATTRIBUTES process_attributes,
IN LPSECURITY_ATTRIBUTES thread_attributes,
IN BOOL inherit_handles, IN DWORD creation_flags,
IN LPVOID environment, IN LPCWSTR current_directory,
IN LPSTARTUPINFOW startup_info,
OUT LPPROCESS_INFORMATION process_information)
{
WCHAR win_sta_path[MAX_PATH];
HINSTANCE win_sta_handle;
WCHAR pipe_name[MAX_PATH] = L"";
DWORD pipe_name_len;
BOOL got_pipe_name = FALSE;
HANDLE named_pipe;
CreateProcessRet proc_ret;
CreateProcessParams proc_params;
LPBYTE buffer = (LPBYTE)proc_params.data;
DWORD max_size = sizeof(proc_params);
DWORD bytes_used = offsetof(CreateProcessParams, data);
DWORD bytes_written;
DWORD bytes_read;
DWORD env_len = 0;
BOOL ret = FALSE;
GetSystemDirectoryW(win_sta_path, MAX_PATH);
lstrcatW(win_sta_path, L"\\winsta.dll");
win_sta_handle = LoadLibrary(win_sta_path);
if (win_sta_handle) {
PWINSTATIONQUERYINFORMATIONW win_sta_query_func =
(PWINSTATIONQUERYINFORMATIONW)GetProcAddress(win_sta_handle,
"WinStationQueryInformationW");
if (win_sta_query_func) {
got_pipe_name = win_sta_query_func(0, session_id, (WINSTATIONINFOCLASS)0x21,
pipe_name, sizeof(pipe_name), &pipe_name_len);
}
FreeLibrary(win_sta_handle);
}
if (!got_pipe_name || pipe_name[0] == '\0') {
swprintf_s(pipe_name, MAX_PATH, L"\\\\.\\Pipe\\TerminalServer\\SystemExecSrvr\\%d",
session_id);
}
do {
named_pipe = CreateFile(pipe_name, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING,
0, 0);
if (named_pipe == INVALID_HANDLE_VALUE) {
if (GetLastError() == ERROR_PIPE_BUSY) {
if (!WaitNamedPipe(pipe_name, 3000)) {
return FALSE;
}
} else {
return FALSE;
}
}
} while (named_pipe == INVALID_HANDLE_VALUE);
memset(&proc_params, 0, sizeof(proc_params));
proc_params.process_id = GetCurrentProcessId();
proc_params.use_default_token = use_default_token;
proc_params.token = token;
proc_params.application_name = (LPWSTR)marshall_string(application_name, max_size, &buffer,
&bytes_used);
proc_params.command_line = (LPWSTR)marshall_string(command_line, max_size, &buffer,
&bytes_used);
if (process_attributes) {
proc_params.process_attributes = *process_attributes;
}
if (thread_attributes) {
proc_params.thread_attributes = *thread_attributes;
}
proc_params.inherit_handles = inherit_handles;
proc_params.creation_flags = creation_flags;
proc_params.current_directory = (LPWSTR)marshall_string(current_directory, max_size,
&buffer, &bytes_used);
if (startup_info) {
proc_params.startup_info = *startup_info;
proc_params.startup_info.lpDesktop = (LPWSTR)marshall_string(startup_info->lpDesktop,
max_size, &buffer,
&bytes_used);
proc_params.startup_info.lpTitle = (LPWSTR)marshall_string(startup_info->lpTitle,
max_size, &buffer, &bytes_used);
}
if (environment) {
if (creation_flags & CREATE_UNICODE_ENVIRONMENT) {
while ((env_len + bytes_used <= max_size)) {
if (((LPWSTR)environment)[env_len / 2] == '\0' &&
((LPWSTR)environment)[env_len / 2 + 1] == '\0') {
env_len += 2 * sizeof(WCHAR);
break;
}
env_len += sizeof(WCHAR);
}
} else {
while (env_len + bytes_used <= max_size) {
if (((LPSTR)environment)[env_len] == '\0' &&
((LPSTR)environment)[env_len + 1] == '\0') {
env_len += 2;
break;
}
env_len++;
}
}
if (env_len + bytes_used <= max_size) {
memmove(buffer, environment, env_len);
proc_params.environment = (LPVOID)(UINT64)bytes_used;
buffer += env_len;
bytes_used += env_len;
} else {
proc_params.environment = NULL;
}
} else {
proc_params.environment = NULL;
}
proc_params.size = bytes_used;
if (WriteFile(named_pipe, &proc_params, proc_params.size, &bytes_written, NULL) &&
ReadFile(named_pipe, &proc_ret, sizeof(proc_ret), &bytes_read, NULL)) {
ret = proc_ret.ret_value;
if (ret) {
*process_information = proc_ret.process_information;
if (process_information->hProcess == 0) {
process_information->hProcess = OpenProcess(SYNCHRONIZE | PROCESS_TERMINATE, FALSE,
process_information->dwProcessId);
if (!process_information->hProcess) {
vd_printf("OpenProcess() failed %lu", GetLastError());
}
}
} else {
SetLastError(proc_ret.last_error);
}
} else {
ret = FALSE;
}
CloseHandle(named_pipe);
return ret;
}
BOOL create_process_as_user(IN DWORD session_id, IN LPCWSTR application_name,
IN LPWSTR command_line, IN LPSECURITY_ATTRIBUTES process_attributes,
IN LPSECURITY_ATTRIBUTES thread_attributes, IN BOOL inherit_handles,
IN DWORD creation_flags, IN LPVOID environment,
IN LPCWSTR current_directory, IN LPSTARTUPINFOW startup_info,
OUT LPPROCESS_INFORMATION process_information)
{
PROCESSENTRY32 proc_entry;
DWORD winlogon_pid = 0;
HANDLE winlogon_proc;
HANDLE token = NULL;
HANDLE token_dup;
BOOL ret = FALSE;
HANDLE snap = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0);
if (snap == INVALID_HANDLE_VALUE) {
vd_printf("CreateToolhelp32Snapshot() failed %lu", GetLastError());
return false;
}
ZeroMemory(&proc_entry, sizeof(proc_entry));
proc_entry.dwSize = sizeof(PROCESSENTRY32);
if (!Process32First(snap, &proc_entry)) {
vd_printf("Process32First() failed %lu", GetLastError());
CloseHandle(snap);
return false;
}
do {
if (_tcsicmp(proc_entry.szExeFile, WINLOGON_FILENAME) == 0) {
DWORD winlogon_session_id = 0;
if (ProcessIdToSessionId(proc_entry.th32ProcessID, &winlogon_session_id) &&
winlogon_session_id == session_id) {
winlogon_pid = proc_entry.th32ProcessID;
break;
}
}
} while (Process32Next(snap, &proc_entry));
CloseHandle(snap);
if (winlogon_pid == 0) {
vd_printf("Winlogon not found");
return false;
}
winlogon_proc = OpenProcess(PROCESS_QUERY_INFORMATION, FALSE, winlogon_pid);
if (!winlogon_proc) {
vd_printf("OpenProcess() failed %lu", GetLastError());
return false;
}
ret = OpenProcessToken(winlogon_proc, TOKEN_DUPLICATE, &token);
CloseHandle(winlogon_proc);
if (!ret) {
vd_printf("OpenProcessToken() failed %lu", GetLastError());
return false;
}
ret = DuplicateTokenEx(token, MAXIMUM_ALLOWED, NULL, SecurityIdentification, TokenPrimary,
&token_dup);
CloseHandle(token);
if (!ret) {
vd_printf("DuplicateTokenEx() failed %lu", GetLastError());
return false;
}
ret = CreateProcessAsUser(token_dup, application_name, command_line, process_attributes,
thread_attributes, inherit_handles, creation_flags, environment,
current_directory, startup_info, process_information);
CloseHandle(token_dup);
return ret;
}
bool VDService::launch_agent()
{
STARTUPINFO startup_info;
BOOL ret = FALSE;
ZeroMemory(&startup_info, sizeof(startup_info));
startup_info.cb = sizeof(startup_info);
startup_info.lpDesktop = const_cast(TEXT("Winsta0\\winlogon"));
ZeroMemory(&_agent_proc_info, sizeof(_agent_proc_info));
if (_system_version == SYS_VER_WIN_XP_CLASS) {
if (_session_id == 0) {
ret = CreateProcess(_agent_path, _agent_path, NULL, NULL, FALSE, 0, NULL, NULL,
&startup_info, &_agent_proc_info);
} else {
for (int i = 0; i < CREATE_PROC_MAX_RETRIES; i++) {
ret = create_session_process_as_user(_session_id, TRUE, NULL, NULL, _agent_path,
NULL, NULL, FALSE, 0, NULL, NULL,
&startup_info, &_agent_proc_info);
if (ret) {
vd_printf("create_session_process_as_user #%d", i);
break;
}
Sleep(CREATE_PROC_INTERVAL_MS);
}
}
} else if (_system_version == SYS_VER_WIN_7_CLASS) {
startup_info.lpDesktop = const_cast(TEXT("Winsta0\\default"));
ret = create_process_as_user(_session_id, _agent_path, _agent_path, NULL, NULL, FALSE, 0,
NULL, NULL, &startup_info, &_agent_proc_info);
} else {
vd_printf("Not supported in this system version");
return false;
}
if (!ret) {
vd_printf("CreateProcess() failed: %lu", GetLastError());
return false;
}
_agent_alive = true;
return true;
}
bool VDService::kill_agent()
{
DWORD exit_code = 0;
DWORD wait_ret;
HANDLE proc_handle;
bool ret = true;
if (!_agent_alive) {
return true;
}
_agent_alive = false;
proc_handle = _agent_proc_info.hProcess;
_agent_proc_info.hProcess = 0;
SetEvent(_agent_stop_event);
if (GetProcessId(proc_handle)) {
wait_ret = WaitForSingleObject(proc_handle, VD_AGENT_TIMEOUT);
switch (wait_ret) {
case WAIT_OBJECT_0:
if (GetExitCodeProcess(proc_handle, &exit_code)) {
ret = (exit_code != STILL_ACTIVE);
} else {
vd_printf("GetExitCodeProcess() failed: %lu", GetLastError());
}
break;
case WAIT_TIMEOUT:
vd_printf("Wait timeout");
ret = false;
break;
case WAIT_FAILED:
default:
vd_printf("WaitForSingleObject() failed: %lu", GetLastError());
break;
}
}
ResetEvent(_agent_stop_event);
CloseHandle(proc_handle);
CloseHandle(_agent_proc_info.hThread);
ZeroMemory(&_agent_proc_info, sizeof(_agent_proc_info));
return ret;
}
bool VDService::restart_agent(bool normal_restart)
{
DWORD time = GetTickCount();
bool ret = true;
MUTEX_LOCK(_agent_mutex);
if (!normal_restart && ++_agent_restarts > VD_AGENT_MAX_RESTARTS) {
vd_printf("Agent restarted too many times");
ret = false;
stop();
}
if (ret && kill_agent() && launch_agent()) {
if (time - _last_agent_restart_time > VD_AGENT_RESTART_COUNT_RESET_INTERVAL) {
_agent_restarts = 0;
}
_last_agent_restart_time = time;
ret = true;
}
MUTEX_UNLOCK(_agent_mutex);
return ret;
}
void VDService::stop()
{
vd_printf("Service stopped");
set_control_event(VD_CONTROL_STOP);
}
#ifdef __GNUC__
#undef _tmain
#ifdef UNICODE
int _tmain(int argc, TCHAR* argv[]);
int main(void)
{
int argc;
TCHAR** argv = CommandLineToArgvW(GetCommandLineW(), &argc);
return _tmain(argc, argv);
}
#else
#define _tmain main
#endif
#endif
int _tmain(int argc, TCHAR* argv[])
{
bool success = false;
if (!supported_system_version()) {
printf("vdservice is not supported in this system version\n");
return -1;
}
VDService* vdservice = VDService::get();
if (argc > 1) {
if (lstrcmpi(argv[1], TEXT("install")) == 0) {
success = vdservice->install();
} else if (lstrcmpi(argv[1], TEXT("uninstall")) == 0) {
success = vdservice->uninstall();
} else {
printf("Use: vdservice install / uninstall\n");
}
} else {
success = vdservice->run();
}
delete vdservice;
return (success ? 0 : -1);
}