summaryrefslogtreecommitdiffstats
path: root/src/VBox/HostServices/HostChannel
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostServices/HostChannel')
-rw-r--r--src/VBox/HostServices/HostChannel/HostChannel.cpp1020
-rw-r--r--src/VBox/HostServices/HostChannel/HostChannel.h137
-rw-r--r--src/VBox/HostServices/HostChannel/Makefile.kmk41
-rw-r--r--src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp901
-rw-r--r--src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc51
5 files changed, 2150 insertions, 0 deletions
diff --git a/src/VBox/HostServices/HostChannel/HostChannel.cpp b/src/VBox/HostServices/HostChannel/HostChannel.cpp
new file mode 100644
index 00000000..972959d2
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/HostChannel.cpp
@@ -0,0 +1,1020 @@
+/** @file
+ * Host channel.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/asm.h>
+#include <iprt/assert.h>
+
+#include "HostChannel.h"
+
+
+static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvInstance,
+ uint32_t u32Id, const void *pvEvent, uint32_t cbEvent);
+static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel);
+
+
+/* A registered provider of channels. */
+typedef struct VBOXHOSTCHPROVIDER
+{
+ int32_t volatile cRefs;
+
+ RTLISTNODE nodeContext; /* Member of the list of providers in the service context. */
+
+ VBOXHOSTCHCTX *pCtx;
+
+ VBOXHOSTCHANNELINTERFACE iface;
+
+ char *pszName;
+
+ RTLISTANCHOR listChannels;
+} VBOXHOSTCHPROVIDER;
+
+/* An established channel. */
+typedef struct VBOXHOSTCHINSTANCE
+{
+ int32_t volatile cRefs;
+
+ RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
+ RTLISTNODE nodeProvider; /* In the provider, needed for cleanup when the provider is unregistered. */
+
+ VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel. */
+ VBOXHOSTCHPROVIDER *pProvider; /* NULL if the provider was unregistered. */
+ void *pvChannel; /* Provider's context of the channel. */
+ uint32_t u32Handle; /* handle assigned to the channel by the service. */
+} VBOXHOSTCHINSTANCE;
+
+struct VBOXHOSTCHCTX
+{
+ bool fInitialized;
+
+ RTLISTANCHOR listProviders;
+};
+
+/* The channel callbacks context. The provider passes the pointer as a callback parameter.
+ * Created for the provider and deleted when the provider says so.
+ */
+typedef struct VBOXHOSTCHCALLBACKCTX
+{
+ RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
+
+ VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel, NULL when the client does not exist. */
+} VBOXHOSTCHCALLBACKCTX;
+
+/* Only one service instance is supported. */
+static VBOXHOSTCHCTX g_ctx = { false };
+
+static VBOXHOSTCHANNELCALLBACKS g_callbacks =
+{
+ HostChannelCallbackEvent,
+ HostChannelCallbackDeleted
+};
+
+
+/*
+ * Provider management.
+ */
+
+static void vhcProviderDestroy(VBOXHOSTCHPROVIDER *pProvider)
+{
+ RTStrFree(pProvider->pszName);
+}
+
+static int32_t vhcProviderAddRef(VBOXHOSTCHPROVIDER *pProvider)
+{
+ return ASMAtomicIncS32(&pProvider->cRefs);
+}
+
+static void vhcProviderRelease(VBOXHOSTCHPROVIDER *pProvider)
+{
+ int32_t c = ASMAtomicDecS32(&pProvider->cRefs);
+ Assert(c >= 0);
+ if (c == 0)
+ {
+ vhcProviderDestroy(pProvider);
+ RTMemFree(pProvider);
+ }
+}
+
+static VBOXHOSTCHPROVIDER *vhcProviderFind(VBOXHOSTCHCTX *pCtx, const char *pszName)
+{
+ VBOXHOSTCHPROVIDER *pProvider = NULL;
+
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHPROVIDER *pIter;
+ RTListForEach(&pCtx->listProviders, pIter, VBOXHOSTCHPROVIDER, nodeContext)
+ {
+ if (RTStrCmp(pIter->pszName, pszName) == 0)
+ {
+ pProvider = pIter;
+
+ vhcProviderAddRef(pProvider);
+
+ break;
+ }
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ return pProvider;
+}
+
+static int vhcProviderRegister(VBOXHOSTCHCTX *pCtx, VBOXHOSTCHPROVIDER *pProvider)
+{
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo check a duplicate. */
+
+ RTListAppend(&pCtx->listProviders, &pProvider->nodeContext);
+
+ vboxHostChannelUnlock();
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ vhcProviderRelease(pProvider);
+ }
+
+ return rc;
+}
+
+static int vhcProviderUnregister(VBOXHOSTCHPROVIDER *pProvider)
+{
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ /** @todo check that the provider is in the list. */
+ /** @todo mark the provider as invalid in each instance. also detach channels? */
+
+ RTListNodeRemove(&pProvider->nodeContext);
+
+ vboxHostChannelUnlock();
+
+ vhcProviderRelease(pProvider);
+ }
+
+ return rc;
+}
+
+
+/*
+ * Select an unique handle for the new channel.
+ * Works under the lock.
+ */
+static int vhcHandleCreate(VBOXHOSTCHCLIENT *pClient, uint32_t *pu32Handle)
+{
+ bool fOver = false;
+
+ for(;;)
+ {
+ uint32_t u32Handle = ASMAtomicIncU32(&pClient->u32HandleSrc);
+
+ if (u32Handle == 0)
+ {
+ if (fOver)
+ {
+ return VERR_NOT_SUPPORTED;
+ }
+
+ fOver = true;
+ continue;
+ }
+
+ VBOXHOSTCHINSTANCE *pDuplicate = NULL;
+ VBOXHOSTCHINSTANCE *pIter;
+ RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
+ {
+ if (pIter->u32Handle == u32Handle)
+ {
+ pDuplicate = pIter;
+ break;
+ }
+ }
+
+ if (pDuplicate == NULL)
+ {
+ *pu32Handle = u32Handle;
+ break;
+ }
+ }
+
+ return VINF_SUCCESS;
+}
+
+
+/*
+ * Channel instance management.
+ */
+
+static void vhcInstanceDestroy(VBOXHOSTCHINSTANCE *pInstance)
+{
+ RT_NOREF1(pInstance);
+ HOSTCHLOG(("HostChannel: destroy %p\n", pInstance));
+}
+
+static int32_t vhcInstanceAddRef(VBOXHOSTCHINSTANCE *pInstance)
+{
+ HOSTCHLOG(("INST: %p %d addref\n", pInstance, pInstance->cRefs));
+ return ASMAtomicIncS32(&pInstance->cRefs);
+}
+
+static void vhcInstanceRelease(VBOXHOSTCHINSTANCE *pInstance)
+{
+ int32_t c = ASMAtomicDecS32(&pInstance->cRefs);
+ HOSTCHLOG(("INST: %p %d release\n", pInstance, pInstance->cRefs));
+ Assert(c >= 0);
+ if (c == 0)
+ {
+ vhcInstanceDestroy(pInstance);
+ RTMemFree(pInstance);
+ }
+}
+
+static int vhcInstanceCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE **ppInstance)
+{
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = (VBOXHOSTCHINSTANCE *)RTMemAllocZ(sizeof(VBOXHOSTCHINSTANCE));
+
+ if (pInstance)
+ {
+ rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vhcHandleCreate(pClient, &pInstance->u32Handle);
+
+ if (RT_SUCCESS(rc))
+ {
+ /* Used by the client, that is in the list of channels. */
+ vhcInstanceAddRef(pInstance);
+ /* Add to the list of created channel instances. It is inactive while pClient is 0. */
+ RTListAppend(&pClient->listChannels, &pInstance->nodeClient);
+
+ /* Return to the caller. */
+ vhcInstanceAddRef(pInstance);
+ *ppInstance = pInstance;
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ RTMemFree(pInstance);
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+static VBOXHOSTCHINSTANCE *vhcInstanceFind(VBOXHOSTCHCLIENT *pClient, uint32_t u32Handle)
+{
+ VBOXHOSTCHINSTANCE *pInstance = NULL;
+
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHINSTANCE *pIter;
+ RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
+ {
+ if ( pIter->pClient
+ && pIter->u32Handle == u32Handle)
+ {
+ pInstance = pIter;
+
+ vhcInstanceAddRef(pInstance);
+
+ break;
+ }
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ return pInstance;
+}
+
+static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient, void *pvChannel)
+{
+ VBOXHOSTCHINSTANCE *pInstance = NULL;
+
+ if (pvChannel == NULL)
+ {
+ return NULL;
+ }
+
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHINSTANCE *pIter;
+ RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
+ {
+ if ( pIter->pClient
+ && pIter->pvChannel == pvChannel)
+ {
+ pInstance = pIter;
+
+ vhcInstanceAddRef(pInstance);
+
+ break;
+ }
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ return pInstance;
+}
+
+static void vhcInstanceDetach(VBOXHOSTCHINSTANCE *pInstance)
+{
+ HOSTCHLOG(("HostChannel: detach %p\n", pInstance));
+
+ if (pInstance->pProvider)
+ {
+ pInstance->pProvider->iface.HostChannelDetach(pInstance->pvChannel);
+ RTListNodeRemove(&pInstance->nodeProvider);
+ vhcProviderRelease(pInstance->pProvider);
+ pInstance->pProvider = NULL;
+ vhcInstanceRelease(pInstance); /* Not in the provider's list anymore. */
+ }
+
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ RTListNodeRemove(&pInstance->nodeClient);
+
+ vboxHostChannelUnlock();
+
+ vhcInstanceRelease(pInstance); /* Not used by the client anymore. */
+ }
+}
+
+/*
+ * Channel callback contexts.
+ */
+static int vhcCallbackCtxCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHCALLBACKCTX **ppCallbackCtx)
+{
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)RTMemAllocZ(sizeof(VBOXHOSTCHCALLBACKCTX));
+
+ if (pCallbackCtx != NULL)
+ {
+ /* The callback context is accessed by the providers threads. */
+ rc = vboxHostChannelLock();
+ if (RT_SUCCESS(rc))
+ {
+ RTListAppend(&pClient->listContexts, &pCallbackCtx->nodeClient);
+ pCallbackCtx->pClient = pClient;
+
+ vboxHostChannelUnlock();
+ }
+ else
+ {
+ RTMemFree(pCallbackCtx);
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ *ppCallbackCtx = pCallbackCtx;
+ }
+
+ return rc;
+}
+
+static int vhcCallbackCtxDelete(VBOXHOSTCHCALLBACKCTX *pCallbackCtx)
+{
+ int rc = vboxHostChannelLock();
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient;
+
+ if (pClient != NULL)
+ {
+ /* The callback is associated with a client.
+ * Check that the callback is in the list and remove it from the list.
+ */
+ bool fFound = false;
+
+ VBOXHOSTCHCALLBACKCTX *pIter;
+ RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient)
+ {
+ if (pIter == pCallbackCtx)
+ {
+ fFound = true;
+ break;
+ }
+ }
+
+ if (fFound)
+ {
+ RTListNodeRemove(&pCallbackCtx->nodeClient);
+ }
+ else
+ {
+ AssertFailed();
+ rc = VERR_INVALID_PARAMETER;
+ }
+ }
+ else
+ {
+ /* It is not in the clients anymore. May be the client has been disconnected.
+ * Just free the memory.
+ */
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ RTMemFree(pCallbackCtx);
+ }
+
+ return rc;
+}
+
+/*
+ * Host channel service functions.
+ */
+
+int vboxHostChannelInit(void)
+{
+ VBOXHOSTCHCTX *pCtx = &g_ctx;
+
+ if (pCtx->fInitialized)
+ {
+ return VERR_NOT_SUPPORTED;
+ }
+
+ pCtx->fInitialized = true;
+ RTListInit(&pCtx->listProviders);
+
+ return VINF_SUCCESS;
+}
+
+void vboxHostChannelDestroy(void)
+{
+ VBOXHOSTCHCTX *pCtx = &g_ctx;
+
+ VBOXHOSTCHPROVIDER *pIter;
+ VBOXHOSTCHPROVIDER *pIterNext;
+ RTListForEachSafe(&pCtx->listProviders, pIter, pIterNext, VBOXHOSTCHPROVIDER, nodeContext)
+ {
+ vhcProviderUnregister(pIter);
+ }
+ pCtx->fInitialized = false;
+}
+
+int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient)
+{
+ /* A guest client is connecting to the service.
+ * Later the client will use Attach calls to connect to channel providers.
+ * pClient is already zeroed.
+ */
+ pClient->pCtx = &g_ctx;
+
+ RTListInit(&pClient->listChannels);
+ RTListInit(&pClient->listEvents);
+ RTListInit(&pClient->listContexts);
+
+ return VINF_SUCCESS;
+}
+
+void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient)
+{
+ /* Clear the list of contexts and prevent acceess to the client. */
+ int rc = vboxHostChannelLock();
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHCALLBACKCTX *pIter;
+ VBOXHOSTCHCALLBACKCTX *pNext;
+ RTListForEachSafe(&pClient->listContexts, pIter, pNext, VBOXHOSTCHCALLBACKCTX, nodeClient)
+ {
+ pIter->pClient = NULL;
+ RTListNodeRemove(&pIter->nodeClient);
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ /* If there are attached channels, detach them. */
+ VBOXHOSTCHINSTANCE *pIter;
+ VBOXHOSTCHINSTANCE *pIterNext;
+ RTListForEachSafe(&pClient->listChannels, pIter, pIterNext, VBOXHOSTCHINSTANCE, nodeClient)
+ {
+ vhcInstanceDetach(pIter);
+ }
+}
+
+int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient,
+ uint32_t *pu32Handle,
+ const char *pszName,
+ uint32_t u32Flags)
+{
+ int rc = VINF_SUCCESS;
+
+ HOSTCHLOG(("HostChannel: Attach: (%d) [%s] 0x%08X\n", pClient->u32ClientID, pszName, u32Flags));
+
+ /* Look if there is a provider. */
+ VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
+
+ if (pProvider)
+ {
+ VBOXHOSTCHINSTANCE *pInstance = NULL;
+
+ rc = vhcInstanceCreate(pClient, &pInstance);
+
+ if (RT_SUCCESS(rc))
+ {
+ VBOXHOSTCHCALLBACKCTX *pCallbackCtx = NULL;
+ rc = vhcCallbackCtxCreate(pClient, &pCallbackCtx);
+
+ if (RT_SUCCESS(rc))
+ {
+ void *pvChannel = NULL;
+ rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider,
+ &pvChannel,
+ u32Flags,
+ &g_callbacks, pCallbackCtx);
+
+ if (RT_SUCCESS(rc))
+ {
+ vhcProviderAddRef(pProvider);
+ pInstance->pProvider = pProvider;
+
+ pInstance->pClient = pClient;
+ pInstance->pvChannel = pvChannel;
+
+ /* It is already in the channels list of the client. */
+
+ vhcInstanceAddRef(pInstance); /* Referenced by the list of provider's channels. */
+ RTListAppend(&pProvider->listChannels, &pInstance->nodeProvider);
+
+ *pu32Handle = pInstance->u32Handle;
+
+ HOSTCHLOG(("HostChannel: Attach: (%d) handle %d\n", pClient->u32ClientID, pInstance->u32Handle));
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ vhcCallbackCtxDelete(pCallbackCtx);
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ vhcInstanceDetach(pInstance);
+ }
+
+ vhcInstanceRelease(pInstance);
+ }
+
+ vhcProviderRelease(pProvider);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle)
+{
+ HOSTCHLOG(("HostChannel: Detach: (%d) handle %d\n", pClient->u32ClientID, u32Handle));
+
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
+
+ if (pInstance)
+ {
+ vhcInstanceDetach(pInstance);
+
+ vhcInstanceRelease(pInstance);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ const void *pvData,
+ uint32_t cbData)
+{
+ HOSTCHLOG(("HostChannel: Send: (%d) handle %d, %d bytes\n", pClient->u32ClientID, u32Handle, cbData));
+
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
+
+ if (pInstance)
+ {
+ if (pInstance->pProvider)
+ {
+ pInstance->pProvider->iface.HostChannelSend(pInstance->pvChannel, pvData, cbData);
+ }
+
+ vhcInstanceRelease(pInstance);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeReceived,
+ uint32_t *pu32SizeRemaining)
+{
+ HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
+
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
+
+ if (pInstance)
+ {
+ if (pInstance->pProvider)
+ {
+ rc = pInstance->pProvider->iface.HostChannelRecv(pInstance->pvChannel, pvData, cbData,
+ pu32SizeReceived, pu32SizeRemaining);
+
+ HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, cbData %d, recv %d, rem %d\n",
+ pClient->u32ClientID, u32Handle, rc, cbData, *pu32SizeReceived, *pu32SizeRemaining));
+ }
+
+ vhcInstanceRelease(pInstance);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned)
+{
+ HOSTCHLOG(("HostChannel: Control: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
+
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
+
+ if (pInstance)
+ {
+ if (pInstance->pProvider)
+ {
+ pInstance->pProvider->iface.HostChannelControl(pInstance->pvChannel, u32Code,
+ pvParm, cbParm,
+ pvData, cbData, pu32SizeDataReturned);
+ }
+
+ vhcInstanceRelease(pInstance);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+typedef struct VBOXHOSTCHANNELEVENT
+{
+ RTLISTNODE NodeEvent;
+
+ uint32_t u32ChannelHandle;
+
+ uint32_t u32Id;
+ void *pvEvent;
+ uint32_t cbEvent;
+} VBOXHOSTCHANNELEVENT;
+
+int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient,
+ bool *pfEvent,
+ VBOXHGCMCALLHANDLE callHandle,
+ VBOXHGCMSVCPARM *paParms)
+{
+ int rc = vboxHostChannelLock();
+ if (RT_FAILURE(rc))
+ {
+ return rc;
+ }
+
+ if (pClient->fAsync)
+ {
+ /* If there is a wait request already, cancel it. */
+ vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0);
+ pClient->fAsync = false;
+ }
+
+ /* Check if there is something in the client's event queue. */
+ VBOXHOSTCHANNELEVENT *pEvent = RTListGetFirst(&pClient->listEvents, VBOXHOSTCHANNELEVENT, NodeEvent);
+
+ HOSTCHLOG(("HostChannel: QueryEvent: (%d), event %p\n", pClient->u32ClientID, pEvent));
+
+ if (pEvent)
+ {
+ /* Report the event. */
+ RTListNodeRemove(&pEvent->NodeEvent);
+
+ HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbEvent %d\n",
+ pClient->u32ClientID, pEvent->cbEvent));
+
+ vboxHostChannelEventParmsSet(paParms, pEvent->u32ChannelHandle,
+ pEvent->u32Id, pEvent->pvEvent, pEvent->cbEvent);
+
+ *pfEvent = true;
+
+ RTMemFree(pEvent);
+ }
+ else
+ {
+ /* No event available at the time. Process asynchronously. */
+ pClient->fAsync = true;
+ pClient->async.callHandle = callHandle;
+ pClient->async.paParms = paParms;
+
+ /* Tell the caller that there is no event. */
+ *pfEvent = false;
+ }
+
+ vboxHostChannelUnlock();
+ return rc;
+}
+
+int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient)
+{
+ int rc = vboxHostChannelLock();
+
+ if (RT_SUCCESS(rc))
+ {
+ if (pClient->fAsync)
+ {
+ /* If there is a wait request alredy, cancel it. */
+ vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0);
+
+ pClient->fAsync = false;
+ }
+
+ vboxHostChannelUnlock();
+ }
+
+ return rc;
+}
+
+/* @thread provider */
+static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvChannel,
+ uint32_t u32Id, const void *pvEvent, uint32_t cbEvent)
+{
+ VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)pvCallbacks;
+
+ int rc = vboxHostChannelLock();
+ if (RT_FAILURE(rc))
+ {
+ return;
+ }
+
+ /* Check that the structure is still associated with a client.
+ * The client can disconnect and will be invalid.
+ */
+ VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient;
+
+ if (pClient == NULL)
+ {
+ vboxHostChannelUnlock();
+
+ HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client gone.\n", pvEvent));
+
+ /* The client does not exist anymore, skip the event. */
+ return;
+ }
+
+ bool fFound = false;
+
+ VBOXHOSTCHCALLBACKCTX *pIter;
+ RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient)
+ {
+ if (pIter == pCallbackCtx)
+ {
+ fFound = true;
+ break;
+ }
+ }
+
+ if (!fFound)
+ {
+ AssertFailed();
+
+ vboxHostChannelUnlock();
+
+ HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client does not have the context.\n", pvEvent));
+
+ /* The context is not in the list of contexts. Skip the event. */
+ return;
+ }
+
+ VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel);
+
+ HOSTCHLOG(("HostChannel: CallbackEvent[%p]: (%d) instance %p\n",
+ pCallbackCtx, pClient->u32ClientID, pInstance));
+
+ if (!pInstance)
+ {
+ /* Instance was already detached. Skip the event. */
+ vboxHostChannelUnlock();
+
+ return;
+ }
+
+ uint32_t u32ChannelHandle = pInstance->u32Handle;
+
+ HOSTCHLOG(("HostChannel: CallbackEvent: (%d) handle %d, async %d, cbEvent %d\n",
+ pClient->u32ClientID, u32ChannelHandle, pClient->fAsync, cbEvent));
+
+ /* Check whether the event is waited. */
+ if (pClient->fAsync)
+ {
+ /* Report the event. */
+ vboxHostChannelReportAsync(pClient, u32ChannelHandle, u32Id, pvEvent, cbEvent);
+
+ pClient->fAsync = false;
+ }
+ else
+ {
+ /* Put it to the queue. */
+ VBOXHOSTCHANNELEVENT *pEvent = (VBOXHOSTCHANNELEVENT *)RTMemAlloc(sizeof(VBOXHOSTCHANNELEVENT) + cbEvent);
+
+ if (pEvent)
+ {
+ pEvent->u32ChannelHandle = u32ChannelHandle;
+ pEvent->u32Id = u32Id;
+
+ if (cbEvent)
+ {
+ pEvent->pvEvent = &pEvent[1];
+ memcpy(pEvent->pvEvent, pvEvent, cbEvent);
+ }
+ else
+ {
+ pEvent->pvEvent = NULL;
+ }
+
+ pEvent->cbEvent = cbEvent;
+
+ RTListAppend(&pClient->listEvents, &pEvent->NodeEvent);
+ }
+ }
+
+ vboxHostChannelUnlock();
+
+ vhcInstanceRelease(pInstance);
+}
+
+/* @thread provider */
+static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel)
+{
+ RT_NOREF1(pvChannel);
+ vhcCallbackCtxDelete((VBOXHOSTCHCALLBACKCTX *)pvCallbacks);
+}
+
+int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient,
+ const char *pszName,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned)
+{
+ HOSTCHLOG(("HostChannel: Query: (%d) name [%s], cbData %d\n", pClient->u32ClientID, pszName, cbData));
+
+ int rc = VINF_SUCCESS;
+
+ /* Look if there is a provider. */
+ VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
+
+ if (pProvider)
+ {
+ pProvider->iface.HostChannelControl(NULL, u32Code,
+ pvParm, cbParm,
+ pvData, cbData, pu32SizeDataReturned);
+
+ vhcProviderRelease(pProvider);
+ }
+ else
+ {
+ rc = VERR_NOT_SUPPORTED;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelRegister(const char *pszName,
+ const VBOXHOSTCHANNELINTERFACE *pInterface,
+ uint32_t cbInterface)
+{
+ RT_NOREF1(cbInterface);
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHCTX *pCtx = &g_ctx;
+
+ VBOXHOSTCHPROVIDER *pProvider = (VBOXHOSTCHPROVIDER *)RTMemAllocZ(sizeof(VBOXHOSTCHPROVIDER));
+
+ if (pProvider)
+ {
+ pProvider->pCtx = pCtx;
+ pProvider->iface = *pInterface;
+
+ RTListInit(&pProvider->listChannels);
+
+ pProvider->pszName = RTStrDup(pszName);
+ if (pProvider->pszName)
+ {
+ vhcProviderAddRef(pProvider);
+ rc = vhcProviderRegister(pCtx, pProvider);
+ }
+ else
+ {
+ RTMemFree(pProvider);
+ rc = VERR_NO_MEMORY;
+ }
+ }
+ else
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+}
+
+int vboxHostChannelUnregister(const char *pszName)
+{
+ int rc = VINF_SUCCESS;
+
+ VBOXHOSTCHCTX *pCtx = &g_ctx;
+
+ VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pCtx, pszName);
+
+ if (pProvider)
+ {
+ rc = vhcProviderUnregister(pProvider);
+ vhcProviderRelease(pProvider);
+ }
+
+ return rc;
+}
diff --git a/src/VBox/HostServices/HostChannel/HostChannel.h b/src/VBox/HostServices/HostChannel/HostChannel.h
new file mode 100644
index 00000000..cb59c391
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/HostChannel.h
@@ -0,0 +1,137 @@
+/* @file
+ *
+ * Host Channel
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#ifndef VBOX_INCLUDED_SRC_HostChannel_HostChannel_h
+#define VBOX_INCLUDED_SRC_HostChannel_HostChannel_h
+#ifndef RT_WITHOUT_PRAGMA_ONCE
+# pragma once
+#endif
+
+#include <iprt/list.h>
+
+#define LOG_GROUP LOG_GROUP_HGCM
+#include <VBox/log.h>
+#include <VBox/HostServices/VBoxHostChannel.h>
+
+#define HOSTCHLOG Log
+
+#ifdef DEBUG_sunlover
+# undef HOSTCHLOG
+# define HOSTCHLOG LogRel
+#endif /* DEBUG_sunlover */
+
+struct VBOXHOSTCHCTX;
+typedef struct VBOXHOSTCHCTX VBOXHOSTCHCTX;
+
+typedef struct VBOXHOSTCHCLIENT
+{
+ RTLISTNODE nodeClient;
+
+ VBOXHOSTCHCTX *pCtx;
+
+ uint32_t u32ClientID;
+
+ RTLISTANCHOR listChannels;
+ uint32_t volatile u32HandleSrc;
+
+ RTLISTANCHOR listContexts; /* Callback contexts. */
+
+ RTLISTANCHOR listEvents;
+
+ bool fAsync; /* Guest is waiting for a message. */
+
+ struct {
+ VBOXHGCMCALLHANDLE callHandle;
+ VBOXHGCMSVCPARM *paParms;
+ } async;
+
+} VBOXHOSTCHCLIENT;
+
+
+/*
+ * The service functions. Locking is between the service thread and the host channel provider thread.
+ */
+int vboxHostChannelLock(void);
+void vboxHostChannelUnlock(void);
+
+int vboxHostChannelInit(void);
+void vboxHostChannelDestroy(void);
+
+int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient);
+void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient);
+
+int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient,
+ uint32_t *pu32Handle,
+ const char *pszName,
+ uint32_t u32Flags);
+int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle);
+
+int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ const void *pvData,
+ uint32_t cbData);
+int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32DataReceived,
+ uint32_t *pu32DataRemaining);
+int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32Handle,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned);
+
+int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient,
+ bool *pfEvent,
+ VBOXHGCMCALLHANDLE callHandle,
+ VBOXHGCMSVCPARM *paParms);
+
+int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient);
+
+int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient,
+ const char *pszName,
+ uint32_t u32Code,
+ void *pvParm,
+ uint32_t cbParm,
+ void *pvData,
+ uint32_t cbData,
+ uint32_t *pu32SizeDataReturned);
+
+int vboxHostChannelRegister(const char *pszName,
+ const VBOXHOSTCHANNELINTERFACE *pInterface,
+ uint32_t cbInterface);
+int vboxHostChannelUnregister(const char *pszName);
+
+
+void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms,
+ uint32_t u32ChannelHandle,
+ uint32_t u32Id,
+ const void *pvEvent,
+ uint32_t cbEvent);
+
+void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32ChannelHandle,
+ uint32_t u32Id,
+ const void *pvEvent,
+ uint32_t cbEvent);
+
+#endif /* !VBOX_INCLUDED_SRC_HostChannel_HostChannel_h */
diff --git a/src/VBox/HostServices/HostChannel/Makefile.kmk b/src/VBox/HostServices/HostChannel/Makefile.kmk
new file mode 100644
index 00000000..80b22ad1
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/Makefile.kmk
@@ -0,0 +1,41 @@
+# $Id: Makefile.kmk $
+## @file
+# Sub-Makefile for the Host Channel Service.
+#
+
+#
+# Copyright (C) 2012-2019 Oracle Corporation
+#
+# This file is part of VirtualBox Open Source Edition (OSE), as
+# available from http://www.virtualbox.org. This file is free software;
+# you can redistribute it and/or modify it under the terms of the GNU
+# General Public License (GPL) as published by the Free Software
+# Foundation, in version 2 as it comes in the "COPYING" file of the
+# VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+# hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+#
+
+SUB_DEPTH = ../../../..
+include $(KBUILD_PATH)/subheader.kmk
+
+#
+# The service DLL.
+#
+DLLS += VBoxHostChannel
+VBoxHostChannel_TEMPLATE = VBOXR3
+VBoxHostChannel_DEFS = VBOX_WITH_HGCM
+VBoxHostChannel_INCS.win = \
+ $(VBOX_PATH_SDK)
+
+VBoxHostChannel_SOURCES = \
+ VBoxHostChannelSvc.cpp \
+ HostChannel.cpp
+
+VBoxHostChannel_SOURCES.win = \
+ VBoxHostChannelSvc.rc
+
+VBoxHostChannel_LIBS = \
+ $(LIB_VMM) \
+ $(LIB_RUNTIME)
+
+include $(FILE_KBUILD_SUB_FOOTER)
diff --git a/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp
new file mode 100644
index 00000000..bb39ef79
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp
@@ -0,0 +1,901 @@
+/* $Id: VBoxHostChannelSvc.cpp $ */
+/* @file
+ * Host Channel: Host service entry points.
+ */
+
+/*
+ * Copyright (C) 2012-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+
+/*
+ * The HostChannel host service provides a generic proxy between a host's
+ * channel provider and a client running in the guest.
+ *
+ * Host providers must register via a HostCall.
+ *
+ * A guest client can connect to a host provider and send/receive data.
+ *
+ * GuestCalls:
+ * * Attach - attach to a host channel
+ * * Detach - completely detach from a channel
+ * * Send - send data from the guest to the channel
+ * * Recv - non blocking read of available data from the channel
+ * * Control - generic channel specific command exchange
+ * * EventWait - wait for a host event
+ * * EventCancel - make the blocking EventWait call to return
+ * HostCalls:
+ * * Register - register a host channel
+ * * Unregister - unregister it
+ *
+ * The guest HGCM client connects to the service. The client can attach multiple channels.
+ *
+ */
+
+#include <iprt/alloc.h>
+#include <iprt/string.h>
+#include <iprt/assert.h>
+#include <iprt/critsect.h>
+#include <VBox/vmm/ssm.h>
+
+#include "HostChannel.h"
+
+
+static void VBoxHGCMParmUInt32Set(VBOXHGCMSVCPARM *pParm, uint32_t u32)
+{
+ pParm->type = VBOX_HGCM_SVC_PARM_32BIT;
+ pParm->u.uint32 = u32;
+}
+
+static int VBoxHGCMParmUInt32Get(VBOXHGCMSVCPARM *pParm, uint32_t *pu32)
+{
+ if (pParm->type == VBOX_HGCM_SVC_PARM_32BIT)
+ {
+ *pu32 = pParm->u.uint32;
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+#if 0 /* unused */
+static void VBoxHGCMParmPtrSet(VBOXHGCMSVCPARM *pParm, void *pv, uint32_t cb)
+{
+ pParm->type = VBOX_HGCM_SVC_PARM_PTR;
+ pParm->u.pointer.size = cb;
+ pParm->u.pointer.addr = pv;
+}
+#endif
+
+static int VBoxHGCMParmPtrGet(VBOXHGCMSVCPARM *pParm, void **ppv, uint32_t *pcb)
+{
+ if (pParm->type == VBOX_HGCM_SVC_PARM_PTR)
+ {
+ *ppv = pParm->u.pointer.addr;
+ *pcb = pParm->u.pointer.size;
+ return VINF_SUCCESS;
+ }
+
+ AssertFailed();
+ return VERR_INVALID_PARAMETER;
+}
+
+
+static PVBOXHGCMSVCHELPERS g_pHelpers = NULL;
+
+static RTCRITSECT g_critsect;
+
+/*
+ * Helpers.
+ */
+
+int vboxHostChannelLock(void)
+{
+ return RTCritSectEnter(&g_critsect);
+}
+
+void vboxHostChannelUnlock(void)
+{
+ RTCritSectLeave(&g_critsect);
+}
+
+void vboxHostChannelEventParmsSet(VBOXHGCMSVCPARM *paParms,
+ uint32_t u32ChannelHandle,
+ uint32_t u32Id,
+ const void *pvEvent,
+ uint32_t cbEvent)
+{
+ if (cbEvent > 0)
+ {
+ void *pvParm = NULL;
+ uint32_t cbParm = 0;
+
+ VBoxHGCMParmPtrGet(&paParms[2], &pvParm, &cbParm);
+
+ uint32_t cbToCopy = RT_MIN(cbParm, cbEvent);
+ if (cbToCopy > 0)
+ {
+ Assert(pvParm);
+ memcpy(pvParm, pvEvent, cbToCopy);
+ }
+ }
+
+ VBoxHGCMParmUInt32Set(&paParms[0], u32ChannelHandle);
+ VBoxHGCMParmUInt32Set(&paParms[1], u32Id);
+ VBoxHGCMParmUInt32Set(&paParms[3], cbEvent);
+}
+
+/* This is called under the lock. */
+void vboxHostChannelReportAsync(VBOXHOSTCHCLIENT *pClient,
+ uint32_t u32ChannelHandle,
+ uint32_t u32Id,
+ const void *pvEvent,
+ uint32_t cbEvent)
+{
+ Assert(RTCritSectIsOwner(&g_critsect));
+
+ vboxHostChannelEventParmsSet(pClient->async.paParms,
+ u32ChannelHandle,
+ u32Id,
+ pvEvent,
+ cbEvent);
+
+ LogRelFlow(("svcCall: CallComplete for pending\n"));
+
+ g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS);
+}
+
+
+/*
+ * Service entry points.
+ */
+
+static DECLCALLBACK(int) svcUnload(void *pvService)
+{
+ NOREF(pvService);
+ vboxHostChannelDestroy();
+ RTCritSectDelete(&g_critsect);
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcDisconnect(void *pvService, uint32_t u32ClientID, void *pvClient)
+{
+ RT_NOREF2(pvService, u32ClientID);
+
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ vboxHostChannelClientDisconnect(pClient);
+
+ memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT));
+
+ return VINF_SUCCESS;
+}
+
+static DECLCALLBACK(int) svcConnect(void *pvService, uint32_t u32ClientID, void *pvClient, uint32_t fRequestor, bool fRestoring)
+{
+ RT_NOREF(pvService, fRequestor, fRestoring);
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ /* Register the client. */
+ memset(pClient, 0, sizeof(VBOXHOSTCHCLIENT));
+
+ pClient->u32ClientID = u32ClientID;
+
+ int rc = vboxHostChannelClientConnect(pClient);
+
+ LogRel2(("svcConnect: rc = %Rrc\n", rc));
+
+ return rc;
+}
+
+static DECLCALLBACK(void) svcCall(void *pvService,
+ VBOXHGCMCALLHANDLE callHandle,
+ uint32_t u32ClientID,
+ void *pvClient,
+ uint32_t u32Function,
+ uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[],
+ uint64_t tsArrival)
+{
+ RT_NOREF(pvService, tsArrival);
+
+ int rc = VINF_SUCCESS;
+
+ LogRel2(("svcCall: u32ClientID = %d, fn = %d, cParms = %d, pparms = %d\n",
+ u32ClientID, u32Function, cParms, paParms));
+
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ bool fAsynchronousProcessing = false;
+
+#ifdef DEBUG
+ uint32_t i;
+
+ for (i = 0; i < cParms; i++)
+ {
+ /** @todo parameters other than 32 bit */
+ LogRel2((" pparms[%d]: type %d value %d\n", i, paParms[i].type, paParms[i].u.uint32));
+ }
+#endif
+
+ switch (u32Function)
+ {
+ case VBOX_HOST_CHANNEL_FN_ATTACH:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_ATTACH\n"));
+
+ if (cParms != 3)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* flags */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Flags;
+ void *pvName;
+ uint32_t cbName;
+
+ rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = VBoxHGCMParmUInt32Get(&paParms[1], &u32Flags);
+
+ if (RT_SUCCESS(rc))
+ {
+ uint32_t u32Handle = 0;
+
+ /** @todo make sure that pvName is a nul terminated */
+ rc = vboxHostChannelAttach(pClient, &u32Handle, (const char *)pvName, u32Flags);
+
+ if (RT_SUCCESS(rc))
+ {
+ VBoxHGCMParmUInt32Set(&paParms[2], u32Handle);
+ }
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_DETACH:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_DETACH\n"));
+
+ if (cParms != 1)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Handle;
+
+ rc = VBoxHGCMParmUInt32Get(&paParms[0], &u32Handle);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxHostChannelDetach(pClient, u32Handle);
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_SEND:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_SEND\n"));
+
+ if (cParms != 2)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* data */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Handle;
+ void *pvData;
+ uint32_t cbData;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = vboxHostChannelSend(pClient, u32Handle, pvData, cbData);
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_RECV:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_RECV\n"));
+
+ if (cParms != 4)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* data */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeReceived */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeRemaining */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Handle;
+ void *pvData;
+ uint32_t cbData;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[1], &pvData, &cbData);
+
+ if (RT_SUCCESS (rc))
+ {
+ uint32_t u32SizeReceived = 0;
+ uint32_t u32SizeRemaining = 0;
+
+ rc = vboxHostChannelRecv(pClient, u32Handle,
+ pvData, cbData,
+ &u32SizeReceived, &u32SizeRemaining);
+
+ if (RT_SUCCESS(rc))
+ {
+ VBoxHGCMParmUInt32Set(&paParms[2], u32SizeReceived);
+ VBoxHGCMParmUInt32Set(&paParms[3], u32SizeRemaining);
+ }
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_CONTROL:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_CONTROL\n"));
+
+ if (cParms != 5)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* code */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* data */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeDataReturned */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ uint32_t u32Handle;
+ uint32_t u32Code;
+ void *pvParm;
+ uint32_t cbParm;
+ void *pvData;
+ uint32_t cbData;
+
+ rc = VBoxHGCMParmUInt32Get (&paParms[0], &u32Handle);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData);
+
+ if (RT_SUCCESS (rc))
+ {
+ uint32_t u32SizeDataReturned = 0;
+
+ rc = vboxHostChannelControl(pClient, u32Handle, u32Code,
+ pvParm, cbParm,
+ pvData, cbData, &u32SizeDataReturned);
+ if (RT_SUCCESS(rc))
+ {
+ VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned);
+ }
+ }
+ }
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_EVENT_WAIT:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_WAIT\n"));
+
+ if (cParms != 4)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_32BIT /* handle */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* id */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeReturned */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ bool fEvent = false;
+
+ rc = vboxHostChannelEventWait(pClient, &fEvent, callHandle, paParms);
+
+ if (RT_SUCCESS(rc))
+ {
+ if (!fEvent)
+ {
+ /* No event available at the time. Process asynchronously. */
+ fAsynchronousProcessing = true;
+
+ LogRel2(("svcCall: async.\n"));
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_EVENT_CANCEL:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_EVENT_CANCEL\n"));
+
+ if (cParms != 0)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ rc = vboxHostChannelEventCancel(pClient);
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_FN_QUERY:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_FN_QUERY\n"));
+
+ if (cParms != 5)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* channel name */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_32BIT /* code */
+ || paParms[2].type != VBOX_HGCM_SVC_PARM_PTR /* parm */
+ || paParms[3].type != VBOX_HGCM_SVC_PARM_PTR /* data */
+ || paParms[4].type != VBOX_HGCM_SVC_PARM_32BIT /* sizeDataReturned */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ void *pvName;
+ uint32_t cbName;
+ uint32_t u32Code;
+ void *pvParm;
+ uint32_t cbParm;
+ void *pvData;
+ uint32_t cbData;
+
+ rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmUInt32Get (&paParms[1], &u32Code);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[2], &pvParm, &cbParm);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = VBoxHGCMParmPtrGet (&paParms[3], &pvData, &cbData);
+
+ if (RT_SUCCESS (rc))
+ {
+ uint32_t u32SizeDataReturned = 0;
+
+ /** @todo make sure that pvName is a nul terminated */
+ rc = vboxHostChannelQuery(pClient, (const char *)pvName, u32Code,
+ pvParm, cbParm,
+ pvData, cbData, &u32SizeDataReturned);
+ if (RT_SUCCESS(rc))
+ {
+ VBoxHGCMParmUInt32Set(&paParms[4], u32SizeDataReturned);
+ }
+ }
+ }
+ }
+ }
+ }
+ } break;
+
+ default:
+ {
+ rc = VERR_NOT_IMPLEMENTED;
+ }
+ }
+
+ LogRelFlow(("svcCall: rc = %Rrc, async %d\n", rc, fAsynchronousProcessing));
+
+ if (!fAsynchronousProcessing)
+ {
+ g_pHelpers->pfnCallComplete(callHandle, rc);
+ }
+}
+
+static DECLCALLBACK(int) svcHostCall(void *pvService,
+ uint32_t u32Function,
+ uint32_t cParms,
+ VBOXHGCMSVCPARM paParms[])
+{
+ NOREF(pvService);
+
+ int rc = VINF_SUCCESS;
+
+ LogRel2(("svcHostCall: fn = %d, cParms = %d, pparms = %d\n",
+ u32Function, cParms, paParms));
+
+ switch (u32Function)
+ {
+ case VBOX_HOST_CHANNEL_HOST_FN_REGISTER:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_REGISTER\n"));
+
+ if (cParms != 2)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
+ || paParms[1].type != VBOX_HGCM_SVC_PARM_PTR /* iface */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ void *pvName;
+ uint32_t cbName;
+ void *pvInterface;
+ uint32_t cbInterface;
+
+ rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = VBoxHGCMParmPtrGet(&paParms[1], &pvInterface, &cbInterface);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxHostChannelRegister((const char *)pvName,
+ (VBOXHOSTCHANNELINTERFACE *)pvInterface, cbInterface);
+ }
+ }
+ }
+ } break;
+
+ case VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER:
+ {
+ LogRel2(("svcCall: VBOX_HOST_CHANNEL_HOST_FN_UNREGISTER\n"));
+
+ if (cParms != 1)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else if ( paParms[0].type != VBOX_HGCM_SVC_PARM_PTR /* name */
+ )
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ void *pvName;
+ uint32_t cbName;
+
+ rc = VBoxHGCMParmPtrGet(&paParms[0], &pvName, &cbName);
+
+ if (RT_SUCCESS(rc))
+ {
+ rc = vboxHostChannelUnregister((const char *)pvName);
+ }
+ }
+ } break;
+
+ default:
+ break;
+ }
+
+ LogRelFlow(("svcHostCall: rc = %Rrc\n", rc));
+ return rc;
+}
+
+#if 0
+/** If the client in the guest is waiting for a read operation to complete
+ * then complete it, otherwise return. See the protocol description in the
+ * shared clipboard module description. */
+void vboxSvcClipboardCompleteReadData(VBOXHOSTCHCLIENT *pClient, int rc, uint32_t cbActual)
+{
+ VBOXHGCMCALLHANDLE callHandle = NULL;
+ VBOXHGCMSVCPARM *paParms = NULL;
+ bool fReadPending = false;
+ if (vboxSvcClipboardLock()) /* if not can we do anything useful? */
+ {
+ callHandle = pClient->asyncRead.callHandle;
+ paParms = pClient->asyncRead.paParms;
+ fReadPending = pClient->fReadPending;
+ pClient->fReadPending = false;
+ vboxSvcClipboardUnlock();
+ }
+ if (fReadPending)
+ {
+ VBoxHGCMParmUInt32Set (&paParms[2], cbActual);
+ g_pHelpers->pfnCallComplete (callHandle, rc);
+ }
+}
+
+/**
+ * SSM descriptor table for the VBOXHOSTCHCLIENT structure.
+ */
+static SSMFIELD const g_aClipboardClientDataFields[] =
+{
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32ClientID), /* for validation purposes */
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgQuit),
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgReadData),
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, fMsgFormats),
+ SSMFIELD_ENTRY(VBOXHOSTCHCLIENT, u32RequestedFormat),
+ SSMFIELD_ENTRY_TERM()
+};
+
+static DECLCALLBACK(int) svcSaveState(void *pvService, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+ NOREF(pvService);
+
+ /* If there are any pending requests, they must be completed here. Since
+ * the service is single threaded, there could be only requests
+ * which the service itself has postponed.
+ *
+ * HGCM knows that the state is being saved and that the pfnComplete
+ * calls are just clean ups. These requests are saved by the VMMDev.
+ *
+ * When the state will be restored, these requests will be reissued
+ * by VMMDev. The service therefore must save state as if there were no
+ * pending request.
+ */
+ LogRel2 (("svcSaveState: u32ClientID = %d\n", u32ClientID));
+
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ /* This field used to be the length. We're using it as a version field
+ with the high bit set. */
+ SSMR3PutU32 (pSSM, UINT32_C (0x80000002));
+ int rc = SSMR3PutStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
+ AssertRCReturn (rc, rc);
+
+ if (pClient->fAsync)
+ {
+ g_pHelpers->pfnCallComplete (pClient->async.callHandle, VINF_SUCCESS /* error code is not important here. */);
+ pClient->fAsync = false;
+ }
+
+ vboxSvcClipboardCompleteReadData (pClient, VINF_SUCCESS, 0);
+
+ return VINF_SUCCESS;
+}
+
+/**
+ * This structure corresponds to the original layout of the
+ * VBOXHOSTCHCLIENT structure. As the structure was saved as a whole
+ * when saving state, we need to remember it forever in order to preserve
+ * compatibility.
+ *
+ * (Starting with 3.1 this is no longer used.)
+ *
+ * @remarks Putting this outside svcLoadState to avoid visibility warning caused
+ * by -Wattributes.
+ */
+typedef struct CLIPSAVEDSTATEDATA
+{
+ struct CLIPSAVEDSTATEDATA *pNext;
+ struct CLIPSAVEDSTATEDATA *pPrev;
+
+ VBOXCLIPBOARDCONTEXT *pCtx;
+
+ uint32_t u32ClientID;
+
+ bool fAsync: 1; /* Guest is waiting for a message. */
+
+ bool fMsgQuit: 1;
+ bool fMsgReadData: 1;
+ bool fMsgFormats: 1;
+
+ struct {
+ VBOXHGCMCALLHANDLE callHandle;
+ VBOXHGCMSVCPARM *paParms;
+ } async;
+
+ struct {
+ void *pv;
+ uint32_t cb;
+ uint32_t u32Format;
+ } data;
+
+ uint32_t u32AvailableFormats;
+ uint32_t u32RequestedFormat;
+
+} CLIPSAVEDSTATEDATA;
+
+static DECLCALLBACK(int) svcLoadState(void *, uint32_t u32ClientID, void *pvClient, PSSMHANDLE pSSM)
+{
+ LogRel2 (("svcLoadState: u32ClientID = %d\n", u32ClientID));
+
+ VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvClient;
+
+ /* Existing client can not be in async state yet. */
+ Assert (!pClient->fAsync);
+
+ /* Save the client ID for data validation. */
+ /** @todo isn't this the same as u32ClientID? Playing safe for now... */
+ uint32_t const u32ClientIDOld = pClient->u32ClientID;
+
+ /* Restore the client data. */
+ uint32_t lenOrVer;
+ int rc = SSMR3GetU32 (pSSM, &lenOrVer);
+ AssertRCReturn (rc, rc);
+ if (lenOrVer == UINT32_C (0x80000002))
+ {
+ rc = SSMR3GetStructEx (pSSM, pClient, sizeof(*pClient), 0 /*fFlags*/, &g_aClipboardClientDataFields[0], NULL);
+ AssertRCReturn (rc, rc);
+ }
+ else if (lenOrVer == (SSMR3HandleHostBits (pSSM) == 64 ? 72 : 48))
+ {
+ /**
+ * SSM descriptor table for the CLIPSAVEDSTATEDATA structure.
+ */
+ static SSMFIELD const s_aClipSavedStateDataFields30[] =
+ {
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pNext),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pPrev),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, pCtx),
+ SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32ClientID),
+ SSMFIELD_ENTRY_CUSTOM(fMsgQuit+fMsgReadData+fMsgFormats, RT_OFFSETOF(CLIPSAVEDSTATEDATA, u32ClientID) + 4, 4),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.callHandle),
+ SSMFIELD_ENTRY_IGN_HCPTR( CLIPSAVEDSTATEDATA, async.paParms),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.pv),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.cb),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, data.u32Format),
+ SSMFIELD_ENTRY_IGNORE( CLIPSAVEDSTATEDATA, u32AvailableFormats),
+ SSMFIELD_ENTRY( CLIPSAVEDSTATEDATA, u32RequestedFormat),
+ SSMFIELD_ENTRY_TERM()
+ };
+
+ CLIPSAVEDSTATEDATA savedState;
+ RT_ZERO (savedState);
+ rc = SSMR3GetStructEx (pSSM, &savedState, sizeof(savedState), SSMSTRUCT_FLAGS_MEM_BAND_AID,
+ &s_aClipSavedStateDataFields30[0], NULL);
+ AssertRCReturn (rc, rc);
+
+ pClient->fMsgQuit = savedState.fMsgQuit;
+ pClient->fMsgReadData = savedState.fMsgReadData;
+ pClient->fMsgFormats = savedState.fMsgFormats;
+ pClient->u32RequestedFormat = savedState.u32RequestedFormat;
+ }
+ else
+ {
+ LogRel (("Client data size mismatch: got %#x\n", lenOrVer));
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ }
+
+ /* Verify the client ID. */
+ if (pClient->u32ClientID != u32ClientIDOld)
+ {
+ LogRel (("Client ID mismatch: expected %d, got %d\n", u32ClientIDOld, pClient->u32ClientID));
+ pClient->u32ClientID = u32ClientIDOld;
+ return VERR_SSM_DATA_UNIT_FORMAT_CHANGED;
+ }
+
+ /* Actual host data are to be reported to guest (SYNC). */
+ vboxClipboardSync (pClient);
+
+ return VINF_SUCCESS;
+}
+#endif
+
+static int svcInit(void)
+{
+ int rc = RTCritSectInit(&g_critsect);
+
+ if (RT_SUCCESS (rc))
+ {
+ rc = vboxHostChannelInit();
+
+ /* Clean up on failure, because 'svnUnload' will not be called
+ * if the 'svcInit' returns an error.
+ */
+ if (RT_FAILURE(rc))
+ {
+ RTCritSectDelete(&g_critsect);
+ }
+ }
+
+ return rc;
+}
+
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
+{
+ int rc = VINF_SUCCESS;
+
+ LogRelFlowFunc(("pTable = %p\n", pTable));
+
+ if (!pTable)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ LogRel2(("VBoxHGCMSvcLoad: pTable->cbSize = %d, pTable->u32Version = 0x%08X\n",
+ pTable->cbSize, pTable->u32Version));
+
+ if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+ || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
+ {
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ g_pHelpers = pTable->pHelpers;
+
+ pTable->cbClient = sizeof(VBOXHOSTCHCLIENT);
+
+ pTable->pfnUnload = svcUnload;
+ pTable->pfnConnect = svcConnect;
+ pTable->pfnDisconnect = svcDisconnect;
+ pTable->pfnCall = svcCall;
+ pTable->pfnHostCall = svcHostCall;
+ pTable->pfnSaveState = NULL; // svcSaveState;
+ pTable->pfnLoadState = NULL; // svcLoadState;
+ pTable->pfnRegisterExtension = NULL;
+ pTable->pvService = NULL;
+
+ /* Service specific initialization. */
+ rc = svcInit();
+ }
+ }
+
+ return rc;
+}
diff --git a/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc
new file mode 100644
index 00000000..a6716c12
--- /dev/null
+++ b/src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc
@@ -0,0 +1,51 @@
+/* $Id: VBoxHostChannelSvc.rc $ */
+/** @file
+ * VBoxHostChannel - Resource file containing version info and icon.
+ */
+
+/*
+ * Copyright (C) 2015-2019 Oracle Corporation
+ *
+ * This file is part of VirtualBox Open Source Edition (OSE), as
+ * available from http://www.virtualbox.org. This file is free software;
+ * you can redistribute it and/or modify it under the terms of the GNU
+ * General Public License (GPL) as published by the Free Software
+ * Foundation, in version 2 as it comes in the "COPYING" file of the
+ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
+ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
+ */
+
+#include <windows.h>
+#include <VBox/version.h>
+
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION VBOX_RC_FILE_VERSION
+ PRODUCTVERSION VBOX_RC_FILE_VERSION
+ FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
+ FILEFLAGS VBOX_RC_FILE_FLAGS
+ FILEOS VBOX_RC_FILE_OS
+ FILETYPE VBOX_RC_TYPE_DLL
+ FILESUBTYPE VFT2_UNKNOWN
+BEGIN
+ BLOCK "StringFileInfo"
+ BEGIN
+ BLOCK "040904b0" // Lang=US English, CharSet=Unicode
+ BEGIN
+ VALUE "FileDescription", "VirtualBox Host Channel Service\0"
+ VALUE "InternalName", "VBoxHostChannel\0"
+ VALUE "OriginalFilename", "VBoxHostChannel.dll\0"
+ VALUE "CompanyName", VBOX_RC_COMPANY_NAME
+ VALUE "FileVersion", VBOX_RC_FILE_VERSION_STR
+ VALUE "LegalCopyright", VBOX_RC_LEGAL_COPYRIGHT
+ VALUE "ProductName", VBOX_RC_PRODUCT_NAME_STR
+ VALUE "ProductVersion", VBOX_RC_PRODUCT_VERSION_STR
+ VBOX_RC_MORE_STRINGS
+ END
+ END
+ BLOCK "VarFileInfo"
+ BEGIN
+ VALUE "Translation", 0x409, 1200
+ END
+END