6
* Copyright (C) 2012 Oracle Corporation
8
* This file is part of VirtualBox Open Source Edition (OSE), as
9
* available from http://www.virtualbox.org. This file is free software;
10
* you can redistribute it and/or modify it under the terms of the GNU
11
* General Public License (GPL) as published by the Free Software
12
* Foundation, in version 2 as it comes in the "COPYING" file of the
13
* VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14
* hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
17
#include <iprt/alloc.h>
18
#include <iprt/string.h>
20
#include <iprt/assert.h>
22
#include "HostChannel.h"
25
static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvInstance,
26
uint32_t u32Id, const void *pvEvent, uint32_t cbEvent);
27
static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel);
30
/* A registered provider of channels. */
31
typedef struct VBOXHOSTCHPROVIDER
33
int32_t volatile cRefs;
35
RTLISTNODE nodeContext; /* Member of the list of providers in the service context. */
39
VBOXHOSTCHANNELINTERFACE iface;
43
RTLISTANCHOR listChannels;
46
/* An established channel. */
47
typedef struct VBOXHOSTCHINSTANCE
49
int32_t volatile cRefs;
51
RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
52
RTLISTNODE nodeProvider; /* In the provider, needed for cleanup when the provider is unregistered. */
54
VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel. */
55
VBOXHOSTCHPROVIDER *pProvider; /* NULL if the provider was unregistered. */
56
void *pvChannel; /* Provider's context of the channel. */
57
uint32_t u32Handle; /* handle assigned to the channel by the service. */
64
RTLISTANCHOR listProviders;
67
/* The channel callbacks context. The provider passes the pointer as a callback parameter.
68
* Created for the provider and deleted when the provider says so.
70
typedef struct VBOXHOSTCHCALLBACKCTX
72
RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
74
VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel, NULL when the client does not exist. */
75
} VBOXHOSTCHCALLBACKCTX;
77
/* Only one service instance is supported. */
78
static VBOXHOSTCHCTX g_ctx = { false };
80
static VBOXHOSTCHANNELCALLBACKS g_callbacks =
82
HostChannelCallbackEvent,
83
HostChannelCallbackDeleted
88
* Provider management.
91
static void vhcProviderDestroy(VBOXHOSTCHPROVIDER *pProvider)
93
RTStrFree(pProvider->pszName);
96
static int32_t vhcProviderAddRef(VBOXHOSTCHPROVIDER *pProvider)
98
return ASMAtomicIncS32(&pProvider->cRefs);
101
static void vhcProviderRelease(VBOXHOSTCHPROVIDER *pProvider)
103
int32_t c = ASMAtomicDecS32(&pProvider->cRefs);
107
vhcProviderDestroy(pProvider);
108
RTMemFree(pProvider);
112
static VBOXHOSTCHPROVIDER *vhcProviderFind(VBOXHOSTCHCTX *pCtx, const char *pszName)
114
VBOXHOSTCHPROVIDER *pProvider = NULL;
116
int rc = vboxHostChannelLock();
120
VBOXHOSTCHPROVIDER *pIter;
121
RTListForEach(&pCtx->listProviders, pIter, VBOXHOSTCHPROVIDER, nodeContext)
123
if (RTStrCmp(pIter->pszName, pszName) == 0)
127
vhcProviderAddRef(pProvider);
133
vboxHostChannelUnlock();
139
static int vhcProviderRegister(VBOXHOSTCHCTX *pCtx, VBOXHOSTCHPROVIDER *pProvider)
141
int rc = vboxHostChannelLock();
145
/* @todo check a duplicate. */
147
RTListAppend(&pCtx->listProviders, &pProvider->nodeContext);
149
vboxHostChannelUnlock();
154
vhcProviderRelease(pProvider);
160
static int vhcProviderUnregister(VBOXHOSTCHPROVIDER *pProvider)
162
int rc = vboxHostChannelLock();
166
/* @todo check that the provider is in the list. */
167
/* @todo mark the provider as invalid in each instance. also detach channels? */
169
RTListNodeRemove(&pProvider->nodeContext);
171
vboxHostChannelUnlock();
173
vhcProviderRelease(pProvider);
181
* Select an unique handle for the new channel.
182
* Works under the lock.
184
static int vhcHandleCreate(VBOXHOSTCHCLIENT *pClient, uint32_t *pu32Handle)
190
uint32_t u32Handle = ASMAtomicIncU32(&pClient->u32HandleSrc);
196
return VERR_NOT_SUPPORTED;
203
VBOXHOSTCHINSTANCE *pDuplicate = NULL;
204
VBOXHOSTCHINSTANCE *pIter;
205
RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
207
if (pIter->u32Handle == u32Handle)
214
if (pDuplicate == NULL)
216
*pu32Handle = u32Handle;
226
* Channel instance management.
229
static void vhcInstanceDestroy(VBOXHOSTCHINSTANCE *pInstance)
231
HOSTCHLOG(("HostChannel: destroy %p\n", pInstance));
234
static int32_t vhcInstanceAddRef(VBOXHOSTCHINSTANCE *pInstance)
236
HOSTCHLOG(("INST: %p %d addref\n", pInstance, pInstance->cRefs));
237
return ASMAtomicIncS32(&pInstance->cRefs);
240
static void vhcInstanceRelease(VBOXHOSTCHINSTANCE *pInstance)
242
int32_t c = ASMAtomicDecS32(&pInstance->cRefs);
243
HOSTCHLOG(("INST: %p %d release\n", pInstance, pInstance->cRefs));
247
vhcInstanceDestroy(pInstance);
248
RTMemFree(pInstance);
252
static int vhcInstanceCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE **ppInstance)
254
int rc = VINF_SUCCESS;
256
VBOXHOSTCHINSTANCE *pInstance = (VBOXHOSTCHINSTANCE *)RTMemAllocZ(sizeof(VBOXHOSTCHINSTANCE));
260
rc = vboxHostChannelLock();
264
rc = vhcHandleCreate(pClient, &pInstance->u32Handle);
268
/* Used by the client, that is in the list of channels. */
269
vhcInstanceAddRef(pInstance);
270
/* Add to the list of created channel instances. It is inactive while pClient is 0. */
271
RTListAppend(&pClient->listChannels, &pInstance->nodeClient);
273
/* Return to the caller. */
274
vhcInstanceAddRef(pInstance);
275
*ppInstance = pInstance;
278
vboxHostChannelUnlock();
283
RTMemFree(pInstance);
294
static VBOXHOSTCHINSTANCE *vhcInstanceFind(VBOXHOSTCHCLIENT *pClient, uint32_t u32Handle)
296
VBOXHOSTCHINSTANCE *pInstance = NULL;
298
int rc = vboxHostChannelLock();
302
VBOXHOSTCHINSTANCE *pIter;
303
RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
306
&& pIter->u32Handle == u32Handle)
310
vhcInstanceAddRef(pInstance);
316
vboxHostChannelUnlock();
322
static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient, void *pvChannel)
324
VBOXHOSTCHINSTANCE *pInstance = NULL;
326
if (pvChannel == NULL)
331
int rc = vboxHostChannelLock();
335
VBOXHOSTCHINSTANCE *pIter;
336
RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
339
&& pIter->pvChannel == pvChannel)
343
vhcInstanceAddRef(pInstance);
349
vboxHostChannelUnlock();
355
static void vhcInstanceDetach(VBOXHOSTCHINSTANCE *pInstance)
357
HOSTCHLOG(("HostChannel: detach %p\n", pInstance));
359
if (pInstance->pProvider)
361
pInstance->pProvider->iface.HostChannelDetach(pInstance->pvChannel);
362
RTListNodeRemove(&pInstance->nodeProvider);
363
vhcProviderRelease(pInstance->pProvider);
364
pInstance->pProvider = NULL;
365
vhcInstanceRelease(pInstance); /* Not in the provider's list anymore. */
368
int rc = vboxHostChannelLock();
372
RTListNodeRemove(&pInstance->nodeClient);
374
vboxHostChannelUnlock();
376
vhcInstanceRelease(pInstance); /* Not used by the client anymore. */
381
* Channel callback contexts.
383
static int vhcCallbackCtxCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHCALLBACKCTX **ppCallbackCtx)
385
int rc = VINF_SUCCESS;
387
VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)RTMemAllocZ(sizeof(VBOXHOSTCHCALLBACKCTX));
389
if (pCallbackCtx != NULL)
391
/* The callback context is accessed by the providers threads. */
392
rc = vboxHostChannelLock();
395
RTListAppend(&pClient->listContexts, &pCallbackCtx->nodeClient);
396
pCallbackCtx->pClient = pClient;
398
vboxHostChannelUnlock();
402
RTMemFree(pCallbackCtx);
412
*ppCallbackCtx = pCallbackCtx;
418
static int vhcCallbackCtxDelete(VBOXHOSTCHCALLBACKCTX *pCallbackCtx)
420
int rc = vboxHostChannelLock();
423
VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient;
427
/* The callback is associated with a client.
428
* Check that the callback is in the list and remove it from the list.
432
VBOXHOSTCHCALLBACKCTX *pIter;
433
RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient)
435
if (pIter == pCallbackCtx)
444
RTListNodeRemove(&pCallbackCtx->nodeClient);
449
rc = VERR_INVALID_PARAMETER;
454
/* It is not in the clients anymore. May be the client has been disconnected.
455
* Just free the memory.
459
vboxHostChannelUnlock();
464
RTMemFree(pCallbackCtx);
471
* Host channel service functions.
474
int vboxHostChannelInit(void)
476
VBOXHOSTCHCTX *pCtx = &g_ctx;
478
if (pCtx->fInitialized)
480
return VERR_NOT_SUPPORTED;
483
pCtx->fInitialized = true;
484
RTListInit(&pCtx->listProviders);
489
void vboxHostChannelDestroy(void)
491
VBOXHOSTCHCTX *pCtx = &g_ctx;
493
VBOXHOSTCHPROVIDER *pIter;
494
VBOXHOSTCHPROVIDER *pIterNext;
495
RTListForEachSafe(&pCtx->listProviders, pIter, pIterNext, VBOXHOSTCHPROVIDER, nodeContext)
497
vhcProviderUnregister(pIter);
499
pCtx->fInitialized = false;
502
int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient)
504
/* A guest client is connecting to the service.
505
* Later the client will use Attach calls to connect to channel providers.
506
* pClient is already zeroed.
508
pClient->pCtx = &g_ctx;
510
RTListInit(&pClient->listChannels);
511
RTListInit(&pClient->listEvents);
512
RTListInit(&pClient->listContexts);
517
void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient)
519
/* Clear the list of contexts and prevent acceess to the client. */
520
int rc = vboxHostChannelLock();
523
VBOXHOSTCHCALLBACKCTX *pIter;
524
VBOXHOSTCHCALLBACKCTX *pNext;
525
RTListForEachSafe(&pClient->listContexts, pIter, pNext, VBOXHOSTCHCALLBACKCTX, nodeClient)
527
pIter->pClient = NULL;
528
RTListNodeRemove(&pIter->nodeClient);
531
vboxHostChannelUnlock();
534
/* If there are attached channels, detach them. */
535
VBOXHOSTCHINSTANCE *pIter;
536
VBOXHOSTCHINSTANCE *pIterNext;
537
RTListForEachSafe(&pClient->listChannels, pIter, pIterNext, VBOXHOSTCHINSTANCE, nodeClient)
539
vhcInstanceDetach(pIter);
543
int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient,
544
uint32_t *pu32Handle,
548
int rc = VINF_SUCCESS;
550
HOSTCHLOG(("HostChannel: Attach: (%d) [%s] 0x%08X\n", pClient->u32ClientID, pszName, u32Flags));
552
/* Look if there is a provider. */
553
VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
557
VBOXHOSTCHINSTANCE *pInstance = NULL;
559
rc = vhcInstanceCreate(pClient, &pInstance);
563
VBOXHOSTCHCALLBACKCTX *pCallbackCtx = NULL;
564
rc = vhcCallbackCtxCreate(pClient, &pCallbackCtx);
568
void *pvChannel = NULL;
569
rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider,
572
&g_callbacks, pCallbackCtx);
576
vhcProviderAddRef(pProvider);
577
pInstance->pProvider = pProvider;
579
pInstance->pClient = pClient;
580
pInstance->pvChannel = pvChannel;
582
/* It is already in the channels list of the client. */
584
vhcInstanceAddRef(pInstance); /* Referenced by the list of provider's channels. */
585
RTListAppend(&pProvider->listChannels, &pInstance->nodeProvider);
587
*pu32Handle = pInstance->u32Handle;
589
HOSTCHLOG(("HostChannel: Attach: (%d) handle %d\n", pClient->u32ClientID, pInstance->u32Handle));
594
vhcCallbackCtxDelete(pCallbackCtx);
600
vhcInstanceDetach(pInstance);
603
vhcInstanceRelease(pInstance);
606
vhcProviderRelease(pProvider);
610
rc = VERR_NOT_SUPPORTED;
616
int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient,
619
HOSTCHLOG(("HostChannel: Detach: (%d) handle %d\n", pClient->u32ClientID, u32Handle));
621
int rc = VINF_SUCCESS;
623
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
627
vhcInstanceDetach(pInstance);
629
vhcInstanceRelease(pInstance);
633
rc = VERR_NOT_SUPPORTED;
639
int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient,
644
HOSTCHLOG(("HostChannel: Send: (%d) handle %d, %d bytes\n", pClient->u32ClientID, u32Handle, cbData));
646
int rc = VINF_SUCCESS;
648
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
652
if (pInstance->pProvider)
654
pInstance->pProvider->iface.HostChannelSend(pInstance->pvChannel, pvData, cbData);
657
vhcInstanceRelease(pInstance);
661
rc = VERR_NOT_SUPPORTED;
667
int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient,
671
uint32_t *pu32SizeReceived,
672
uint32_t *pu32SizeRemaining)
674
HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
676
int rc = VINF_SUCCESS;
678
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
682
if (pInstance->pProvider)
684
rc = pInstance->pProvider->iface.HostChannelRecv(pInstance->pvChannel, pvData, cbData,
685
pu32SizeReceived, pu32SizeRemaining);
687
HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, recv %d, rem %d\n",
688
pClient->u32ClientID, u32Handle, rc, cbData, *pu32SizeReceived, *pu32SizeRemaining));
691
vhcInstanceRelease(pInstance);
695
rc = VERR_NOT_SUPPORTED;
701
int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient,
708
uint32_t *pu32SizeDataReturned)
710
HOSTCHLOG(("HostChannel: Control: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
712
int rc = VINF_SUCCESS;
714
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
718
if (pInstance->pProvider)
720
pInstance->pProvider->iface.HostChannelControl(pInstance->pvChannel, u32Code,
722
pvData, cbData, pu32SizeDataReturned);
725
vhcInstanceRelease(pInstance);
729
rc = VERR_NOT_SUPPORTED;
735
typedef struct VBOXHOSTCHANNELEVENT
737
RTLISTNODE NodeEvent;
739
uint32_t u32ChannelHandle;
744
} VBOXHOSTCHANNELEVENT;
746
int vboxHostChannelEventWait(VBOXHOSTCHCLIENT *pClient,
748
VBOXHGCMCALLHANDLE callHandle,
749
VBOXHGCMSVCPARM *paParms)
751
int rc = vboxHostChannelLock();
759
/* If there is a wait request already, cancel it. */
760
vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0);
761
pClient->fAsync = false;
764
/* Check if there is something in the client's event queue. */
765
VBOXHOSTCHANNELEVENT *pEvent = RTListGetFirst(&pClient->listEvents, VBOXHOSTCHANNELEVENT, NodeEvent);
767
HOSTCHLOG(("HostChannel: QueryEvent: (%d), event %p\n", pClient->u32ClientID, pEvent));
771
/* Report the event. */
772
RTListNodeRemove(&pEvent->NodeEvent);
774
HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbEvent %d\n",
775
pClient->u32ClientID, pEvent->cbEvent));
777
vboxHostChannelEventParmsSet(paParms, pEvent->u32ChannelHandle,
778
pEvent->u32Id, pEvent->pvEvent, pEvent->cbEvent);
786
/* No event available at the time. Process asynchronously. */
787
pClient->fAsync = true;
788
pClient->async.callHandle = callHandle;
789
pClient->async.paParms = paParms;
791
/* Tell the caller that there is no event. */
795
vboxHostChannelUnlock();
799
int vboxHostChannelEventCancel(VBOXHOSTCHCLIENT *pClient)
801
int rc = vboxHostChannelLock();
807
/* If there is a wait request alredy, cancel it. */
808
vboxHostChannelReportAsync(pClient, 0, VBOX_HOST_CHANNEL_EVENT_CANCELLED, NULL, 0);
810
pClient->fAsync = false;
813
vboxHostChannelUnlock();
819
/* @thread provider */
820
static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvChannel,
821
uint32_t u32Id, const void *pvEvent, uint32_t cbEvent)
823
VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)pvCallbacks;
825
int rc = vboxHostChannelLock();
831
/* Check that the structure is still associated with a client.
832
* The client can disconnect and will be invalid.
834
VBOXHOSTCHCLIENT *pClient = pCallbackCtx->pClient;
838
vboxHostChannelUnlock();
840
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client gone.\n"));
842
/* The client does not exist anymore, skip the event. */
848
VBOXHOSTCHCALLBACKCTX *pIter;
849
RTListForEach(&pClient->listContexts, pIter, VBOXHOSTCHCALLBACKCTX, nodeClient)
851
if (pIter == pCallbackCtx)
862
vboxHostChannelUnlock();
864
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client does not have the context.\n"));
866
/* The context is not in the list of contexts. Skip the event. */
870
VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel);
872
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: (%d) instance %p\n",
873
pCallbackCtx, pClient->u32ClientID, pInstance));
877
/* Instance was already detached. Skip the event. */
878
vboxHostChannelUnlock();
883
uint32_t u32ChannelHandle = pInstance->u32Handle;
885
HOSTCHLOG(("HostChannel: CallbackEvent: (%d) handle %d, async %d, cbEvent %d\n",
886
pClient->u32ClientID, u32ChannelHandle, pClient->fAsync, cbEvent));
888
/* Check whether the event is waited. */
891
/* Report the event. */
892
vboxHostChannelReportAsync(pClient, u32ChannelHandle, u32Id, pvEvent, cbEvent);
894
pClient->fAsync = false;
898
/* Put it to the queue. */
899
VBOXHOSTCHANNELEVENT *pEvent = (VBOXHOSTCHANNELEVENT *)RTMemAlloc(sizeof(VBOXHOSTCHANNELEVENT) + cbEvent);
903
pEvent->u32ChannelHandle = u32ChannelHandle;
904
pEvent->u32Id = u32Id;
908
pEvent->pvEvent = &pEvent[1];
909
memcpy(pEvent->pvEvent, pvEvent, cbEvent);
913
pEvent->pvEvent = NULL;
916
pEvent->cbEvent = cbEvent;
918
RTListAppend(&pClient->listEvents, &pEvent->NodeEvent);
922
vboxHostChannelUnlock();
924
vhcInstanceRelease(pInstance);
927
/* @thread provider */
928
static DECLCALLBACK(void) HostChannelCallbackDeleted(void *pvCallbacks, void *pvChannel)
930
vhcCallbackCtxDelete((VBOXHOSTCHCALLBACKCTX *)pvCallbacks);
933
int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient,
940
uint32_t *pu32SizeDataReturned)
942
HOSTCHLOG(("HostChannel: Query: (%d) name [%s], cbData %d\n", pClient->u32ClientID, pszName, cbData));
944
int rc = VINF_SUCCESS;
946
/* Look if there is a provider. */
947
VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
951
pProvider->iface.HostChannelControl(NULL, u32Code,
953
pvData, cbData, pu32SizeDataReturned);
955
vhcProviderRelease(pProvider);
959
rc = VERR_NOT_SUPPORTED;
965
int vboxHostChannelRegister(const char *pszName,
966
const VBOXHOSTCHANNELINTERFACE *pInterface,
967
uint32_t cbInterface)
969
int rc = VINF_SUCCESS;
971
VBOXHOSTCHCTX *pCtx = &g_ctx;
973
VBOXHOSTCHPROVIDER *pProvider = (VBOXHOSTCHPROVIDER *)RTMemAllocZ(sizeof(VBOXHOSTCHPROVIDER));
977
pProvider->pCtx = pCtx;
978
pProvider->iface = *pInterface;
980
RTListInit(&pProvider->listChannels);
982
pProvider->pszName = RTStrDup(pszName);
983
if (pProvider->pszName)
985
vhcProviderAddRef(pProvider);
986
rc = vhcProviderRegister(pCtx, pProvider);
990
RTMemFree(pProvider);
1002
int vboxHostChannelUnregister(const char *pszName)
1004
int rc = VINF_SUCCESS;
1006
VBOXHOSTCHCTX *pCtx = &g_ctx;
1008
VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pCtx, pszName);
1012
rc = vhcProviderUnregister(pProvider);
1013
vhcProviderRelease(pProvider);