summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorArnon Gilboa <agilboa@redhat.com>2009-11-05 13:02:18 +0200
committerYaniv Kamay <ykamay@redhat.com>2009-11-05 13:02:54 +0200
commit5ee7b5b27021ff68428f127648da728f45c68cd9 (patch)
tree0540e134bcfbac785546bb0d18573096fbffe667
fresh start
-rw-r--r--common/stdint.h394
-rw-r--r--common/vdcommon.h58
-rw-r--r--common/vdlog.cpp114
-rw-r--r--common/vdlog.h60
-rw-r--r--vdagent.sln26
-rw-r--r--vdagent/desktop_layout.cpp195
-rw-r--r--vdagent/desktop_layout.h84
-rw-r--r--vdagent/resource.h14
-rw-r--r--vdagent/vdagent.cpp598
-rw-r--r--vdagent/vdagent.rc102
-rw-r--r--vdagent/vdagent.vcproj230
-rw-r--r--vdservice/resource.h14
-rw-r--r--vdservice/vdi_port.cpp185
-rw-r--r--vdservice/vdi_port.h68
-rw-r--r--vdservice/vdservice.cpp947
-rw-r--r--vdservice/vdservice.rc102
-rw-r--r--vdservice/vdservice.vcproj229
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>