diff options
author | Arnon Gilboa <agilboa@redhat.com> | 2009-11-05 13:02:18 +0200 |
---|---|---|
committer | Yaniv Kamay <ykamay@redhat.com> | 2009-11-05 13:02:54 +0200 |
commit | 5ee7b5b27021ff68428f127648da728f45c68cd9 (patch) | |
tree | 0540e134bcfbac785546bb0d18573096fbffe667 |
fresh start
-rw-r--r-- | common/stdint.h | 394 | ||||
-rw-r--r-- | common/vdcommon.h | 58 | ||||
-rw-r--r-- | common/vdlog.cpp | 114 | ||||
-rw-r--r-- | common/vdlog.h | 60 | ||||
-rw-r--r-- | vdagent.sln | 26 | ||||
-rw-r--r-- | vdagent/desktop_layout.cpp | 195 | ||||
-rw-r--r-- | vdagent/desktop_layout.h | 84 | ||||
-rw-r--r-- | vdagent/resource.h | 14 | ||||
-rw-r--r-- | vdagent/vdagent.cpp | 598 | ||||
-rw-r--r-- | vdagent/vdagent.rc | 102 | ||||
-rw-r--r-- | vdagent/vdagent.vcproj | 230 | ||||
-rw-r--r-- | vdservice/resource.h | 14 | ||||
-rw-r--r-- | vdservice/vdi_port.cpp | 185 | ||||
-rw-r--r-- | vdservice/vdi_port.h | 68 | ||||
-rw-r--r-- | vdservice/vdservice.cpp | 947 | ||||
-rw-r--r-- | vdservice/vdservice.rc | 102 | ||||
-rw-r--r-- | vdservice/vdservice.vcproj | 229 |
17 files changed, 3420 insertions, 0 deletions
diff --git a/common/stdint.h b/common/stdint.h new file mode 100644 index 0000000..9316d56 --- /dev/null +++ b/common/stdint.h @@ -0,0 +1,394 @@ +/* ISO C9x 7.18 Integer types <stdint.h> + + * Based on ISO/IEC SC22/WG14 9899 Committee draft (SC22 N2794) + + * + + * THIS SOFTWARE IS NOT COPYRIGHTED + + * + + * Contributor: Danny Smith <danny_r_smith_2001@yahoo.co.nz> + + * + + * This source code is offered for use in the public domain. You may + + * use, modify or distribute it freely. + + * + + * This code is distributed in the hope that it will be useful but + + * WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY + + * DISCLAIMED. This includes but is not limited to warranties of + + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + + * + + * Date: 2000-12-02 + + */ + + + + + +#ifndef _STDINT_H + +#define _STDINT_H + +#define __need_wint_t + +#define __need_wchar_t + +#include <stddef.h> + + + +#ifdef _WIN32_WCE + +typedef _int64 int64_t; + +typedef unsigned _int64 uint64_t; + +#else + +typedef long long int64_t; + +typedef unsigned long long uint64_t; + +#endif /* _WIN32_WCE */ + + + +/* 7.18.1.1 Exact-width integer types */ + +typedef signed char int8_t; + +typedef unsigned char uint8_t; + +typedef short int16_t; + +typedef unsigned short uint16_t; + +typedef int int32_t; + +typedef unsigned uint32_t; + + + +/* 7.18.1.2 Minimum-width integer types */ + +typedef signed char int_least8_t; + +typedef unsigned char uint_least8_t; + +typedef short int_least16_t; + +typedef unsigned short uint_least16_t; + +typedef int int_least32_t; + +typedef unsigned uint_least32_t; + +#ifndef _WIN32_WCE + +typedef long long int_least64_t; + +typedef unsigned long long uint_least64_t; + +#endif + + + +/* 7.18.1.3 Fastest minimum-width integer types + + * Not actually guaranteed to be fastest for all purposes + + * Here we use the exact-width types for 8 and 16-bit ints. + + */ + +typedef char int_fast8_t; + +typedef unsigned char uint_fast8_t; + +typedef short int_fast16_t; + +typedef unsigned short uint_fast16_t; + +typedef int int_fast32_t; + +typedef unsigned int uint_fast32_t; + +#ifndef _WIN32_WCE + +typedef long long int_fast64_t; + +typedef unsigned long long uint_fast64_t; + +#endif + + + +/* 7.18.1.4 Integer types capable of holding object pointers */ + +typedef int intptr_t; + +typedef unsigned uintptr_t; + + + +/* 7.18.1.5 Greatest-width integer types */ + +#ifndef _WIN32_WCE + +typedef long long intmax_t; + +typedef unsigned long long uintmax_t; + +#endif + + + +/* 7.18.2 Limits of specified-width integer types */ + +#if !defined ( __cplusplus) || defined (__STDC_LIMIT_MACROS) + + + +/* 7.18.2.1 Limits of exact-width integer types */ + +#define INT8_MIN (-128) + +#define INT16_MIN (-32768) + +#define INT32_MIN (-2147483647 - 1) + +#define INT64_MIN (-9223372036854775807LL - 1) + + + +#define INT8_MAX 127 + +#define INT16_MAX 32767 + +#define INT32_MAX 2147483647 + +#define INT64_MAX 9223372036854775807LL + + + +#define UINT8_MAX 0xff /* 255U */ + +#define UINT16_MAX 0xffff /* 65535U */ + +#define UINT32_MAX 0xffffffff /* 4294967295U */ + +#define UINT64_MAX 0xffffffffffffffffULL /* 18446744073709551615ULL */ + + + +/* 7.18.2.2 Limits of minimum-width integer types */ + +#define INT_LEAST8_MIN INT8_MIN + +#define INT_LEAST16_MIN INT16_MIN + +#define INT_LEAST32_MIN INT32_MIN + +#define INT_LEAST64_MIN INT64_MIN + + + +#define INT_LEAST8_MAX INT8_MAX + +#define INT_LEAST16_MAX INT16_MAX + +#define INT_LEAST32_MAX INT32_MAX + +#define INT_LEAST64_MAX INT64_MAX + + + +#define UINT_LEAST8_MAX UINT8_MAX + +#define UINT_LEAST16_MAX UINT16_MAX + +#define UINT_LEAST32_MAX UINT32_MAX + +#define UINT_LEAST64_MAX UINT64_MAX + + + +/* 7.18.2.3 Limits of fastest minimum-width integer types */ + +#define INT_FAST8_MIN INT8_MIN + +#define INT_FAST16_MIN INT16_MIN + +#define INT_FAST32_MIN INT32_MIN + +#define INT_FAST64_MIN INT64_MIN + + + +#define INT_FAST8_MAX INT8_MAX + +#define INT_FAST16_MAX INT16_MAX + +#define INT_FAST32_MAX INT32_MAX + +#define INT_FAST64_MAX INT64_MAX + + + +#define UINT_FAST8_MAX UINT8_MAX + +#define UINT_FAST16_MAX UINT16_MAX + +#define UINT_FAST32_MAX UINT32_MAX + +#define UINT_FAST64_MAX UINT64_MAX + + + +/* 7.18.2.4 Limits of integer types capable of holding + + object pointers */ + +#define INTPTR_MIN INT32_MIN + +#define INTPTR_MAX INT32_MAX + +#define UINTPTR_MAX UINT32_MAX + + + +/* 7.18.2.5 Limits of greatest-width integer types */ + +#define INTMAX_MIN INT64_MIN + +#define INTMAX_MAX INT64_MAX + +#define UINTMAX_MAX UINT64_MAX + + + +/* 7.18.3 Limits of other integer types */ + +#define PTRDIFF_MIN INT32_MIN + +#define PTRDIFF_MAX INT32_MAX + + + +#define SIG_ATOMIC_MIN INT32_MIN + +#define SIG_ATOMIC_MAX INT32_MAX + + + +#define SIZE_MAX UINT32_MAX + + + +#ifndef WCHAR_MIN /* also in wchar.h */ + +#define WCHAR_MIN 0 + +#define WCHAR_MAX 0xffff /* UINT16_MAX */ + +#endif + + + +/* + + * wint_t is unsigned short for compatibility with MS runtime + + */ + +#define WINT_MIN 0 + +#define WINT_MAX 0xffff /* UINT16_MAX */ + + + +#endif /* !defined ( __cplusplus) || defined __STDC_LIMIT_MACROS */ + + + + + +/* 7.18.4 Macros for integer constants */ + +#if !defined ( __cplusplus) || defined (__STDC_CONSTANT_MACROS) + + + +/* 7.18.4.1 Macros for minimum-width integer constants + + + + Accoding to Douglas Gwyn <gwyn@arl.mil>: + + "This spec was changed in ISO/IEC 9899:1999 TC1; in ISO/IEC + + 9899:1999 as initially published, the expansion was required + + to be an integer constant of precisely matching type, which + + is impossible to accomplish for the shorter types on most + + platforms, because C99 provides no standard way to designate + + an integer constant with width less than that of type int. + + TC1 changed this to require just an integer constant + + *expression* with *promoted* type." + +*/ + + + +#define INT8_C(val) ((int8_t) + (val)) + +#define UINT8_C(val) ((uint8_t) + (val##U)) + +#define INT16_C(val) ((int16_t) + (val)) + +#define UINT16_C(val) ((uint16_t) + (val##U)) + + + +#define INT32_C(val) val##L + +#define UINT32_C(val) val##UL + +#define INT64_C(val) val##LL + +#define UINT64_C(val) val##ULL + + + +/* 7.18.4.2 Macros for greatest-width integer constants */ + +#define INTMAX_C(val) INT64_C(val) + +#define UINTMAX_C(val) UINT64_C(val) + + + +#endif /* !defined ( __cplusplus) || defined __STDC_CONSTANT_MACROS */ + + + +#endif + + + diff --git a/common/vdcommon.h b/common/vdcommon.h new file mode 100644 index 0000000..2e4ed5d --- /dev/null +++ b/common/vdcommon.h @@ -0,0 +1,58 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_VDCOMMON +#define _H_VDCOMMON + +#pragma warning(disable:4200) + +#include <windows.h> +#include "vd_agent.h" +#include "vdlog.h" + +#define VD_SERVICE_PIPE_NAME TEXT("\\\\.\\pipe\\vdservicepipe") +#define VD_MESSAGE_HEADER_SIZE (sizeof(VDPipeMessage) + sizeof(VDAgentMessage)) +#define VD_PIPE_BUF_SIZE (1024 * 1024) + +enum { + VD_AGENT_COMMAND, + VD_AGENT_RESET, + VD_AGENT_RESET_ACK, + VD_AGENT_QUIT, +}; + +typedef __declspec (align(1)) struct VDPipeMessage { + uint32_t type; + uint32_t opaque; + uint8_t data[0]; +} VDPipeMessage; + +typedef struct VDPipeBuffer { + OVERLAPPED overlap; + DWORD start; + DWORD end; + uint8_t data[VD_PIPE_BUF_SIZE]; +} VDPipeBuffer; + +typedef struct VDPipeState { + HANDLE pipe; + VDPipeBuffer write; + VDPipeBuffer read; +} VDPipeState; + +#endif + diff --git a/common/vdlog.cpp b/common/vdlog.cpp new file mode 100644 index 0000000..19f7a7c --- /dev/null +++ b/common/vdlog.cpp @@ -0,0 +1,114 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "vdlog.h" +#include <stdio.h> +#include <stdarg.h> +#include <share.h> + +#define LOG_ROLL_SIZE (1024 * 1024) + +VDLog* VDLog::_log = NULL; + +VDLog::VDLog(FILE* handle) + : _handle(handle) +{ + _log = this; +} + +VDLog::~VDLog() +{ + if (_log && _handle) { + fclose(_handle); + _log = NULL; + } +} + +VDLog* VDLog::get(TCHAR* path) +{ +#ifdef LOG_ENABLED + if (_log || !path) { + return _log; + } + DWORD size = 0; + HANDLE file = CreateFile(path, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, + NULL); + if (file != INVALID_HANDLE_VALUE) { + size = GetFileSize(file, NULL); + CloseHandle(file); + } + if (size != INVALID_FILE_SIZE && size > LOG_ROLL_SIZE) { + TCHAR roll_path[MAX_PATH]; + swprintf_s(roll_path, MAX_PATH, L"%s.1", path); + if (!MoveFileEx(path, roll_path, MOVEFILE_REPLACE_EXISTING)) { + return NULL; + } + } + FILE* handle = _wfsopen(path, L"a+", _SH_DENYNO); + if (!handle) { + return NULL; + } + _log = new VDLog(handle); + return _log; +#else + return NULL; +#endif +} + +void VDLog::printf(const char* format, ...) +{ + va_list args; + + va_start(args, format); + vfprintf(_handle, format, args); + va_end(args); + fflush(_handle); +} + +void log_version() +{ + DWORD handle; + TCHAR module_fname[MAX_PATH]; + TCHAR* info_buf = NULL; + try { + if (!GetModuleFileName(NULL, module_fname, MAX_PATH)) { + throw; + } + DWORD version_inf_size = GetFileVersionInfoSize(module_fname, &handle); + if (version_inf_size == 0) { + throw; + } + TCHAR* info_buf = new TCHAR[version_inf_size]; + if (!GetFileVersionInfo(module_fname, handle, version_inf_size, info_buf)) { + throw; + } + UINT size; + VS_FIXEDFILEINFO* file_info; + if (!VerQueryValue(info_buf, L"\\", (VOID**)&file_info, &size) || + size < sizeof(VS_FIXEDFILEINFO)) { + throw; + } + vd_printf("%d.%d.%d.%d", + file_info->dwFileVersionMS >> 16, + file_info->dwFileVersionMS & 0x0ffff, + file_info->dwFileVersionLS >> 16, + file_info->dwFileVersionLS & 0x0ffff); + } catch (...) { + vd_printf("get version info failed"); + } + delete[] info_buf; +} diff --git a/common/vdlog.h b/common/vdlog.h new file mode 100644 index 0000000..419248a --- /dev/null +++ b/common/vdlog.h @@ -0,0 +1,60 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_VDLOG +#define _H_VDLOG + +#include <tchar.h> +#include <crtdbg.h> +#include <windows.h> + +#define LOG_ENABLED + +class VDLog { +public: + ~VDLog(); + static VDLog* get(TCHAR* path = NULL); + void printf(const char* format, ...); + +private: + VDLog(FILE* handle); + +private: + static VDLog* _log; + FILE* _handle; +}; + +#ifdef LOG_ENABLED +#define vd_printf(format, ...) { \ + VDLog* log = VDLog::get(); \ + double secs = GetTickCount() / 1000.0; \ + if (log) { \ + log->printf("%.3f %s: " format "\n", secs, __FUNCTION__, __VA_ARGS__); \ + } else { \ + printf("%.3f %s: " format "\n", secs, __FUNCTION__, __VA_ARGS__); \ + } \ +} + +#define ASSERT(x) _ASSERTE(x) +#else +#define vd_printf(format, ...) +#define ASSERT(x) +#endif + +void log_version(); + +#endif diff --git a/vdagent.sln b/vdagent.sln new file mode 100644 index 0000000..afb6513 --- /dev/null +++ b/vdagent.sln @@ -0,0 +1,26 @@ +
+Microsoft Visual Studio Solution File, Format Version 9.00
+# Visual Studio 2005
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vdagent", "vdagent\vdagent.vcproj", "{CAD5A7E6-E9F5-4071-AFDA-25F76FDA5442}"
+EndProject
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vdservice", "vdservice\vdservice.vcproj", "{ADFE5E22-31D0-4343-AE9E-8102CC0051F9}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Win32 = Debug|Win32
+ Release|Win32 = Release|Win32
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CAD5A7E6-E9F5-4071-AFDA-25F76FDA5442}.Debug|Win32.ActiveCfg = Debug|Win32
+ {CAD5A7E6-E9F5-4071-AFDA-25F76FDA5442}.Debug|Win32.Build.0 = Debug|Win32
+ {CAD5A7E6-E9F5-4071-AFDA-25F76FDA5442}.Release|Win32.ActiveCfg = Release|Win32
+ {CAD5A7E6-E9F5-4071-AFDA-25F76FDA5442}.Release|Win32.Build.0 = Release|Win32
+ {ADFE5E22-31D0-4343-AE9E-8102CC0051F9}.Debug|Win32.ActiveCfg = Debug|Win32
+ {ADFE5E22-31D0-4343-AE9E-8102CC0051F9}.Debug|Win32.Build.0 = Debug|Win32
+ {ADFE5E22-31D0-4343-AE9E-8102CC0051F9}.Release|Win32.ActiveCfg = Release|Win32
+ {ADFE5E22-31D0-4343-AE9E-8102CC0051F9}.Release|Win32.Build.0 = Release|Win32
+ EndGlobalSection
+ GlobalSection(SolutionProperties) = preSolution
+ HideSolutionNode = FALSE
+ EndGlobalSection
+EndGlobal
diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp new file mode 100644 index 0000000..32502bc --- /dev/null +++ b/vdagent/desktop_layout.cpp @@ -0,0 +1,195 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "desktop_layout.h" +#include "vdlog.h" + +void DisplayMode::set_res(DWORD width, DWORD height, DWORD depth) +{ + _width = width; + _height = height; + _depth = depth; +} + +DesktopLayout::DesktopLayout() + : _total_width (0) + , _total_height (0) +{ + MUTEX_INIT(_mutex); + get_displays(); +} + +DesktopLayout::~DesktopLayout() +{ + clean_displays(); +} + +void DesktopLayout::get_displays() +{ + DISPLAY_DEVICE dev_info; + DEVMODE mode; + DWORD qxl_id; + DWORD dev_id = 0; + LONG min_x = 0; + LONG min_y = 0; + LONG max_x = 0; + LONG max_y = 0; + bool attached; + + lock(); + clean_displays(); + ZeroMemory(&dev_info, sizeof(dev_info)); + dev_info.cb = sizeof(dev_info); + ZeroMemory(&mode, sizeof(mode)); + mode.dmSize = sizeof(mode); + while (EnumDisplayDevices(NULL, dev_id, &dev_info, 0)) { + if (wcsstr(dev_info.DeviceString, L"QXL")) { + attached = !!(dev_info.StateFlags & DISPLAY_DEVICE_ATTACHED_TO_DESKTOP); + EnumDisplaySettings(dev_info.DeviceName, ENUM_CURRENT_SETTINGS, &mode); + if (!get_qxl_device_id(dev_info.DeviceKey, &qxl_id)) { + vd_printf("get_qxl_device_id failed"); + break; + } + size_t size = _displays.size(); + if (qxl_id >= size) { + _displays.resize(qxl_id + 1); + for (size_t i = size; i < qxl_id; i++) { + _displays[i] = NULL; + } + } + _displays[qxl_id] = new DisplayMode(mode.dmPosition.x, mode.dmPosition.y, + mode.dmPelsWidth, mode.dmPelsHeight, + mode.dmBitsPerPel, attached); + if (attached) { + min_x = min(min_x, mode.dmPosition.x); + min_y = min(min_y, mode.dmPosition.y); + max_x = max(max_x, mode.dmPosition.x + (LONG)mode.dmPelsWidth); + max_y = max(max_y, mode.dmPosition.y + (LONG)mode.dmPelsHeight); + } + } + dev_id++; + } + if (min_x || min_y) { + for (Displays::iterator iter = _displays.begin(); iter != _displays.end(); iter++) { + (*iter)->move_pos(-min_x, -min_y); + } + } + _total_width = max_x - min_x; + _total_height = max_y - min_y; + unlock(); +} + +void DesktopLayout::set_displays() +{ + DISPLAY_DEVICE dev_info; + DEVMODE dev_mode; + DWORD dev_id = 0; + DWORD qxl_id = 0; + int dev_sets = 0; + + lock(); + ZeroMemory(&dev_info, sizeof(dev_info)); + dev_info.cb = sizeof(dev_info); + ZeroMemory(&dev_mode, sizeof(dev_mode)); + dev_mode.dmSize = sizeof(dev_mode); + while (EnumDisplayDevices(NULL, dev_id, &dev_info, 0)) { + if (wcsstr(dev_info.DeviceString, L"QXL")) { + if (!get_qxl_device_id(dev_info.DeviceKey, &qxl_id)) { + vd_printf("get_qxl_device_id failed"); + break; + } + if (qxl_id >= _displays.size()) { + vd_printf("qxl_id %u out of range, #displays %u", qxl_id, _displays.size()); + break; + } + //FIXME: always set pos? + init_dev_mode(&dev_mode, _displays.at(qxl_id), true); + LONG ret = ChangeDisplaySettingsEx(dev_info.DeviceName, &dev_mode, NULL, + CDS_UPDATEREGISTRY | CDS_NORESET, NULL); + if (ret == DISP_CHANGE_SUCCESSFUL) { + dev_sets++; + } + } + dev_id++; + } + if (dev_sets) { + ChangeDisplaySettingsEx(NULL, NULL, NULL, 0, NULL); + } + unlock(); +} + +void DesktopLayout::clean_displays() +{ + lock(); + _total_width = 0; + _total_height = 0; + while (!_displays.empty()) { + DisplayMode* mode = _displays.back(); + _displays.pop_back(); + delete mode; + } + unlock(); +} + +bool DesktopLayout::is_attached(LPCTSTR dev_name) +{ + DEVMODE dev_mode; + + ZeroMemory(&dev_mode, sizeof(dev_mode)); + dev_mode.dmSize = sizeof(dev_mode); + EnumDisplaySettings(dev_name, ENUM_CURRENT_SETTINGS, &dev_mode); + return !!dev_mode.dmBitsPerPel; +} + +bool DesktopLayout::get_qxl_device_id(WCHAR* device_key, DWORD* device_id) +{ + DWORD type = REG_BINARY; + DWORD size = sizeof(*device_id); + bool key_found = false; + HKEY key; + + if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, wcsstr(device_key, L"System"), + 0L, KEY_QUERY_VALUE, &key) == ERROR_SUCCESS) { + if (RegQueryValueEx(key, L"QxlDeviceID", NULL, &type, (LPBYTE)device_id, &size) == + ERROR_SUCCESS) { + key_found = true; + } + RegCloseKey(key); + } + return key_found; +} + +void DesktopLayout::init_dev_mode(DEVMODE* dev_mode, DisplayMode* mode, bool set_pos) +{ + ZeroMemory(dev_mode, sizeof(DEVMODE)); + dev_mode->dmSize = sizeof(DEVMODE); + if (mode && mode->get_attached()) { + dev_mode->dmBitsPerPel = mode->get_depth(); + dev_mode->dmPelsWidth = mode->get_width(); + dev_mode->dmPelsHeight = mode->get_height(); + dev_mode->dmFields = DM_BITSPERPEL | DM_PELSWIDTH | DM_PELSHEIGHT; + if (set_pos) { + dev_mode->dmPosition.x = mode->get_pos_x(); + dev_mode->dmPosition.y = mode->get_pos_y(); + dev_mode->dmFields |= DM_POSITION; + } + } else { + //detach monitor + dev_mode->dmFields = DM_PELSWIDTH | DM_PELSHEIGHT | DM_POSITION; + } +} + diff --git a/vdagent/desktop_layout.h b/vdagent/desktop_layout.h new file mode 100644 index 0000000..797a82c --- /dev/null +++ b/vdagent/desktop_layout.h @@ -0,0 +1,84 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_DESKTOP_LAYOUT +#define _H_DESKTOP_LAYOUT + +#include "mutex.h" +#include <vector> + +class DisplayMode { +public: + DisplayMode(LONG pos_x, LONG pos_y, DWORD width, DWORD height, DWORD depth, bool attached) + : _pos_x (pos_x) + , _pos_y (pos_y) + , _width (width) + , _height (height) + , _depth (depth) + , _attached (attached) + { + } + + LONG get_pos_x() { return _pos_x;} + LONG get_pos_y() { return _pos_y;} + DWORD get_width() { return _width;} + DWORD get_height() { return _height;} + DWORD get_depth() { return _depth;} + bool get_attached() { return _attached;} + void set_pos(LONG x, LONG y) { _pos_x = x; _pos_y = y;} + void move_pos(LONG x, LONG y) { _pos_x += x; _pos_y += y;} + void set_res(DWORD width, DWORD height, DWORD depth); + void set_attached(bool attached) { _attached = attached;} + +private: + LONG _pos_x; + LONG _pos_y; + DWORD _width; + DWORD _height; + DWORD _depth; + bool _attached; +}; + +typedef std::vector<DisplayMode*> Displays; + +class DesktopLayout { +public: + DesktopLayout(); + ~DesktopLayout(); + void get_displays(); + void set_displays(); + void lock() { MUTEX_LOCK(_mutex);} + void unlock() { MUTEX_UNLOCK(_mutex);} + DisplayMode* get_display(int i) { return _displays.at(i);} + size_t get_display_count() { return _displays.size();} + DWORD get_total_width() { return _total_width;} + DWORD get_total_height() { return _total_height;} + +private: + void clean_displays(); + static bool is_attached(LPCTSTR dev_name); + static bool get_qxl_device_id(WCHAR* device_key, DWORD* device_id); + static void init_dev_mode(DEVMODE* dev_mode, DisplayMode* mode, bool set_pos); + +private: + mutex_t _mutex; + Displays _displays; + DWORD _total_width; + DWORD _total_height; +}; + +#endif diff --git a/vdagent/resource.h b/vdagent/resource.h new file mode 100644 index 0000000..bf4b1dc --- /dev/null +++ b/vdagent/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by vdagent.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp new file mode 100644 index 0000000..332438b --- /dev/null +++ b/vdagent/vdagent.cpp @@ -0,0 +1,598 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "vdcommon.h" +#include "desktop_layout.h" +#include <lmcons.h> + +#define VD_AGENT_LOG_PATH TEXT("%svdagent.log") +#define VD_AGENT_WINCLASS_NAME TEXT("VDAGENT") +#define VD_INPUT_INTERVAL_MS 20 +#define VD_TIMER_ID 1 + +class VDAgent { +public: + static VDAgent* get(); + ~VDAgent(); + bool run(); + +private: + VDAgent(); + void input_desktop_message_loop(); + bool handle_mouse_event(VDAgentMouseState* state); + bool handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port); + bool handle_control(VDPipeMessage* msg); + DWORD get_buttons_change(DWORD last_buttons_state, DWORD new_buttons_state, + DWORD mask, DWORD down_flag, DWORD up_flag); + static LRESULT CALLBACK wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam); + static VOID CALLBACK read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap); + static VOID CALLBACK write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap); + static DWORD WINAPI event_thread_proc(LPVOID param); + uint8_t* write_lock(DWORD bytes = 0); + void write_unlock(DWORD bytes = 0); + bool connect_pipe(); + bool send_input(); + +private: + static VDAgent* _singleton; + HWND _hwnd; + DWORD _buttons_state; + LONG _mouse_x; + LONG _mouse_y; + INPUT _input; + DWORD _input_time; + HANDLE _desktop_switch_event; + bool _pending_input; + bool _pending_write; + bool _running; + DesktopLayout* _desktop_layout; + VDPipeState _pipe_state; + mutex_t _write_mutex; + VDLog* _log; +}; + +VDAgent* VDAgent::_singleton = NULL; + +VDAgent* VDAgent::get() +{ + if (!_singleton) { + _singleton = new VDAgent(); + } + return _singleton; +} + +VDAgent::VDAgent() + : _hwnd (NULL) + , _buttons_state (0) + , _mouse_x (0) + , _mouse_y (0) + , _input_time (0) + , _pending_input (false) + , _pending_write (false) + , _running (false) + , _desktop_layout (NULL) + , _log (NULL) +{ + TCHAR log_path[MAX_PATH]; + TCHAR temp_path[MAX_PATH]; + + if (GetTempPath(MAX_PATH, temp_path)) { + swprintf_s(log_path, MAX_PATH, VD_AGENT_LOG_PATH, temp_path); + _log = VDLog::get(log_path); + } + ZeroMemory(&_input, sizeof(INPUT)); + ZeroMemory(&_pipe_state, sizeof(VDPipeState)); + MUTEX_INIT(_write_mutex); + _singleton = this; +} + +VDAgent::~VDAgent() +{ + delete _log; +} + +DWORD WINAPI VDAgent::event_thread_proc(LPVOID param) +{ + HANDLE desktop_event = OpenEvent(SYNCHRONIZE, FALSE, L"WinSta0_DesktopSwitch"); + if (!desktop_event) { + vd_printf("OpenEvent() failed: %d", GetLastError()); + return 1; + } + while (_singleton->_running) { + DWORD wait_ret = WaitForSingleObject(desktop_event, INFINITE); + switch (wait_ret) { + case WAIT_OBJECT_0: + SetEvent((HANDLE)param); + break; + case WAIT_TIMEOUT: + default: + vd_printf("WaitForSingleObject(): %u", wait_ret); + } + } + CloseHandle(desktop_event); + return 0; +} + +bool VDAgent::run() +{ + DWORD session_id; + DWORD event_thread_id; + HANDLE event_thread; + WNDCLASS wcls; + + if (!ProcessIdToSessionId(GetCurrentProcessId(), &session_id)) { + vd_printf("ProcessIdToSessionId failed %u", GetLastError()); + return false; + } + vd_printf("***Agent started in session %u***", session_id); + log_version(); + if (!SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS)) { + vd_printf("SetPriorityClass failed %u", GetLastError()); + } + if (!SetProcessShutdownParameters(0x100, 0)) { + vd_printf("SetProcessShutdownParameters failed %u", GetLastError()); + } + _desktop_switch_event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (!_desktop_switch_event) { + vd_printf("CreateEvent() failed: %d", GetLastError()); + return false; + } + memset(&wcls, 0, sizeof(wcls)); + wcls.lpfnWndProc = &VDAgent::wnd_proc; + wcls.lpszClassName = VD_AGENT_WINCLASS_NAME; + if (!RegisterClass(&wcls)) { + vd_printf("RegisterClass() failed: %d", GetLastError()); + return false; + } + _desktop_layout = new DesktopLayout(); + if (_desktop_layout->get_display_count() == 0) { + vd_printf("No QXL devices!"); + } + if (!connect_pipe()) { + CloseHandle(_desktop_switch_event); + delete _desktop_layout; + return false; + } + _running = true; + event_thread = CreateThread(NULL, 0, event_thread_proc, _desktop_switch_event, 0, + &event_thread_id); + if (!event_thread) { + vd_printf("CreateThread() failed: %d", GetLastError()); + CloseHandle(_desktop_switch_event); + CloseHandle(_pipe_state.pipe); + delete _desktop_layout; + return false; + } + read_completion(0, 0, &_pipe_state.read.overlap); + while (_running) { + input_desktop_message_loop(); + } + vd_printf("Agent stopped"); + CloseHandle(event_thread); + CloseHandle(_desktop_switch_event); + CloseHandle(_pipe_state.pipe); + delete _desktop_layout; + return true; +} + +void VDAgent::input_desktop_message_loop() +{ + bool desktop_switch = false; + TCHAR desktop_name[MAX_PATH]; + DWORD wait_ret; + HDESK hdesk; + MSG msg; + + hdesk = OpenInputDesktop(0, FALSE, GENERIC_ALL); + if (!hdesk) { + vd_printf("OpenInputDesktop() failed: %u", GetLastError()); + _running = false; + return; + } + if (!SetThreadDesktop(hdesk)) { + vd_printf("SetThreadDesktop failed %u", GetLastError()); + _running = false; + return; + } + if (GetUserObjectInformation(hdesk, UOI_NAME, desktop_name, sizeof(desktop_name), NULL)) { + vd_printf("Desktop: %S", desktop_name); + } else { + vd_printf("GetUserObjectInformation failed %u", GetLastError()); + } + _hwnd = CreateWindow(VD_AGENT_WINCLASS_NAME, NULL, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL); + if (!_hwnd) { + vd_printf("CreateWindow() failed: %u", GetLastError()); + _running = false; + return; + } + while (_running && !desktop_switch) { + wait_ret = MsgWaitForMultipleObjectsEx(1, &_desktop_switch_event, INFINITE, QS_ALLINPUT, + MWMO_ALERTABLE); + switch (wait_ret) { + case WAIT_OBJECT_0: + vd_printf("WinSta0_DesktopSwitch"); + desktop_switch = true; + break; + case WAIT_OBJECT_0 + 1: + while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { + TranslateMessage(&msg); + DispatchMessage(&msg); + } + break; + case WAIT_IO_COMPLETION: + break; + case WAIT_TIMEOUT: + default: + vd_printf("MsgWaitForMultipleObjectsEx(): %u", wait_ret); + } + } + if (_pending_input) { + KillTimer(_hwnd, VD_TIMER_ID); + _pending_input = false; + } + DestroyWindow(_hwnd); + CloseDesktop(hdesk); +} + +DWORD VDAgent::get_buttons_change(DWORD last_buttons_state, DWORD new_buttons_state, + DWORD mask, DWORD down_flag, DWORD up_flag) +{ + DWORD ret = 0; + if (!(last_buttons_state & mask) && (new_buttons_state & mask)) { + ret = down_flag; + } else if ((last_buttons_state & mask) && !(new_buttons_state & mask)) { + ret = up_flag; + } + return ret; +} + +bool VDAgent::send_input() +{ + UINT ret; + _desktop_layout->lock(); + if (_pending_input) { + if (KillTimer(_hwnd, VD_TIMER_ID)) { + _pending_input = false; + } else { + vd_printf("KillTimer failed: %d", GetLastError()); + _running = false; + _desktop_layout->unlock(); + return false; + } + } + ret = SendInput(1, &_input, sizeof(INPUT)); + if (!ret) { + vd_printf("SendInput failed: %d", GetLastError()); + _running = false; + } + _input_time = GetTickCount(); + _desktop_layout->unlock(); + return !!ret; +} + +bool VDAgent::handle_mouse_event(VDAgentMouseState* state) +{ + DisplayMode* mode = NULL; + DWORD mouse_move = 0; + DWORD buttons_change = 0; + DWORD mouse_wheel = 0; + bool ret = true; + + ASSERT(_desktop_layout); + _desktop_layout->lock(); + if (state->display_id < _desktop_layout->get_display_count()) { + mode = _desktop_layout->get_display(state->display_id); + } + if (!mode || !mode->get_attached()) { + _desktop_layout->unlock(); + return true; + } + ZeroMemory(&_input, sizeof(INPUT)); + _input.type = INPUT_MOUSE; + if (state->x != _mouse_x || state->y != _mouse_y) { + _mouse_x = state->x; + _mouse_y = state->y; + mouse_move = MOUSEEVENTF_MOVE; + _input.mi.dx = (mode->get_pos_x() + _mouse_x) * 0xffff / + _desktop_layout->get_total_width(); + _input.mi.dy = (mode->get_pos_y() + _mouse_y) * 0xffff / + _desktop_layout->get_total_height(); + } + if (state->buttons != _buttons_state) { + buttons_change = get_buttons_change(_buttons_state, state->buttons, VD_AGENT_LBUTTON_MASK, + MOUSEEVENTF_LEFTDOWN, MOUSEEVENTF_LEFTUP) | + get_buttons_change(_buttons_state, state->buttons, VD_AGENT_MBUTTON_MASK, + MOUSEEVENTF_MIDDLEDOWN, MOUSEEVENTF_MIDDLEUP) | + get_buttons_change(_buttons_state, state->buttons, VD_AGENT_RBUTTON_MASK, + MOUSEEVENTF_RIGHTDOWN, MOUSEEVENTF_RIGHTUP); + mouse_wheel = get_buttons_change(_buttons_state, state->buttons, + VD_AGENT_UBUTTON_MASK | VD_AGENT_DBUTTON_MASK, + MOUSEEVENTF_WHEEL, 0); + if (mouse_wheel) { + if (state->buttons & VD_AGENT_UBUTTON_MASK) { + _input.mi.mouseData = WHEEL_DELTA; + } else if (state->buttons & VD_AGENT_DBUTTON_MASK) { + _input.mi.mouseData = (DWORD)(-WHEEL_DELTA); + } + } + _buttons_state = state->buttons; + } + + _input.mi.dwFlags = MOUSEEVENTF_ABSOLUTE | MOUSEEVENTF_VIRTUALDESK | mouse_move | + mouse_wheel | buttons_change; + + if ((mouse_move && GetTickCount() - _input_time > VD_INPUT_INTERVAL_MS) || buttons_change || + mouse_wheel) { + ret = send_input(); + } else if (!_pending_input) { + if (SetTimer(_hwnd, VD_TIMER_ID, VD_INPUT_INTERVAL_MS, NULL)) { + _pending_input = true; + } else { + vd_printf("SetTimer failed: %d", GetLastError()); + _running = false; + ret = false; + } + } + _desktop_layout->unlock(); + return ret; +} + +bool VDAgent::handle_mon_config(VDAgentMonitorsConfig* mon_config, uint32_t port) +{ + VDPipeMessage* reply_pipe_msg; + VDAgentMessage* reply_msg; + VDAgentReply* reply; + size_t display_count; + + display_count = _desktop_layout->get_display_count(); + for (uint32_t i = 0; i < display_count; i++) { + DisplayMode* mode = _desktop_layout->get_display(i); + ASSERT(mode); + if (i >= mon_config->num_of_monitors) { + vd_printf("%d. detached", i); + mode->set_attached(false); + continue; + } + VDAgentMonConfig* mon = &mon_config->monitors[i]; + vd_printf("%d. %u*%u*%u (%d,%d) %u", i, mon->width, mon->height, mon->depth, mon->x, + mon->y, !!(mon_config->flags & VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS)); + mode->set_res(mon->width, mon->height, mon->depth); + if (mon_config->flags & VD_AGENT_CONFIG_MONITORS_FLAG_USE_POS) { + mode->set_pos(mon->x, mon->y); + } + mode->set_attached(true); + } + if (display_count) { + _desktop_layout->set_displays(); + } + + DWORD msg_size = VD_MESSAGE_HEADER_SIZE + sizeof(VDAgentReply); + reply_pipe_msg = (VDPipeMessage*)write_lock(msg_size); + if (!reply_pipe_msg) { + return false; + } + reply_pipe_msg->type = VD_AGENT_COMMAND; + reply_pipe_msg->opaque = port; + reply_msg = (VDAgentMessage*)reply_pipe_msg->data; + reply_msg->protocol = VD_AGENT_PROTOCOL; + reply_msg->type = VD_AGENT_REPLY; + reply_msg->opaque = 0; + reply_msg->size = sizeof(VDAgentReply); + reply = (VDAgentReply*)reply_msg->data; + reply->type = VD_AGENT_MONITORS_CONFIG; + reply->error = display_count ? VD_AGENT_SUCCESS : VD_AGENT_ERROR; + write_unlock(msg_size); + if (!_pending_write) { + write_completion(0, 0, &_pipe_state.write.overlap); + } + return true; +} + +bool VDAgent::handle_control(VDPipeMessage* msg) +{ + switch (msg->type) { + case VD_AGENT_RESET: { + vd_printf("Agent reset"); + VDPipeMessage* ack = (VDPipeMessage*)write_lock(sizeof(VDPipeMessage)); + if (!ack) { + return false; + } + ack->type = VD_AGENT_RESET_ACK; + ack->opaque = msg->opaque; + write_unlock(sizeof(VDPipeMessage)); + if (!_pending_write) { + write_completion(0, 0, &_pipe_state.write.overlap); + } + break; + } + case VD_AGENT_QUIT: + vd_printf("Agent quit"); + _running = false; + break; + default: + vd_printf("Unsupported control %u", msg->type); + return false; + } + return true; +} + +bool VDAgent::connect_pipe() +{ + VDAgent* a = _singleton; + HANDLE pipe; + + ZeroMemory(&a->_pipe_state, sizeof(VDPipeState)); + if (!WaitNamedPipe(VD_SERVICE_PIPE_NAME, NMPWAIT_USE_DEFAULT_WAIT)) { + vd_printf("WaitNamedPipe() failed: %d", GetLastError()); + return false; + } + //assuming vdservice created the named pipe before creating this vdagent process + pipe = CreateFile(VD_SERVICE_PIPE_NAME, GENERIC_READ | GENERIC_WRITE, + 0, NULL, OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL); + if (pipe == INVALID_HANDLE_VALUE) { + vd_printf("CreateFile() failed: %d", GetLastError()); + return false; + } + DWORD pipe_mode = PIPE_READMODE_MESSAGE | PIPE_WAIT; + if (!SetNamedPipeHandleState(pipe, &pipe_mode, NULL, NULL)) { + vd_printf("SetNamedPipeHandleState() failed: %d", GetLastError()); + CloseHandle(pipe); + return false; + } + a->_pipe_state.pipe = pipe; + vd_printf("Connected to service pipe"); + return true; +} + +VOID CALLBACK VDAgent::read_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap) +{ + VDAgent* a = _singleton; + VDPipeState* ps = &a->_pipe_state; + DWORD len; + + if (!a->_running) { + return; + } + if (err) { + vd_printf("error %u", err); + a->_running = false; + return; + } + ps->read.end += bytes; + while (a->_running && (len = ps->read.end - ps->read.start) >= sizeof(VDPipeMessage)) { + VDPipeMessage* pipe_msg = (VDPipeMessage*)&ps->read.data[ps->read.start]; + + if (pipe_msg->type != VD_AGENT_COMMAND) { + a->handle_control(pipe_msg); + ps->read.start += sizeof(VDPipeMessage); + continue; + } + if (len < VD_MESSAGE_HEADER_SIZE) { + break; + } + VDAgentMessage* msg = (VDAgentMessage*)pipe_msg->data; + if (len < VD_MESSAGE_HEADER_SIZE + msg->size) { + break; + } + if (msg->protocol != VD_AGENT_PROTOCOL) { + vd_printf("Invalid protocol %d", msg->protocol); + a->_running = false; + break; + } + switch (msg->type) { + case VD_AGENT_MOUSE_STATE: + if (!a->handle_mouse_event((VDAgentMouseState*)msg->data)) { + vd_printf("handle_mouse_event failed: %d", GetLastError()); + a->_running = false; + } + break; + case VD_AGENT_MONITORS_CONFIG: + if (!a->handle_mon_config((VDAgentMonitorsConfig*)msg->data, pipe_msg->opaque)) { + vd_printf("handle_mon_config failed: %d", GetLastError()); + a->_running = false; + } + break; + default: + vd_printf("Unsupported message type %d size %d", msg->type, msg->size); + } + ps->read.start += (VD_MESSAGE_HEADER_SIZE + msg->size); + if (ps->read.start == ps->read.end) { + ps->read.start = ps->read.end = 0; + } + } + if (a->_running && ps->read.end < sizeof(ps->read.data) && + !ReadFileEx(ps->pipe, ps->read.data + ps->read.end, sizeof(ps->read.data) - ps->read.end, + overlap, read_completion)) { + vd_printf("ReadFileEx() failed: %u", GetLastError()); + a->_running = false; + } +} + +VOID CALLBACK VDAgent::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap) +{ + VDAgent* a = _singleton; + VDPipeState* ps = &a->_pipe_state; + + a->_pending_write = false; + if (!a->_running) { + return; + } + if (err) { + vd_printf("error %u", err); + a->_running = false; + return; + } + if (!a->write_lock()) { + a->_running = false; + return; + } + ps->write.start += bytes; + if (ps->write.start == ps->write.end) { + ps->write.start = ps->write.end = 0; + } else if (WriteFileEx(ps->pipe, ps->write.data + ps->write.start, + ps->write.end - ps->write.start, overlap, write_completion)) { + a->_pending_write = true; + } else { + vd_printf("WriteFileEx() failed: %u", GetLastError()); + a->_running = false; + } + a->write_unlock(); +} + +uint8_t* VDAgent::write_lock(DWORD bytes) +{ + if (_pipe_state.write.end + bytes <= sizeof(_pipe_state.write.data)) { + MUTEX_LOCK(_write_mutex); + return &_pipe_state.write.data[_pipe_state.write.end]; + } else { + vd_printf("write buffer is full"); + return NULL; + } +} + +void VDAgent::write_unlock(DWORD bytes) +{ + _pipe_state.write.end += bytes; + MUTEX_UNLOCK(_write_mutex); +} + +LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARAM lparam) +{ + VDAgent* a = _singleton; + + switch (message) { + case WM_DISPLAYCHANGE: + vd_printf("Display change"); + a->_desktop_layout->get_displays(); + break; + case WM_TIMER: + a->send_input(); + break; + default: + return DefWindowProc(hwnd, message, wparam, lparam); + } + return 0; +} + +int APIENTRY _tWinMain(HINSTANCE instance, HINSTANCE prev_instance, LPTSTR cmd_line, int cmd_show) +{ + VDAgent* vdagent = VDAgent::get(); + vdagent->run(); + delete vdagent; + return 0; +} + diff --git a/vdagent/vdagent.rc b/vdagent/vdagent.rc new file mode 100644 index 0000000..770fc22 --- /dev/null +++ b/vdagent/vdagent.rc @@ -0,0 +1,102 @@ +// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Hebrew resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_HEB)
+#ifdef _WIN32
+LANGUAGE LANG_HEBREW, SUBLANG_DEFAULT
+#pragma code_page(1255)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,3,0,0
+ PRODUCTVERSION 0,3,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000904b0"
+ BEGIN
+ VALUE "CompanyName", "Red Hat Inc."
+ VALUE "FileDescription", "Spice agent"
+ VALUE "FileVersion", "0, 3, 0, 0"
+ VALUE "InternalName", "vdagent"
+ VALUE "LegalCopyright", "Copyright (c) 2009 Red Hat Inc. and/or its affiliates"
+ VALUE "OriginalFilename", "vdagent.exe"
+ VALUE "ProductName", "Red Hat Spice"
+ VALUE "ProductVersion", "0, 3, 0, 0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x9, 1200
+ END
+END
+
+#endif // Hebrew resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/vdagent/vdagent.vcproj b/vdagent/vdagent.vcproj new file mode 100644 index 0000000..954f865 --- /dev/null +++ b/vdagent/vdagent.vcproj @@ -0,0 +1,230 @@ +<?xml version="1.0" encoding="windows-1255"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="vdagent"
+ ProjectGUID="{CAD5A7E6-E9F5-4071-AFDA-25F76FDA5442}"
+ RootNamespace="redagent"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\common;$(SPICE_COMMON_DIR)"
+ PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS,_WIN32_WINNT=0x0501"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="Version.lib"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\common;$(SPICE_COMMON_DIR)"
+ AdditionalUsingDirectories=""
+ PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS,_WIN32_WINNT=0x0501"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="Version.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="2"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\desktop_layout.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\vdagent.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\common\vdlog.cpp"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\desktop_layout.h"
+ >
+ </File>
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath="..\common\vdcommon.h"
+ >
+ </File>
+ <File
+ RelativePath="..\common\vdlog.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\vdagent.rc"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/vdservice/resource.h b/vdservice/resource.h new file mode 100644 index 0000000..b204524 --- /dev/null +++ b/vdservice/resource.h @@ -0,0 +1,14 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by vdservice.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/vdservice/vdi_port.cpp b/vdservice/vdi_port.cpp new file mode 100644 index 0000000..4c0a99d --- /dev/null +++ b/vdservice/vdi_port.cpp @@ -0,0 +1,185 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include "stdio.h" +#include "vdi_port.h" +#include "vdlog.h" + +#define VDI_PORT_DEV_NAME TEXT("\\\\.\\VDIPort") +#define FILE_DEVICE_UNKNOWN 0x00000022 +#define METHOD_BUFFERED 0 +#define FILE_ANY_ACCESS 0 + +#define CTL_CODE(DeviceType, Function, Method, Access) ( \ + ((DeviceType) << 16) | ((Access) << 14) | ((Function) << 2) | (Method) \ +) + +#define FIRST_AVAIL_IO_FUNC 0x800 +#define RED_TUNNEL_CTL_FUNC FIRST_AVAIL_IO_FUNC + +#define IOCTL_RED_TUNNEL_SET_EVENT \ + CTL_CODE(FILE_DEVICE_UNKNOWN, RED_TUNNEL_CTL_FUNC, METHOD_BUFFERED, FILE_ANY_ACCESS) + +#define MIN(a, b) ((a) > (b) ? (b) : (a)) + +VDIPort::VDIPort() + : _handle (INVALID_HANDLE_VALUE) + , _event (NULL) + , _write_start (_write_ring) + , _write_end (_write_ring) + , _read_start (_read_ring) + , _read_end (_read_ring) +{ +} + +VDIPort::~VDIPort() +{ + if (_handle != INVALID_HANDLE_VALUE) { + CloseHandle(_handle); + } + if (_event) { + CloseHandle(_event); + } +} + +bool VDIPort::init() +{ + DWORD io_ret_len; + _handle = CreateFile(VDI_PORT_DEV_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL, + OPEN_EXISTING, 0, NULL); + if (_handle == INVALID_HANDLE_VALUE) { + vd_printf("CreateFile() failed: %u", GetLastError()); + return false; + } + _event = CreateEvent(NULL, FALSE, FALSE, NULL); + if (_event == NULL) { + vd_printf("CreateEvent() failed: %u", GetLastError()); + return false; + } + if (!DeviceIoControl(_handle, IOCTL_RED_TUNNEL_SET_EVENT, &_event, sizeof(_event), + NULL, 0, &io_ret_len, NULL)) { + vd_printf("DeviceIoControl() failed: %u", GetLastError()); + return false; + } + return true; +} + +size_t VDIPort::ring_write(const void* buf, size_t size) +{ + size_t free_size = (BUF_SIZE + _write_start - _write_end - 1) % BUF_SIZE; + size_t n; + + if (size > free_size) { + size = free_size; + } + if (_write_end < _write_start) { + memcpy(_write_end, buf, size); + } else { + n = MIN(size, (size_t)(&_write_ring[BUF_SIZE] - _write_end)); + memcpy(_write_end, buf, n); + if (size > n) { + memcpy(_write_ring, (uint8_t*)buf + n, size - n); + } + } + _write_end = _write_ring + (_write_end - _write_ring + size) % BUF_SIZE; + return size; +} + +int VDIPort::write() +{ + int size; + int n; + + if (_write_start == _write_end) { + return 0; + } + if (_write_start < _write_end) { + size = (int)(_write_end - _write_start); + } else { + size = (int)(&_write_ring[BUF_SIZE] - _write_start); + } + if (!WriteFile(_handle, _write_start, size, (LPDWORD)&n, NULL)) { + return handle_error(); + } + _write_start = _write_ring + (_write_start - _write_ring + n) % BUF_SIZE; + return n; +} + +size_t VDIPort::read_ring_size() +{ + return (BUF_SIZE + _read_end - _read_start) % BUF_SIZE; +} + +size_t VDIPort::ring_read(void* buf, size_t size) +{ + size_t n; + size_t m = 0; + + if (_read_start == _read_end) { + return 0; + } + if (_read_start < _read_end) { + n = MIN(size, (size_t)(_read_end - _read_start)); + memcpy(buf, _read_start, n); + } else { + n = MIN(size, (size_t)(&_read_ring[BUF_SIZE] - _read_start)); + memcpy(buf, _read_start, n); + if (size > n) { + m = MIN(size - n, (size_t)(_read_end - _read_ring)); + memcpy((uint8_t*)buf + n, _read_ring, m); + } + } + _read_start = _read_ring + (_read_start - _read_ring + n + m) % BUF_SIZE; + return n + m; +} + +int VDIPort::read() +{ + int size; + int n; + + if ((_read_end - _read_ring + 1) % BUF_SIZE == _read_start - _read_ring) { + return 0; + } + if (_read_start == _read_end) { + _read_start = _read_end = _read_ring; + } + if (_read_start <= _read_end) { + size = MIN(BUF_SIZE - 1, (int)(&_read_ring[BUF_SIZE] - _read_end)); + } else { + size = (int)(_read_start - _read_end - 1); + } + if (!ReadFile(_handle, _read_end, size, (LPDWORD)&n, NULL)) { + return handle_error(); + } + _read_end = _read_ring + (_read_end - _read_ring + n) % BUF_SIZE; + return n; +} + +int VDIPort::handle_error() +{ + switch (GetLastError()) { + case ERROR_CONNECTION_INVALID: + vd_printf("port reset"); + _write_start = _write_end = _write_ring; + _read_start = _read_end = _read_ring; + return VDI_PORT_RESET; + default: + vd_printf("port io failed: %u", GetLastError()); + return VDI_PORT_ERROR; + } +} diff --git a/vdservice/vdi_port.h b/vdservice/vdi_port.h new file mode 100644 index 0000000..3af3f18 --- /dev/null +++ b/vdservice/vdi_port.h @@ -0,0 +1,68 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#ifndef _H_VDI_PORT +#define _H_VDI_PORT + +#include <windows.h> +#include <stdint.h> + +#define BUF_SIZE (1024 * 1024) + +#define BUF_READ (1 << 0) +#define BUF_WRITE (1 << 1) +#define BUF_ALL (BUF_READ | BUF_WRITE) + +#define VDI_PORT_BLOCKED 0 +#define VDI_PORT_RESET -1 +#define VDI_PORT_ERROR -2 + +class VDIPort { +public: + VDIPort(); + ~VDIPort(); + bool init(); + size_t ring_write(const void* buf, size_t size); + size_t ring_read(void* buf, size_t size); + size_t read_ring_size(); + int write(); + int read(); + HANDLE get_event() { return _event;} + +private: + int handle_error(); + +private: + HANDLE _handle; + HANDLE _event; + uint8_t _write_ring[BUF_SIZE]; + uint8_t* _write_start; + uint8_t* _write_end; + uint8_t _read_ring[BUF_SIZE]; + uint8_t* _read_start; + uint8_t* _read_end; +}; + +// Ring notes: +// _end is one after the end of data +// _start==_end means empty ring +// _start-1==_end (modulo) means full ring +// _start-1 is never used +// ring_write & read on right side of the ring (update _end) +// ring_read & write from left (update _start) + +#endif diff --git a/vdservice/vdservice.cpp b/vdservice/vdservice.cpp new file mode 100644 index 0000000..d3b9502 --- /dev/null +++ b/vdservice/vdservice.cpp @@ -0,0 +1,947 @@ +/* + 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 <http://www.gnu.org/licenses/>. +*/ + +#include <windows.h> +#include <winternl.h> +#include <wtsapi32.h> +#include <userenv.h> +#include <stdio.h> +#include "vdcommon.h" +#include "vdi_port.h" +#include "mutex.h" + +#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 3000 +#define VD_AGENT_MAX_RESTARTS 10 +#define VD_AGENT_RESTART_INTERVAL 3000 +#define VD_AGENT_RESTART_COUNT_RESET_INTERVAL 60000 +#define VD_EVENTS_COUNT 4 + +class VDService { +public: + static VDService* get(); + ~VDService(); + void run(); + void install(); + void 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[]); + static VOID CALLBACK write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap); + void write_agent_control(uint32_t type, uint32_t opaque); + void read_pipe(); + void handle_pipe_data(DWORD bytes); + void handle_port_data(); + bool handle_agent_control(VDPipeMessage* msg); + bool restart_agent(bool normal_restart); + bool launch_agent(); + bool kill_agent(); + +private: + static VDService* _singleton; + SERVICE_STATUS _status; + SERVICE_STATUS_HANDLE _status_handle; + PROCESS_INFORMATION _agent_proc_info; + HANDLE _control_event; + HANDLE _events[VD_EVENTS_COUNT]; + TCHAR _agent_path[MAX_PATH]; + VDIPort* _vdi_port; + VDPipeState _pipe_state; + uint32_t _connection_id; + mutex_t _agent_mutex; + DWORD _session_id; + DWORD _chunk_port; + DWORD _chunk_size; + DWORD _last_agent_restart_time; + int _agent_restarts; + bool _pipe_connected; + bool _pending_reset; + bool _pending_write; + bool _pending_read; + bool _agent_alive; + bool _running; + VDLog* _log; +}; + +VDService* VDService::_singleton = NULL; + +VDService* VDService::get() +{ + if (!_singleton) { + _singleton = new VDService(); + } + return (VDService*)_singleton; +} + +VDService::VDService() + : _status_handle (0) + , _vdi_port (NULL) + , _connection_id (0) + , _session_id (0) + , _chunk_port (0) + , _chunk_size (0) + , _last_agent_restart_time (0) + , _agent_restarts (0) + , _pipe_connected (false) + , _pending_reset (false) + , _pending_write (false) + , _pending_read (false) + , _agent_alive (false) + , _running (false) + , _log (NULL) +{ + ZeroMemory(&_agent_proc_info, sizeof(_agent_proc_info)); + ZeroMemory(&_pipe_state, sizeof(_pipe_state)); + ZeroMemory(_events, sizeof(_events)); + _control_event = CreateEvent(NULL, FALSE, FALSE, NULL); + _pipe_state.read.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); + _agent_path[0] = wchar_t('\0'); + MUTEX_INIT(_agent_mutex); + _singleton = this; +} + +VDService::~VDService() +{ + CloseHandle(_pipe_state.read.overlap.hEvent); + CloseHandle(_control_event); + delete _log; +} + +void VDService::run() +{ + SERVICE_TABLE_ENTRY service_table[] = {{VD_SERVICE_NAME, main}, {0, 0}}; + StartServiceCtrlDispatcher(service_table); +} + +void VDService::install() +{ + SC_HANDLE service_control_manager = OpenSCManager(0, 0, SC_MANAGER_CREATE_SERVICE); + if (!service_control_manager) { + printf("OpenSCManager failed\n"); + return; + } + TCHAR path[_MAX_PATH + 1]; + if (!GetModuleFileName(0, path, sizeof(path) / sizeof(path[0]))) { + printf("GetModuleFileName failed\n"); + CloseServiceHandle(service_control_manager); + return; + } + 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 = VD_SERVICE_DESCRIPTION; + if (!ChangeServiceConfig2(service, SERVICE_CONFIG_DESCRIPTION, &descr)) { + printf("ChangeServiceConfig2 failed\n"); + } + CloseServiceHandle(service); + printf("Service installed successfully\n"); + } else if (GetLastError() == ERROR_SERVICE_EXISTS) { + printf("Service already exists\n"); + } else { + printf("Service not installed successfully, error %d\n", GetLastError()); + } + CloseServiceHandle(service_control_manager); +} + +void VDService::uninstall() +{ + SC_HANDLE service_control_manager = OpenSCManager(0, 0, SC_MANAGER_CONNECT); + if (!service_control_manager) { + printf("OpenSCManager failed\n"); + return; + } + 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; + } + 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"); + } 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); +} + +const char* session_events[] = { + "INVALID", "CONNECT", "DISCONNECT", "REMOTE_CONNECT", "REMOTE_DISCONNECT", "LOGON", "LOGOFF", + "LOCK", "UNLOCK", "REMOTE_CONTROL" +}; + +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 %u %s", session_id, session_events[event_type]); + SetServiceStatus(s->_status_handle, &s->_status); + if (event_type == WTS_CONSOLE_CONNECT) { + s->_session_id = session_id; + if (!s->restart_agent(true)) { + s->stop(); + } + } + break; + } + default: + vd_printf("Unsupported control %u", 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 %u", 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; + s->_status_handle = RegisterServiceCtrlHandlerEx(VD_SERVICE_NAME, &VDService::control_handler, + NULL); + if (!s->_status_handle) { + 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); + + s->_running = true; + s->execute(); + + // 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); +} + +typedef __declspec (align(1)) struct VDAgentDataChunk { + uint32_t port; + uint32_t size; + uint8_t data[0]; +} VDAgentDataChunk; + +bool VDService::execute() +{ + SECURITY_ATTRIBUTES sec_attr; + SECURITY_DESCRIPTOR* sec_desr; + HANDLE pipe; + + sec_desr = (SECURITY_DESCRIPTOR*)LocalAlloc(LPTR, SECURITY_DESCRIPTOR_MIN_LENGTH); + InitializeSecurityDescriptor(sec_desr, SECURITY_DESCRIPTOR_REVISION); + SetSecurityDescriptorDacl(sec_desr, TRUE, (PACL)NULL, FALSE); + sec_attr.nLength = sizeof(sec_attr); + sec_attr.bInheritHandle = TRUE; + sec_attr.lpSecurityDescriptor = sec_desr; + pipe = CreateNamedPipe(VD_SERVICE_PIPE_NAME, PIPE_ACCESS_DUPLEX | + FILE_FLAG_FIRST_PIPE_INSTANCE | FILE_FLAG_OVERLAPPED, + PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, + PIPE_UNLIMITED_INSTANCES, BUF_SIZE, BUF_SIZE, + VD_AGENT_TIMEOUT, &sec_attr); + LocalFree(sec_desr); + if (pipe == INVALID_HANDLE_VALUE) { + vd_printf("CreatePipe() failed: %u", GetLastError()); + return false; + } + _pipe_state.pipe = pipe; + _session_id = WTSGetActiveConsoleSessionId(); + if (_session_id == 0xFFFFFFFF) { + vd_printf("WTSGetActiveConsoleSessionId() failed"); + CloseHandle(pipe); + return false; + } + if (!launch_agent()) { + CloseHandle(pipe); + return false; + } + _vdi_port = new VDIPort(); + if (!_vdi_port->init()) { + delete _vdi_port; + CloseHandle(pipe); + return false; + } + vd_printf("Connected to server"); + _events[0] = _vdi_port->get_event(); + _events[1] = _pipe_state.read.overlap.hEvent; + _events[2] = _control_event; + _events[3] = _agent_proc_info.hProcess; + _chunk_size = _chunk_port = 0; + read_pipe(); + while (_running) { + int cont_read = _vdi_port->read(); + int cont_write = _vdi_port->write(); + bool cont = false; + + if (cont_read >= 0 && cont_write >= 0) { + cont = cont_read || cont_write; + } else if (cont_read == VDI_PORT_ERROR || cont_write == VDI_PORT_ERROR) { + _running = false; + } else if (cont_read == VDI_PORT_RESET || cont_write == VDI_PORT_RESET) { + _chunk_size = _chunk_port = 0; + write_agent_control(VD_AGENT_RESET, ++_connection_id); + _pending_reset = true; + } + if (cont) { + handle_port_data(); + } + if (_running && (!cont || _pending_read || _pending_write)) { + DWORD events_count = _events[VD_EVENTS_COUNT - 1] ? VD_EVENTS_COUNT : + VD_EVENTS_COUNT - 1; + DWORD wait_ret = WaitForMultipleObjectsEx(events_count, _events, FALSE, + cont ? 0 : INFINITE, TRUE); + switch (wait_ret) { + case WAIT_OBJECT_0: + break; + case WAIT_OBJECT_0 + 1: { + DWORD bytes = 0; + if (_pipe_connected && _pending_read) { + _pending_read = false; + if (GetOverlappedResult(_pipe_state.pipe, &_pipe_state.read.overlap, &bytes, + FALSE)) { + handle_pipe_data(bytes); + read_pipe(); + } else { + vd_printf("GetOverlappedResult failed %u", GetLastError()); + _pipe_connected = false; + DisconnectNamedPipe(_pipe_state.pipe); + } + } + break; + } + case WAIT_OBJECT_0 + 2: + vd_printf("Control event"); + break; + case WAIT_OBJECT_0 + 3: + vd_printf("Agent killed"); + restart_agent(false); + break; + case WAIT_IO_COMPLETION: + case WAIT_TIMEOUT: + break; + default: + vd_printf("WaitForMultipleObjectsEx failed %u", GetLastError()); + } + } + } + delete _vdi_port; + CloseHandle(pipe); + 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; + } else { + SetLastError(proc_ret.last_error); + } + } else { + ret = FALSE; + } + CloseHandle(named_pipe); + return ret; +} + +bool supported_system_version() +{ + OSVERSIONINFOEX osvi; + + ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX)); + osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFO); + if (!GetVersionEx((OSVERSIONINFO*)&osvi)) { + vd_printf("GetVersionEx() failed: %u", GetLastError()); + return false; + } + if (osvi.dwMajorVersion == 5 && osvi.dwMinorVersion == 1) { + return true; + } + return false; +} + +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 = TEXT("Winsta0\\winlogon"); + ZeroMemory(&_agent_proc_info, sizeof(_agent_proc_info)); + if (_session_id == 0) { + ret = CreateProcess(_agent_path, _agent_path, NULL, NULL, FALSE, 0, NULL, NULL, + &startup_info, &_agent_proc_info); + } else { + if (!supported_system_version()) { + vd_printf("create_session_process_as_user is not supported by this system version"); + return false; + } + for (int i = 0; i < 20; 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(500); + } + } + if (!ret) { + vd_printf("CreateProcess() failed: %u", GetLastError()); + return false; + } + _agent_alive = true; + if (_pipe_connected) { + vd_printf("Pipe already connected"); + return false; + } + vd_printf("Wait for vdagent to connect"); + if (ConnectNamedPipe(_pipe_state.pipe, NULL)) { + _pipe_connected = true; + _pending_reset = false; + vd_printf("Pipe connected by vdagent"); + } else { + vd_printf("ConnectNamedPipe() failed: %u", GetLastError()); + return false; + } + _events[VD_EVENTS_COUNT - 1] = _agent_proc_info.hProcess; + return true; +} + +bool VDService::kill_agent() +{ + DWORD exit_code = 0; + DWORD wait_ret; + bool ret = true; + + if (!_agent_alive) { + return true; + } + _events[VD_EVENTS_COUNT - 1] = 0; + _agent_alive = false; + if (_pipe_connected) { + _pipe_connected = false; + DisconnectNamedPipe(_pipe_state.pipe); + } + if (GetProcessId(_agent_proc_info.hProcess)) { + wait_ret = WaitForSingleObject(_agent_proc_info.hProcess, 3000); + switch (wait_ret) { + case WAIT_OBJECT_0: + if (GetExitCodeProcess(_agent_proc_info.hProcess, &exit_code)) { + vd_printf("vdagent exit code %u", exit_code); + } else if (exit_code == STILL_ACTIVE) { + vd_printf("Failed killing vdagent"); + ret = false; + } else { + vd_printf("GetExitCodeProcess() failed: %u", GetLastError()); + } + break; + case WAIT_TIMEOUT: + vd_printf("Wait timeout"); + ret = false; + break; + case WAIT_FAILED: + default: + vd_printf("WaitForSingleObject() failed: %u", GetLastError()); + break; + } + } + CloseHandle(_agent_proc_info.hProcess); + 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; + read_pipe(); + } + MUTEX_UNLOCK(_agent_mutex); + return ret; +} + +void VDService::stop() +{ + _running = false; + if (_control_event && !SetEvent(_control_event)) { + vd_printf("SetEvent() failed: %u", GetLastError()); + } +} + +VOID CALLBACK VDService::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap) +{ + VDService* s = _singleton; + VDPipeState* ps = &s->_pipe_state; + + s->_pending_write = false; + if (!s->_running) { + return; + } + if (err) { + vd_printf("error %u", err); + return; + } + ps->write.start += bytes; + if (ps->write.start < ps->write.end) { + s->_pending_write = true; + if (!WriteFileEx(ps->pipe, ps->write.data + ps->write.start, + ps->write.end - ps->write.start, overlap, write_completion)) { + vd_printf("WriteFileEx() failed: %u", GetLastError()); + s->_pending_write = false; + s->_pipe_connected = false; + DisconnectNamedPipe(s->_pipe_state.pipe); + } + } else { + s->_pending_write = false; + } +} + +void VDService::read_pipe() +{ + VDPipeState* ps = &_pipe_state; + DWORD bytes; + + if (ps->read.end < sizeof(ps->read.data)) { + _pending_read = true; + if (ReadFile(ps->pipe, ps->read.data + ps->read.end, sizeof(ps->read.data) - ps->read.end, + &bytes, &ps->read.overlap)) { + _pending_read = false; + vd_printf("ReadFile without pending"); + handle_pipe_data(bytes); + read_pipe(); + } else if (GetLastError() != ERROR_IO_PENDING) { + vd_printf("ReadFile() failed: %u", GetLastError()); + _pending_read = false; + _pipe_connected = false; + DisconnectNamedPipe(_pipe_state.pipe); + } + } else { + _pending_read = false; + } +} + +void VDService::handle_pipe_data(DWORD bytes) +{ + VDPipeState* ps = &_pipe_state; + DWORD read_size; + + _pending_read = false; + if (!_running) { + return; + } + ps->read.end += bytes; + while (_running && (read_size = ps->read.end - ps->read.start) >= sizeof(VDPipeMessage)) { + VDPipeMessage* pipe_msg = (VDPipeMessage*)&ps->read.data[ps->read.start]; + if (pipe_msg->type != VD_AGENT_COMMAND) { + handle_agent_control(pipe_msg); + ps->read.start += sizeof(VDPipeMessage); + continue; + } + if (read_size < VD_MESSAGE_HEADER_SIZE) { + break; + } + VDAgentMessage* msg = (VDAgentMessage*)pipe_msg->data; + DWORD chunk_size = sizeof(VDAgentMessage) + msg->size; + VDAgentDataChunk chunk; + if (read_size < VD_MESSAGE_HEADER_SIZE + msg->size) { + break; + } + if (!_pending_reset) { + chunk.port = pipe_msg->opaque; + chunk.size = chunk_size; + if (_vdi_port->ring_write(&chunk, sizeof(chunk)) != sizeof(chunk) || + _vdi_port->ring_write(msg, chunk_size) != chunk_size) { + vd_printf("ring_write failed"); + _running = false; + return; + } + } + ps->read.start += (VD_MESSAGE_HEADER_SIZE + msg->size); + if (ps->read.start == ps->read.end) { + ps->read.start = ps->read.end = 0; + } + } +} + +void VDService::handle_port_data() +{ + VDPipeMessage* pipe_msg; + VDAgentDataChunk chunk; + int chunks_count = 0; + DWORD count; + + while (_running) { + if (!_chunk_size && _vdi_port->read_ring_size() >= sizeof(chunk)) { + if (_vdi_port->ring_read(&chunk, sizeof(chunk)) != sizeof(chunk)) { + vd_printf("ring_read of chunk header failed"); + _running = false; + break; + } + count = sizeof(VDPipeMessage) + chunk.size; + if (_pipe_state.write.start == _pipe_state.write.end) { + _pipe_state.write.start = _pipe_state.write.end = 0; + } + if (_pipe_state.write.end + count > sizeof(_pipe_state.write.data)) { + vd_printf("chunk is too large, size %u port %u", chunk.size, chunk.port); + _running = false; + break; + } + _chunk_size = chunk.size; + _chunk_port = chunk.port; + } + if (_chunk_size && _vdi_port->read_ring_size() >= _chunk_size) { + ASSERT(_pipe_state.write.end + count <= sizeof(_pipe_state.write.data)); + pipe_msg = (VDPipeMessage*)&_pipe_state.write.data[_pipe_state.write.end]; + if (_vdi_port->ring_read(pipe_msg->data, _chunk_size) != _chunk_size) { + vd_printf("ring_read of chunk data failed"); + _running = false; + break; + } + if (_pipe_connected) { + pipe_msg->type = VD_AGENT_COMMAND; + pipe_msg->opaque = _chunk_port; + _pipe_state.write.end += count; + chunks_count++; + } else { + _pipe_state.write.start = _pipe_state.write.end = 0; + } + _chunk_size = 0; + _chunk_port = 0; + } else { + break; + } + } + if (_pipe_connected && chunks_count && !_pending_write) { + write_completion(0, 0, &_pipe_state.write.overlap); + } +} + +bool VDService::handle_agent_control(VDPipeMessage* msg) +{ + switch (msg->type) { + case VD_AGENT_RESET_ACK: { + if (msg->opaque != _connection_id) { + vd_printf("Agent reset ack mismatch %u %u", msg->opaque, _connection_id); + break; + } + vd_printf("Agent reset ack"); + _pending_reset = false; + break; + } + default: + vd_printf("Unsupported control %u %u", msg->type, msg->opaque); + return false; + } + return true; +} + +void VDService::write_agent_control(uint32_t type, uint32_t opaque) +{ + if (!_pipe_connected) { + return; + } + if (_pipe_state.write.end + sizeof(VDPipeMessage) > sizeof(_pipe_state.write.data)) { + vd_printf("msg is too large"); + _running = false; + return; + } + VDPipeMessage* msg = (VDPipeMessage*)&_pipe_state.write.data[_pipe_state.write.end]; + msg->type = type; + msg->opaque = opaque; + _pipe_state.write.end += sizeof(VDPipeMessage); + if (!_pending_write) { + write_completion(0, 0, &_pipe_state.write.overlap); + } +} + +int _tmain(int argc, TCHAR* argv[], TCHAR* envp[]) +{ + VDService* vdservice = VDService::get(); + if (argc > 1) { + if (lstrcmpi(argv[1], TEXT("install")) == 0) { + vdservice->install(); + } else if (lstrcmpi(argv[1], TEXT("uninstall")) == 0) { + vdservice->uninstall(); + } else { + printf("Use: vdservice install / uninstall\n"); + } + } else { + vdservice->run(); + } + delete vdservice; + return 0; +} + diff --git a/vdservice/vdservice.rc b/vdservice/vdservice.rc new file mode 100644 index 0000000..976325b --- /dev/null +++ b/vdservice/vdservice.rc @@ -0,0 +1,102 @@ +// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// Hebrew resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_HEB)
+#ifdef _WIN32
+LANGUAGE LANG_HEBREW, SUBLANG_DEFAULT
+#pragma code_page(1255)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 0,3,0,0
+ PRODUCTVERSION 0,3,0,0
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "000904b0"
+ BEGIN
+ VALUE "CompanyName", "Red Hat Inc."
+ VALUE "FileDescription", "Spice service"
+ VALUE "FileVersion", "0, 3, 0, 0"
+ VALUE "InternalName", "vdservice"
+ VALUE "LegalCopyright", "Copyright (c) 2009 Red Hat Inc. and/or its affiliates"
+ VALUE "OriginalFilename", "vdservice.exe"
+ VALUE "ProductName", "Red Hat Spice"
+ VALUE "ProductVersion", "0, 3, 0, 0"
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x9, 1200
+ END
+END
+
+#endif // Hebrew resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/vdservice/vdservice.vcproj b/vdservice/vdservice.vcproj new file mode 100644 index 0000000..bad8505 --- /dev/null +++ b/vdservice/vdservice.vcproj @@ -0,0 +1,229 @@ +<?xml version="1.0" encoding="windows-1255"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="vdservice"
+ ProjectGUID="{ADFE5E22-31D0-4343-AE9E-8102CC0051F9}"
+ RootNamespace="redservice"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories="..\common;$(SPICE_COMMON_DIR)"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE,_WIN32_WINNT=0x0501"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ CharacterSet="1"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories="..\common;$(SPICE_COMMON_DIR)"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE,_WIN32_WINNT=0x0501"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="true"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ <File
+ RelativePath=".\resource.h"
+ >
+ </File>
+ <File
+ RelativePath="..\common\vdcommon.h"
+ >
+ </File>
+ <File
+ RelativePath=".\vdi_port.h"
+ >
+ </File>
+ <File
+ RelativePath="..\common\vdlog.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ <File
+ RelativePath=".\vdservice.rc"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\vdi_port.cpp"
+ >
+ </File>
+ <File
+ RelativePath="..\common\vdlog.cpp"
+ >
+ </File>
+ <File
+ RelativePath=".\vdservice.cpp"
+ >
+ </File>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
|