diff --git a/include/freerdp/channels/wtsvc.h b/include/freerdp/channels/wtsvc.h index 2f439e7fe..652f501a1 100644 --- a/include/freerdp/channels/wtsvc.h +++ b/include/freerdp/channels/wtsvc.h @@ -25,9 +25,8 @@ * Difference between the MS API are documented in this header. All functions * are implemented in and integrated with libfreerdp-channels. * - * Thread-safety: the API is designed to be safe to use one thread per channel. - * To access a virtual channel from multiple threads, one must provide his own - * machanism to protect the virtual channel handle. + * Unlike MS API, all functions except WTSVirtualChannelOpenEx in this + * implementation are thread-safe. */ #ifndef __FREERDP_WTSVC_H @@ -36,6 +35,8 @@ #include #include +typedef struct WTSVirtualChannelManager WTSVirtualChannelManager; + #define WTS_CHANNEL_OPTION_DYNAMIC 0x00000001 typedef enum _WTS_VIRTUAL_CLASS @@ -44,15 +45,26 @@ typedef enum _WTS_VIRTUAL_CLASS WTSVirtualFileHandle } WTS_VIRTUAL_CLASS; +/** + * WTSVirtualChannelManager functions are FreeRDP extensions to the API. + */ +FREERDP_API WTSVirtualChannelManager* WTSCreateVirtualChannelManager(freerdp_peer* client); +FREERDP_API void WTSDestroyVirtualChannelManager(WTSVirtualChannelManager* vcm); +FREERDP_API void WTSVirtualChannelManagerGetFileDescriptor(WTSVirtualChannelManager* vcm, + void** fds, int* fds_count); +FREERDP_API boolean WTSVirtualChannelManagerCheckFileDescriptor(WTSVirtualChannelManager* vcm); + /** * Opens a static or dynamic virtual channel and return the handle. If the * operation fails, a NULL handle is returned. * * The original MS API has 'DWORD SessionId' as the first argument, while we - * just use the server peer instance (representing the session) instead. + * use our WTSVirtualChannelManager object instead. + * + * This functions should be called only from the main thread. */ FREERDP_API void* WTSVirtualChannelOpenEx( - /* __in */ freerdp_peer* client, + /* __in */ WTSVirtualChannelManager* vcm, /* __in */ const char* pVirtualName, /* __in */ uint32 flags); diff --git a/libfreerdp-channels/wtsvc.c b/libfreerdp-channels/wtsvc.c index 24d68ea4e..88f3d0c26 100644 --- a/libfreerdp-channels/wtsvc.c +++ b/libfreerdp-channels/wtsvc.c @@ -25,8 +25,83 @@ #include "wtsvc.h" +struct send_item +{ + uint16 channel_id; + uint8* buffer; + uint32 length; +}; + +static void send_item_free(struct send_item* item) +{ + xfree(item->buffer); + xfree(item); +} + +WTSVirtualChannelManager* WTSCreateVirtualChannelManager(freerdp_peer* client) +{ + WTSVirtualChannelManager* vcm; + + vcm = xnew(WTSVirtualChannelManager); + if (vcm != NULL) + { + vcm->client = client; + vcm->send_event = wait_obj_new(); + vcm->send_queue = list_new(); + vcm->mutex = freerdp_mutex_new(); + } + + return vcm; +} + +void WTSDestroyVirtualChannelManager(WTSVirtualChannelManager* vcm) +{ + struct send_item* item; + + if (vcm != NULL) + { + wait_obj_free(vcm->send_event); + while ((item = (struct send_item*) list_dequeue(vcm->send_queue)) != NULL) + { + send_item_free(item); + } + list_free(vcm->send_queue); + freerdp_mutex_free(vcm->mutex); + xfree(vcm); + } +} + +void WTSVirtualChannelManagerGetFileDescriptor(WTSVirtualChannelManager* vcm, + void** fds, int* fds_count) +{ + wait_obj_get_fds(vcm->send_event, fds, fds_count); +} + +boolean WTSVirtualChannelManagerCheckFileDescriptor(WTSVirtualChannelManager* vcm) +{ + boolean result = true; + struct send_item* item; + + wait_obj_clear(vcm->send_event); + + freerdp_mutex_lock(vcm->mutex); + while ((item = (struct send_item*) list_dequeue(vcm->send_queue)) != NULL) + { + if (vcm->client->SendChannelData(vcm->client, item->channel_id, item->buffer, item->length) == false) + { + result = false; + } + send_item_free(item); + if (result == false) + break; + } + freerdp_mutex_unlock(vcm->mutex); + + return result; +} + void* WTSVirtualChannelOpenEx( - /* __in */ freerdp_peer* client, + /* __in */ WTSVirtualChannelManager* vcm, /* __in */ const char* pVirtualName, /* __in */ uint32 flags) { @@ -34,6 +109,7 @@ void* WTSVirtualChannelOpenEx( int len; rdpPeerChannel* channel; const char* channel_name; + freerdp_peer* client = vcm->client; channel_name = ((flags & WTS_CHANNEL_OPTION_DYNAMIC) != 0 ? "drdynvc" : pVirtualName); @@ -53,6 +129,7 @@ void* WTSVirtualChannelOpenEx( return NULL; channel = xnew(rdpPeerChannel); + channel->vcm = vcm; channel->client = client; channel->channel_id = client->settings->channels[i].channel_id; if ((flags & WTS_CHANNEL_OPTION_DYNAMIC) != 0) @@ -99,7 +176,40 @@ boolean WTSVirtualChannelWrite( /* __in */ uint32 Length, /* __out */ uint32* pBytesWritten) { - return false; + uint32 written = 0; + boolean result = false; + struct send_item* item; + rdpPeerChannel* channel = (rdpPeerChannel*) hChannelHandle; + WTSVirtualChannelManager* vcm = channel->vcm; + + if (channel == NULL) + return false; + + if (channel->channel_type == RDP_PEER_CHANNEL_TYPE_SVC) + { + item = xnew(struct send_item); + item->channel_id = channel->channel_id; + item->buffer = xmalloc(Length); + item->length = Length; + memcpy(item->buffer, Buffer, Length); + + freerdp_mutex_lock(vcm->mutex); + list_enqueue(vcm->send_queue, item); + freerdp_mutex_unlock(vcm->mutex); + + wait_obj_set(vcm->send_event); + + written = Length; + result = true; + } + else + { + /* TODO: Send to DVC channel */ + } + + if (pBytesWritten != NULL) + *pBytesWritten = written; + return result; } boolean WTSVirtualChannelClose( diff --git a/libfreerdp-channels/wtsvc.h b/libfreerdp-channels/wtsvc.h index 795e4090b..ce71958cc 100644 --- a/libfreerdp-channels/wtsvc.h +++ b/libfreerdp-channels/wtsvc.h @@ -23,6 +23,7 @@ #include #include #include +#include #include enum @@ -33,9 +34,18 @@ enum typedef struct rdp_peer_channel { + WTSVirtualChannelManager* vcm; freerdp_peer* client; uint16 channel_id; uint16 channel_type; } rdpPeerChannel; +struct WTSVirtualChannelManager +{ + freerdp_peer* client; + struct wait_obj* send_event; + LIST* send_queue; + freerdp_mutex mutex; +}; + #endif /* __WTSVC_H */ diff --git a/server/test/tfreerdp.c b/server/test/tfreerdp.c index 78a1a146e..90743c6a1 100644 --- a/server/test/tfreerdp.c +++ b/server/test/tfreerdp.c @@ -55,6 +55,7 @@ struct test_peer_context int icon_x; int icon_y; boolean activated; + WTSVirtualChannelManager* vcm; void* debug_channel; }; typedef struct test_peer_context testPeerContext; @@ -71,6 +72,8 @@ void test_peer_context_new(freerdp_peer* client, testPeerContext* context) context->icon_x = -1; context->icon_y = -1; + + context->vcm = WTSCreateVirtualChannelManager(client); } void test_peer_context_free(freerdp_peer* client, testPeerContext* context) @@ -81,6 +84,11 @@ void test_peer_context_free(freerdp_peer* client, testPeerContext* context) xfree(context->icon_data); xfree(context->bg_data); rfx_context_free(context->rfx_context); + if (context->debug_channel) + { + WTSVirtualChannelClose(context->debug_channel); + } + WTSDestroyVirtualChannelManager(context->vcm); xfree(context); } } @@ -354,10 +362,11 @@ boolean tf_peer_post_connect(freerdp_peer* client) { if (strncmp(client->settings->channels[i].name, "rdpdbg", 6) == 0) { - context->debug_channel = WTSVirtualChannelOpenEx(client, "rdpdbg", 0); + context->debug_channel = WTSVirtualChannelOpenEx(context->vcm, "rdpdbg", 0); if (context->debug_channel != NULL) { printf("Open channel rdpdbg.\n"); + WTSVirtualChannelWrite(context->debug_channel, (uint8*) "test1", 5, NULL); } } } @@ -442,6 +451,7 @@ static void* test_peer_mainloop(void* arg) int rcount; void* rfds[32]; fd_set rfds_set; + testPeerContext* context; freerdp_peer* client = (freerdp_peer*) arg; memset(rfds, 0, sizeof(rfds)); @@ -464,6 +474,7 @@ static void* test_peer_mainloop(void* arg) client->input->ExtendedMouseEvent = tf_peer_extended_mouse_event; client->Initialize(client); + context = (testPeerContext*) client->context; printf("We've got a client %s\n", client->hostname); @@ -476,6 +487,7 @@ static void* test_peer_mainloop(void* arg) printf("Failed to get FreeRDP file descriptor\n"); break; } + WTSVirtualChannelManagerGetFileDescriptor(context->vcm, rfds, &rcount); max_fds = 0; FD_ZERO(&rfds_set); @@ -508,6 +520,8 @@ static void* test_peer_mainloop(void* arg) if (client->CheckFileDescriptor(client) != true) break; + if (WTSVirtualChannelManagerCheckFileDescriptor(context->vcm) != true) + break; } printf("Client %s disconnected.\n", client->hostname);