diff options
Diffstat (limited to 'src/VBox/HostServices/HostChannel')
-rw-r--r-- | src/VBox/HostServices/HostChannel/HostChannel.cpp | 1020 | ||||
-rw-r--r-- | src/VBox/HostServices/HostChannel/HostChannel.h | 137 | ||||
-rw-r--r-- | src/VBox/HostServices/HostChannel/Makefile.kmk | 41 | ||||
-rw-r--r-- | src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.cpp | 901 | ||||
-rw-r--r-- | src/VBox/HostServices/HostChannel/VBoxHostChannelSvc.rc | 51 |
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 |