/* 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); }