From: Arnon Gilboa <agil...@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 _______________________________________________ Spice-devel mailing list Spice-devel@lists.freedesktop.org http://lists.freedesktop.org/mailman/listinfo/spice-devel