[Spice-devel] [PATCH] spice: vdagent: virtio serial adaptations
Arnon Gilboa
agilboa at redhat.com
Mon Aug 9 02:58:40 PDT 2010
From: Arnon Gilboa <agilboa at agilboa.usersys.redhat.com>
-mostly based on Alon's patches with cleanup
-adapt vdi_port class to virtio serial
-replace vdi_port callbacks with events
-add get_device_path() - using setupapi.lib
-add struct VDIPortBuffer for read/write buffers
-add VD_EVENT_XXX enum for clarity
-add DEBUG_VDSERVICE for debugging as standalone executable
-add CLIPBOARD_ENABLED ifdefs in the agent for disabling clipboard support
-remove mutex.h usage, deines in vdcommon.h instead
-include the common spice/vd_agent.h
---
common/vdcommon.h | 8 ++-
vdagent/desktop_layout.cpp | 1 +
vdagent/desktop_layout.h | 2 +-
vdagent/vdagent.cpp | 7 ++
vdservice/vdi_port.cpp | 255 +++++++++++++++++++++++++++++++-------------
vdservice/vdi_port.h | 33 ++++---
vdservice/vdservice.cpp | 135 +++++++++++++++---------
vdservice/vdservice.vcproj | 4 +-
8 files changed, 305 insertions(+), 140 deletions(-)
diff --git a/common/vdcommon.h b/common/vdcommon.h
index c807cbb..9095404 100644
--- a/common/vdcommon.h
+++ b/common/vdcommon.h
@@ -21,9 +21,15 @@
#pragma warning(disable:4200)
#include <windows.h>
-#include "vd_agent.h"
+#include "spice/vd_agent.h"
#include "vdlog.h"
+typedef CRITICAL_SECTION mutex_t;
+
+#define MUTEX_INIT(mutex) InitializeCriticalSection(&mutex)
+#define MUTEX_LOCK(mutex) EnterCriticalSection(&mutex)
+#define MUTEX_UNLOCK(mutex) LeaveCriticalSection(&mutex)
+
#define VD_SERVICE_PIPE_NAME TEXT("\\\\.\\pipe\\vdservicepipe")
#define VD_MESSAGE_HEADER_SIZE (sizeof(VDPipeMessage) + sizeof(VDAgentMessage))
#define VD_PIPE_BUF_SIZE (1024 * 1024)
diff --git a/vdagent/desktop_layout.cpp b/vdagent/desktop_layout.cpp
index 32502bc..4687483 100644
--- a/vdagent/desktop_layout.cpp
+++ b/vdagent/desktop_layout.cpp
@@ -16,6 +16,7 @@
*/
#include "desktop_layout.h"
+#include "vdcommon.h"
#include "vdlog.h"
void DisplayMode::set_res(DWORD width, DWORD height, DWORD depth)
diff --git a/vdagent/desktop_layout.h b/vdagent/desktop_layout.h
index 797a82c..22bbc8f 100644
--- a/vdagent/desktop_layout.h
+++ b/vdagent/desktop_layout.h
@@ -18,7 +18,7 @@
#ifndef _H_DESKTOP_LAYOUT
#define _H_DESKTOP_LAYOUT
-#include "mutex.h"
+#include "vdcommon.h"
#include <vector>
class DisplayMode {
diff --git a/vdagent/vdagent.cpp b/vdagent/vdagent.cpp
index bf5f5f5..d6518a1 100644
--- a/vdagent/vdagent.cpp
+++ b/vdagent/vdagent.cpp
@@ -19,6 +19,8 @@
#include "desktop_layout.h"
#include <lmcons.h>
+#define CLIPBOARD_ENABLED
+
#define VD_AGENT_LOG_PATH TEXT("%svdagent.log")
#define VD_AGENT_WINCLASS_NAME TEXT("VDAGENT")
#define VD_INPUT_INTERVAL_MS 20
@@ -608,6 +610,7 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port)
a->_running = false;
}
break;
+#ifdef CLIPBOARD_ENABLED
case VD_AGENT_CLIPBOARD:
if (!a->handle_clipboard((VDAgentClipboard*)msg->data,
msg->size - sizeof(VDAgentClipboard))) {
@@ -615,6 +618,7 @@ void VDAgent::dispatch_message(VDAgentMessage* msg, uint32_t port)
a->_running = false;
}
break;
+#endif // CLIPBOARD_ENABLED
default:
vd_printf("Unsupported message type %u size %u", msg->type, msg->size);
}
@@ -670,6 +674,7 @@ VOID CALLBACK VDAgent::read_completion(DWORD err, DWORD bytes, LPOVERLAPPED over
} else {
memcpy((uint8_t*)a->_in_msg + a->_in_msg_pos, pipe_msg->data, pipe_msg->size);
a->_in_msg_pos += pipe_msg->size;
+ //vd_printf("DEBUG: pipe_msg size %u pos %u total %u", pipe_msg->size, a->_in_msg_pos, sizeof(VDAgentMessage) + a->_in_msg->size);
if (a->_in_msg_pos == sizeof(VDAgentMessage) + a->_in_msg->size) {
dispatch_message(a->_in_msg, 0);
a->_in_msg_pos = 0;
@@ -754,6 +759,7 @@ LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARA
case WM_TIMER:
a->send_input();
break;
+#ifdef CLIPBOARD_ENABLED
case WM_CHANGECBCHAIN:
if (a->_hwnd_next_viewer == (HWND)wparam) {
a->_hwnd_next_viewer = (HWND)lparam;
@@ -769,6 +775,7 @@ LRESULT CALLBACK VDAgent::wnd_proc(HWND hwnd, UINT message, WPARAM wparam, LPARA
}
SendMessage(a->_hwnd_next_viewer, message, wparam, lparam);
break;
+#endif // CLIPBOARD_ENABLED
default:
return DefWindowProc(hwnd, message, wparam, lparam);
}
diff --git a/vdservice/vdi_port.cpp b/vdservice/vdi_port.cpp
index 2de53b0..52bdfd8 100644
--- a/vdservice/vdi_port.cpp
+++ b/vdservice/vdi_port.cpp
@@ -19,31 +19,26 @@
#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
+const GUID GUID_VIOSERIAL_PORT =
+ {0x6fde7521, 0x1b65, 0x48ae, 0xb6, 0x28, 0x80, 0xbe, 0x62, 0x1, 0x60, 0x26};
-#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)
+// Current limitation of virtio-serial windows driver (RHBZ 617000)
+#define VIOSERIAL_PORT_MAX_WRITE_BYTES 2048
+#define VIOSERIAL_PORT_MAX_RETRIES 30
+#define VIOSERIAL_PORT_RETRY_INTERVAL_MS 1000
#define MIN(a, b) ((a) > (b) ? (b) : (a))
+VDIPort* VDIPort::_singleton;
+
VDIPort::VDIPort()
: _handle (INVALID_HANDLE_VALUE)
- , _event (NULL)
- , _write_start (_write_ring)
- , _write_end (_write_ring)
- , _read_start (_read_ring)
- , _read_end (_read_ring)
{
+ ZeroMemory(&_write, offsetof(VDIPortBuffer, ring));
+ _write.start = _write.end = _write.ring;
+ ZeroMemory(&_read, offsetof(VDIPortBuffer, ring));
+ _read.start = _read.end = _read.ring;
+ _singleton = this;
}
VDIPort::~VDIPort()
@@ -51,28 +46,84 @@ VDIPort::~VDIPort()
if (_handle != INVALID_HANDLE_VALUE) {
CloseHandle(_handle);
}
- if (_event) {
- CloseHandle(_event);
+ if (_read.overlap.hEvent) {
+ CloseHandle(_read.overlap.hEvent);
+ }
+ if (_write.overlap.hEvent) {
+ CloseHandle(_write.overlap.hEvent);
}
}
+#include <setupapi.h>
+
+//Based on device.cpp from vioserial test app
+//FIXME: remove this call & lib?
+PTCHAR get_device_path(IN LPGUID interface_guid)
+{
+ HDEVINFO dev_info;
+ SP_DEVICE_INTERFACE_DATA dev_interface;
+ PSP_DEVICE_INTERFACE_DETAIL_DATA dev_interface_detail = NULL;
+ ULONG len, req_len = 0;
+
+ dev_info = SetupDiGetClassDevs(interface_guid, NULL, NULL,
+ DIGCF_PRESENT | DIGCF_DEVICEINTERFACE);
+ if (dev_info == INVALID_HANDLE_VALUE) {
+ vd_printf("Cannot get class devices");
+ return NULL;
+ }
+ dev_interface.cbSize = sizeof(SP_DEVICE_INTERFACE_DATA);
+ if (!SetupDiEnumDeviceInterfaces(dev_info, 0, interface_guid, 0, &dev_interface)) {
+ vd_printf("Cannot get enumerate device interfaces");
+ SetupDiDestroyDeviceInfoList(dev_info);
+ return NULL;
+ }
+ SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface, NULL, 0, &req_len, NULL);
+ dev_interface_detail = (PSP_DEVICE_INTERFACE_DETAIL_DATA)LocalAlloc(LMEM_FIXED, req_len);
+ if (dev_interface_detail == NULL) {
+ vd_printf("Cannot allocate memory");
+ SetupDiDestroyDeviceInfoList(dev_info);
+ return NULL;
+ }
+ dev_interface_detail->cbSize = sizeof(SP_DEVICE_INTERFACE_DETAIL_DATA);
+ len = req_len;
+ if (!SetupDiGetDeviceInterfaceDetail(dev_info, &dev_interface, dev_interface_detail, len,
+ &req_len, NULL)) {
+ vd_printf("Cannot get device interface details.\n");
+ SetupDiDestroyDeviceInfoList(dev_info);
+ LocalFree(dev_interface_detail);
+ return NULL;
+ }
+ return dev_interface_detail->DevicePath;
+}
+
bool VDIPort::init()
{
- DWORD io_ret_len;
- _handle = CreateFile(VDI_PORT_DEV_NAME, GENERIC_READ | GENERIC_WRITE, 0, NULL,
- OPEN_EXISTING, 0, NULL);
+ PTCHAR path = NULL;
+
+ for (int retry = 0; retry < VIOSERIAL_PORT_MAX_RETRIES && path == NULL; retry++) {
+ if (path = get_device_path((LPGUID)&GUID_VIOSERIAL_PORT)) {
+ break;
+ }
+ Sleep(VIOSERIAL_PORT_RETRY_INTERVAL_MS);
+ }
+ if (path == NULL) {
+ vd_printf("GetDevicePath failed - device/driver missing?");
+ return false;
+ }
+ _handle = CreateFile(path, GENERIC_READ | GENERIC_WRITE , 0, NULL,
+ OPEN_EXISTING, FILE_FLAG_OVERLAPPED, NULL);
if (_handle == INVALID_HANDLE_VALUE) {
vd_printf("CreateFile() failed: %u", GetLastError());
return false;
}
- _event = CreateEvent(NULL, FALSE, FALSE, NULL);
- if (_event == NULL) {
+ _write.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (_write.overlap.hEvent == 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());
+ _read.overlap.hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+ if (_read.overlap.hEvent == NULL) {
+ vd_printf("CreateEvent() failed: %u", GetLastError());
return false;
}
return true;
@@ -80,53 +131,89 @@ bool VDIPort::init()
size_t VDIPort::write_ring_free_space()
{
- return (BUF_SIZE + _write_start - _write_end - 1) % BUF_SIZE;
+ return (BUF_SIZE + _write.start - _write.end - 1) % BUF_SIZE;
}
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 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);
+ 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);
+ 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);
+ memcpy(_write.ring, (uint8_t*)buf + n, size - n);
}
}
- _write_end = _write_ring + (_write_end - _write_ring + size) % BUF_SIZE;
+ _write.end = _write.ring + (_write.end - _write.ring + size) % BUF_SIZE;
return size;
}
int VDIPort::write()
{
int size;
- int n;
+ int ret;
- if (_write_start == _write_end) {
+ //FIXME: return VDI_PORT_NO_DATA
+ 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 (!_write.pending) {
+ if (_write.start < _write.end) {
+ size = (int)(_write.end - _write.start);
+ } else {
+ size = (int)(&_write.ring[BUF_SIZE] - _write.start);
+ }
+ size = MIN(size, VIOSERIAL_PORT_MAX_WRITE_BYTES);
+ _write.pending = true;
+ if (WriteFile(_handle, _write.start, size, NULL, &_write.overlap)) {
+ write_completion();
+ } if (GetLastError() != ERROR_IO_PENDING) {
+ return handle_error();
+ }
+ }
+ ret = _write.bytes;
+ _write.bytes = 0;
+ return ret;
+}
+
+void VDIPort::write_completion()
+{
+ DWORD bytes;
+
+ if (!_write.pending) {
+ return;
}
- if (!WriteFile(_handle, _write_start, size, (LPDWORD)&n, NULL)) {
- return handle_error();
+ if (!GetOverlappedResult(_handle, &_write.overlap, &bytes, FALSE)) {
+ vd_printf("GetOverlappedResult failed: %u", GetLastError());
+ return;
}
- _write_start = _write_ring + (_write_start - _write_ring + n) % BUF_SIZE;
- return n;
+ _write.start = _write.ring + (_write.start - _write.ring + bytes) % BUF_SIZE;
+ _write.bytes = bytes;
+ _write.pending = false;
}
size_t VDIPort::read_ring_size()
{
- return (BUF_SIZE + _read_end - _read_start) % BUF_SIZE;
+ return (BUF_SIZE + _read.end - _read.start) % BUF_SIZE;
+}
+
+size_t VDIPort::read_ring_continuous_remaining_size()
+{
+ DWORD size;
+
+ if (_read.start <= _read.end) {
+ size = MIN(BUF_SIZE - 1, (int)(&_read.ring[BUF_SIZE] - _read.end));
+ } else {
+ size = (DWORD)(_read.start - _read.end - 1);
+ }
+ return size;
}
size_t VDIPort::ring_read(void* buf, size_t size)
@@ -134,45 +221,67 @@ size_t VDIPort::ring_read(void* buf, size_t size)
size_t n;
size_t m = 0;
- if (_read_start == _read_end) {
+ 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);
+ 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);
+ 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);
+ 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;
+ _read.start = _read.ring + (_read.start - _read.ring + n + m) % BUF_SIZE;
return n + m;
}
int VDIPort::read()
{
int size;
- int n;
+ int ret;
- 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 (!_read.pending) {
+ //FIXME: read_ring_continuous_remaining_size? return VDI_PORT_BUFFER_FULL
+ if ((_read.end - _read.ring + 1) % BUF_SIZE == _read.start - _read.ring) {
+ vd_printf("DEBUG: buffer full");
+ 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);
+ }
+ _read.pending = true;
+ if (ReadFile(_handle, _read.end, size, NULL, &_read.overlap)) {
+ read_completion();
+ } else if (GetLastError() != ERROR_IO_PENDING) {
+ return handle_error();
+ }
}
- if (!ReadFile(_handle, _read_end, size, (LPDWORD)&n, NULL)) {
- return handle_error();
+ ret = _read.bytes;
+ _read.bytes = 0;
+ return ret;
+}
+
+void VDIPort::read_completion()
+{
+ DWORD bytes;
+
+ if (!GetOverlappedResult(_handle, &_read.overlap, &bytes, FALSE) &&
+ GetLastError() != ERROR_MORE_DATA) {
+ vd_printf("GetOverlappedResult failed: %u", GetLastError());
+ return;
}
- _read_end = _read_ring + (_read_end - _read_ring + n) % BUF_SIZE;
- return n;
+ _read.end = _read.ring + (_read.end - _read.ring + bytes) % BUF_SIZE;
+ _read.bytes = bytes;
+ _read.pending = false;
}
int VDIPort::handle_error()
@@ -180,8 +289,8 @@ 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;
+ _write.start = _write.end = _write.ring;
+ _read.start = _read.end = _read.ring;
return VDI_PORT_RESET;
default:
vd_printf("port io failed: %u", GetLastError());
diff --git a/vdservice/vdi_port.h b/vdservice/vdi_port.h
index 2fbfb19..035d305 100644
--- a/vdservice/vdi_port.h
+++ b/vdservice/vdi_port.h
@@ -21,16 +21,21 @@
#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 BUF_SIZE (1024 * 1024)
#define VDI_PORT_BLOCKED 0
#define VDI_PORT_RESET -1
#define VDI_PORT_ERROR -2
+typedef struct VDIPortBuffer {
+ OVERLAPPED overlap;
+ uint8_t* start;
+ uint8_t* end;
+ bool pending;
+ int bytes;
+ uint8_t ring[BUF_SIZE];
+} VDIPortBuffer;
+
class VDIPort {
public:
VDIPort();
@@ -40,22 +45,22 @@ public:
size_t write_ring_free_space();
size_t ring_read(void* buf, size_t size);
size_t read_ring_size();
+ size_t read_ring_continuous_remaining_size();
+ HANDLE get_write_event() { return _write.overlap.hEvent; }
+ HANDLE get_read_event() { return _read.overlap.hEvent; }
int write();
int read();
- HANDLE get_event() { return _event;}
-
+ void write_completion();
+ void read_completion();
+
private:
int handle_error();
private:
+ static VDIPort* _singleton;
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;
+ VDIPortBuffer _write;
+ VDIPortBuffer _read;
};
// Ring notes:
diff --git a/vdservice/vdservice.cpp b/vdservice/vdservice.cpp
index 7c6f976..7d13e56 100644
--- a/vdservice/vdservice.cpp
+++ b/vdservice/vdservice.cpp
@@ -23,7 +23,8 @@
#include <tlhelp32.h>
#include "vdcommon.h"
#include "vdi_port.h"
-#include "mutex.h"
+
+//#define DEBUG_VDSERVICE
#define VD_SERVICE_DISPLAY_NAME TEXT("RHEV Spice Agent")
#define VD_SERVICE_NAME TEXT("vdservice")
@@ -35,11 +36,20 @@
#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
#define WINLOGON_FILENAME TEXT("winlogon.exe")
#define CREATE_PROC_MAX_RETRIES 10
#define CREATE_PROC_INTERVAL_MS 500
+enum {
+ VD_EVENT_PIPE_READ = 0,
+ VD_EVENT_PIPE_WRITE,
+ VD_EVENT_CONTROL,
+ VD_EVENT_READ,
+ VD_EVENT_WRITE,
+ VD_EVENT_AGENT, // Must be before last
+ VD_EVENTS_COUNT // Must be last
+};
+
class VDService {
public:
static VDService* get();
@@ -55,7 +65,7 @@ private:
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 pipe_write_completion();
void write_agent_control(uint32_t type, uint32_t opaque);
void read_pipe();
void handle_pipe_data(DWORD bytes);
@@ -149,6 +159,7 @@ VDService::VDService()
ZeroMemory(_events, sizeof(_events));
_system_version = supported_system_version();
_control_event = CreateEvent(NULL, FALSE, FALSE, NULL);
+ _pipe_state.write.overlap.hEvent = 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);
@@ -158,14 +169,20 @@ VDService::VDService()
VDService::~VDService()
{
CloseHandle(_pipe_state.read.overlap.hEvent);
+ CloseHandle(_pipe_state.write.overlap.hEvent);
CloseHandle(_control_event);
delete _log;
}
bool VDService::run()
{
+#ifndef DEBUG_VDSERVICE
SERVICE_TABLE_ENTRY service_table[] = {{VD_SERVICE_NAME, main}, {0, 0}};
return !!StartServiceCtrlDispatcher(service_table);
+#else
+ main(0, NULL);
+ return true;
+#endif
}
bool VDService::install()
@@ -329,7 +346,9 @@ VOID WINAPI VDService::main(DWORD argc, TCHAR* argv[])
NULL);
if (!s->_status_handle) {
printf("RegisterServiceCtrlHandler failed\n");
+#ifndef DEBUG_VDSERVICE
return;
+#endif // DEBUG_VDSERVICE
}
// service is starting
@@ -351,7 +370,9 @@ VOID WINAPI VDService::main(DWORD argc, TCHAR* argv[])
// service is stopped
status->dwControlsAccepted &= ~VDSERVICE_ACCEPTED_CONTROLS;
status->dwCurrentState = SERVICE_STOPPED;
+#ifndef DEBUG_VDSERVICE
SetServiceStatus(s->_status_handle, status);
+#endif //DEBUG_VDSERVICE
}
typedef __declspec (align(1)) struct VDAgentDataChunk {
@@ -400,10 +421,12 @@ bool VDService::execute()
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;
+ _events[VD_EVENT_PIPE_READ] = _pipe_state.read.overlap.hEvent;
+ _events[VD_EVENT_PIPE_WRITE] = _pipe_state.write.overlap.hEvent;
+ _events[VD_EVENT_CONTROL] = _control_event;
+ _events[VD_EVENT_READ] = _vdi_port->get_read_event();
+ _events[VD_EVENT_WRITE] = _vdi_port->get_write_event();
+ _events[VD_EVENT_AGENT] = _agent_proc_info.hProcess;
_chunk_size = _chunk_port = 0;
read_pipe();
while (_running) {
@@ -429,19 +452,16 @@ bool VDService::execute()
handle_pipe_data(0);
}
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);
+ DWORD events_count = _events[VD_EVENT_AGENT] ? VD_EVENTS_COUNT : VD_EVENTS_COUNT - 1;
+ DWORD wait_ret = WaitForMultipleObjects(events_count, _events, FALSE,
+ cont ? 0 : INFINITE);
switch (wait_ret) {
- case WAIT_OBJECT_0:
- break;
- case WAIT_OBJECT_0 + 1: {
+ case WAIT_OBJECT_0 + VD_EVENT_PIPE_READ: {
DWORD bytes = 0;
if (_pipe_connected && _pending_read) {
_pending_read = false;
- if (GetOverlappedResult(_pipe_state.pipe, &_pipe_state.read.overlap, &bytes,
- FALSE)) {
+ if (GetOverlappedResult(_pipe_state.pipe, &_pipe_state.read.overlap,
+ &bytes, FALSE) || GetLastError() == ERROR_MORE_DATA) {
handle_pipe_data(bytes);
read_pipe();
} else {
@@ -452,10 +472,19 @@ bool VDService::execute()
}
break;
}
- case WAIT_OBJECT_0 + 2:
+ case WAIT_OBJECT_0 + VD_EVENT_PIPE_WRITE:
+ pipe_write_completion();
+ break;
+ case WAIT_OBJECT_0 + VD_EVENT_CONTROL:
vd_printf("Control event");
break;
- case WAIT_OBJECT_0 + 3:
+ case WAIT_OBJECT_0 + VD_EVENT_READ:
+ _vdi_port->read_completion();
+ break;
+ case WAIT_OBJECT_0 + VD_EVENT_WRITE:
+ _vdi_port->write_completion();
+ break;
+ case WAIT_OBJECT_0 + VD_EVENT_AGENT:
vd_printf("Agent killed");
if (_system_version == SYS_VER_WIN_XP) {
restart_agent(false);
@@ -463,11 +492,10 @@ bool VDService::execute()
kill_agent();
}
break;
- case WAIT_IO_COMPLETION:
case WAIT_TIMEOUT:
break;
default:
- vd_printf("WaitForMultipleObjectsEx failed %u", GetLastError());
+ vd_printf("WaitForMultipleObjects failed %u", GetLastError());
}
}
}
@@ -766,7 +794,7 @@ bool VDService::launch_agent()
vd_printf("ConnectNamedPipe() failed: %u", GetLastError());
return false;
}
- _events[VD_EVENTS_COUNT - 1] = _agent_proc_info.hProcess;
+ _events[VD_EVENT_AGENT] = _agent_proc_info.hProcess;
return true;
}
@@ -779,7 +807,7 @@ bool VDService::kill_agent()
if (!_agent_alive) {
return true;
}
- _events[VD_EVENTS_COUNT - 1] = 0;
+ _events[VD_EVENT_AGENT] = 0;
_agent_alive = false;
if (_pipe_connected) {
_pipe_connected = false;
@@ -846,31 +874,40 @@ void VDService::stop()
}
}
-VOID CALLBACK VDService::write_completion(DWORD err, DWORD bytes, LPOVERLAPPED overlap)
+void VDService::pipe_write_completion()
{
- VDService* s = _singleton;
- VDPipeState* ps = &s->_pipe_state;
+ VDPipeState* ps = &this->_pipe_state;
+ DWORD bytes;
- s->_pending_write = false;
- if (!s->_running) {
- return;
- }
- if (err) {
- vd_printf("error %u", err);
+ if (!_running) {
return;
}
- ps->write.start += bytes;
+ if (_pending_write) {
+ if (GetOverlappedResult(_pipe_state.pipe, &_pipe_state.write.overlap, &bytes, FALSE)) {
+ ps->write.start += bytes;
+ if (ps->write.start == ps->write.end) {
+ ps->write.start = ps->write.end = 0;
+ }
+ } else if (GetLastError() == ERROR_IO_PENDING){
+ vd_printf("Overlapped write is pending");
+ return;
+ } else {
+ vd_printf("GetOverlappedResult() failed : %d", GetLastError());
+ }
+ _pending_write = false;
+ }
+
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);
+ _pending_write = true;
+ if (!WriteFile(ps->pipe, ps->write.data + ps->write.start,
+ ps->write.end - ps->write.start, NULL, &_pipe_state.write.overlap)) {
+ vd_printf("WriteFile() failed: %u", GetLastError());
+ _pending_write = false;
+ _pipe_connected = false;
+ DisconnectNamedPipe(_pipe_state.pipe);
}
} else {
- s->_pending_write = false;
+ _pending_write = false;
}
}
@@ -882,9 +919,9 @@ void VDService::read_pipe()
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)) {
+ &bytes, &ps->read.overlap) || GetLastError() == ERROR_MORE_DATA) {
_pending_read = false;
- vd_printf("ReadFile without pending");
+ vd_printf("ReadFile without pending %u", bytes);
handle_pipe_data(bytes);
read_pipe();
} else if (GetLastError() != ERROR_IO_PENDING) {
@@ -918,8 +955,11 @@ void VDService::handle_pipe_data(DWORD bytes)
ps->read.start += sizeof(VDPipeMessage);
continue;
}
- if (read_size < sizeof(VDPipeMessage) + pipe_msg->size ||
- _vdi_port->write_ring_free_space() < sizeof(VDAgentDataChunk) + pipe_msg->size) {
+ if (read_size < sizeof(VDPipeMessage) + pipe_msg->size) {
+ break;
+ }
+ if (_vdi_port->write_ring_free_space() < sizeof(VDAgentDataChunk) + pipe_msg->size) {
+ //vd_printf("DEBUG: no space in write ring %u", _vdi_port->write_ring_free_space());
break;
}
if (!_pending_reset) {
@@ -959,9 +999,6 @@ void VDService::handle_port_data()
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;
@@ -995,7 +1032,7 @@ void VDService::handle_port_data()
}
}
if (_pipe_connected && chunks_count && !_pending_write) {
- write_completion(0, 0, &_pipe_state.write.overlap);
+ pipe_write_completion();
}
}
@@ -1033,7 +1070,7 @@ void VDService::write_agent_control(uint32_t type, uint32_t opaque)
msg->opaque = opaque;
_pipe_state.write.end += sizeof(VDPipeMessage);
if (!_pending_write) {
- write_completion(0, 0, &_pipe_state.write.overlap);
+ pipe_write_completion();
}
}
diff --git a/vdservice/vdservice.vcproj b/vdservice/vdservice.vcproj
index 32ec7c8..1fd6827 100644
--- a/vdservice/vdservice.vcproj
+++ b/vdservice/vdservice.vcproj
@@ -65,7 +65,7 @@
/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib"
+ AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib setupapi.lib"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
@@ -216,7 +216,7 @@
/>
<Tool
Name="VCLinkerTool"
- AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib"
+ AdditionalDependencies="WtsApi32.lib Userenv.lib Version.lib setupapi.lib"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
--
1.5.5.6
More information about the Spice-devel
mailing list