VirtualBox

source: vbox/trunk/src/VBox/HostServices/HostChannel/HostChannel.cpp@ 43462

Last change on this file since 43462 was 43462, checked in by vboxsync, 12 years ago

HGCM Host Channel service: implemented a generic Query function.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 19.4 KB
Line 
1/** @file
2 * Host channel.
3 */
4
5/*
6 * Copyright (C) 2012 Oracle Corporation
7 *
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.
15 */
16
17#include <iprt/alloc.h>
18#include <iprt/string.h>
19#include <iprt/asm.h>
20#include <iprt/assert.h>
21
22#include "HostChannel.h"
23
24static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvInstance,
25 uint32_t u32Id, const void *pvEvent, uint32_t cbEvent);
26
27
28/* A registered provider of channels. */
29typedef struct VBOXHOSTCHPROVIDER
30{
31 int32_t volatile cRefs;
32
33 RTLISTNODE nodeContext; /* Member of the list of providers in the service context. */
34
35 VBOXHOSTCHCTX *pCtx;
36
37 VBOXHOSTCHANNELINTERFACE iface;
38
39 char *pszName;
40
41 RTLISTANCHOR listChannels;
42} VBOXHOSTCHPROVIDER;
43
44/* An established channel. */
45typedef struct VBOXHOSTCHINSTANCE
46{
47 int32_t volatile cRefs;
48
49 RTLISTNODE nodeClient; /* In the client, for cleanup when a client disconnects. */
50 RTLISTNODE nodeProvider; /* In the provider, needed for cleanup when the provider is unregistered. */
51
52 VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel. */
53 VBOXHOSTCHPROVIDER *pProvider; /* NULL if the provider was unregistered. */
54 void *pvChannel; /* Provider's context of the channel. */
55 uint32_t u32Handle; /* handle assigned to the channel by the service. */
56} VBOXHOSTCHINSTANCE;
57
58struct VBOXHOSTCHCTX
59{
60 bool fInitialized;
61
62 RTLISTANCHOR listProviders;
63};
64
65/* Only one service instance is supported. */
66static VBOXHOSTCHCTX g_ctx = { false };
67
68static VBOXHOSTCHANNELCALLBACKS g_callbacks =
69{
70 HostChannelCallbackEvent
71};
72
73
74/*
75 * Provider management.
76 */
77
78static void vhcProviderDestroy(VBOXHOSTCHPROVIDER *pProvider)
79{
80 RTStrFree(pProvider->pszName);
81}
82
83static int32_t vhcProviderAddRef(VBOXHOSTCHPROVIDER *pProvider)
84{
85 return ASMAtomicIncS32(&pProvider->cRefs);
86}
87
88static void vhcProviderRelease(VBOXHOSTCHPROVIDER *pProvider)
89{
90 int32_t c = ASMAtomicDecS32(&pProvider->cRefs);
91 Assert(c >= 0);
92 if (c == 0)
93 {
94 vhcProviderDestroy(pProvider);
95 RTMemFree(pProvider);
96 }
97}
98
99static VBOXHOSTCHPROVIDER *vhcProviderFind(VBOXHOSTCHCTX *pCtx, const char *pszName)
100{
101 VBOXHOSTCHPROVIDER *pProvider = NULL;
102
103 int rc = vboxHostChannelLock();
104
105 if (RT_SUCCESS(rc))
106 {
107 VBOXHOSTCHPROVIDER *pIter;
108 RTListForEach(&pCtx->listProviders, pIter, VBOXHOSTCHPROVIDER, nodeContext)
109 {
110 if (RTStrCmp(pIter->pszName, pszName) == 0)
111 {
112 pProvider = pIter;
113
114 vhcProviderAddRef(pProvider);
115
116 break;
117 }
118 }
119
120 vboxHostChannelUnlock();
121 }
122
123 return pProvider;
124}
125
126static int vhcProviderRegister(VBOXHOSTCHCTX *pCtx, VBOXHOSTCHPROVIDER *pProvider)
127{
128 int rc = vboxHostChannelLock();
129
130 if (RT_SUCCESS(rc))
131 {
132 /* @todo check a duplicate. */
133
134 RTListAppend(&pCtx->listProviders, &pProvider->nodeContext);
135
136 vboxHostChannelUnlock();
137 }
138
139 if (RT_FAILURE(rc))
140 {
141 vhcProviderRelease(pProvider);
142 }
143
144 return rc;
145}
146
147static int vhcProviderUnregister(VBOXHOSTCHPROVIDER *pProvider)
148{
149 int rc = vboxHostChannelLock();
150
151 if (RT_SUCCESS(rc))
152 {
153 /* @todo check that the provider is in the list. */
154 /* @todo mark the provider as invalid in each instance. also detach channels? */
155
156 RTListNodeRemove(&pProvider->nodeContext);
157
158 vboxHostChannelUnlock();
159
160 vhcProviderRelease(pProvider);
161 }
162
163 return rc;
164}
165
166
167/*
168 * Select an unique handle for the new channel.
169 * Works under the lock.
170 */
171static int vhcHandleCreate(VBOXHOSTCHCLIENT *pClient, uint32_t *pu32Handle)
172{
173 bool fOver = false;
174
175 for(;;)
176 {
177 uint32_t u32Handle = ASMAtomicIncU32(&pClient->u32HandleSrc);
178
179 if (u32Handle == 0)
180 {
181 if (fOver)
182 {
183 return VERR_NOT_SUPPORTED;
184 }
185
186 fOver = true;
187 continue;
188 }
189
190 VBOXHOSTCHINSTANCE *pDuplicate = NULL;
191 VBOXHOSTCHINSTANCE *pIter;
192 RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
193 {
194 if (pIter->u32Handle == u32Handle)
195 {
196 pDuplicate = pIter;
197 break;
198 }
199 }
200
201 if (pDuplicate == NULL)
202 {
203 *pu32Handle = u32Handle;
204 break;
205 }
206 }
207
208 return VINF_SUCCESS;
209}
210
211
212/*
213 * Channel instance management.
214 */
215
216static void vhcInstanceDestroy(VBOXHOSTCHINSTANCE *pInstance)
217{
218 /* @todo free u32Handle? */
219}
220
221static int32_t vhcInstanceAddRef(VBOXHOSTCHINSTANCE *pInstance)
222{
223 return ASMAtomicIncS32(&pInstance->cRefs);
224}
225
226static void vhcInstanceRelease(VBOXHOSTCHINSTANCE *pInstance)
227{
228 int32_t c = ASMAtomicDecS32(&pInstance->cRefs);
229 Assert(c >= 0);
230 if (c == 0)
231 {
232 vhcInstanceDestroy(pInstance);
233 RTMemFree(pInstance);
234 }
235}
236
237static int vhcInstanceCreate(VBOXHOSTCHCLIENT *pClient, VBOXHOSTCHINSTANCE **ppInstance)
238{
239 int rc = VINF_SUCCESS;
240
241 VBOXHOSTCHINSTANCE *pInstance = (VBOXHOSTCHINSTANCE *)RTMemAllocZ(sizeof(VBOXHOSTCHINSTANCE));
242
243 if (pInstance)
244 {
245 rc = vboxHostChannelLock();
246
247 if (RT_SUCCESS(rc))
248 {
249 rc = vhcHandleCreate(pClient, &pInstance->u32Handle);
250
251 if (RT_SUCCESS(rc))
252 {
253 vhcInstanceAddRef(pInstance);
254
255 *ppInstance = pInstance;
256 }
257
258 vboxHostChannelUnlock();
259 }
260
261 if (RT_FAILURE(rc))
262 {
263 RTMemFree(pInstance);
264 }
265 }
266 else
267 {
268 rc = VERR_NO_MEMORY;
269 }
270
271 return rc;
272}
273
274static VBOXHOSTCHINSTANCE *vhcInstanceFind(VBOXHOSTCHCLIENT *pClient, uint32_t u32Handle)
275{
276 VBOXHOSTCHINSTANCE *pInstance = NULL;
277
278 int rc = vboxHostChannelLock();
279
280 if (RT_SUCCESS(rc))
281 {
282 VBOXHOSTCHINSTANCE *pIter;
283 RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
284 {
285 if (pIter->u32Handle == u32Handle)
286 {
287 pInstance = pIter;
288
289 vhcInstanceAddRef(pInstance);
290
291 break;
292 }
293 }
294
295 vboxHostChannelUnlock();
296 }
297
298 return pInstance;
299}
300
301static VBOXHOSTCHINSTANCE *vhcInstanceFindByChannelPtr(VBOXHOSTCHCLIENT *pClient, void *pvChannel)
302{
303 VBOXHOSTCHINSTANCE *pInstance = NULL;
304
305 int rc = vboxHostChannelLock();
306
307 if (RT_SUCCESS(rc))
308 {
309 VBOXHOSTCHINSTANCE *pIter;
310 RTListForEach(&pClient->listChannels, pIter, VBOXHOSTCHINSTANCE, nodeClient)
311 {
312 if (pIter->pvChannel == pvChannel)
313 {
314 pInstance = pIter;
315
316 vhcInstanceAddRef(pInstance);
317
318 break;
319 }
320 }
321
322 vboxHostChannelUnlock();
323 }
324
325 return pInstance;
326}
327
328static void vhcInstanceDetach(VBOXHOSTCHINSTANCE *pInstance)
329{
330 if (pInstance->pProvider)
331 {
332 pInstance->pProvider->iface.HostChannelDetach(pInstance->pvChannel);
333 RTListNodeRemove(&pInstance->nodeProvider);
334 vhcProviderRelease(pInstance->pProvider);
335 vhcInstanceRelease(pInstance); /* Not in the list anymore. */
336 }
337
338 RTListNodeRemove(&pInstance->nodeClient);
339 vhcInstanceRelease(pInstance); /* Not in the list anymore. */
340}
341
342
343/*
344 * Host channel service functions.
345 */
346
347int vboxHostChannelInit(void)
348{
349 VBOXHOSTCHCTX *pCtx = &g_ctx;
350
351 if (pCtx->fInitialized)
352 {
353 return VERR_NOT_SUPPORTED;
354 }
355
356 pCtx->fInitialized = true;
357 RTListInit(&pCtx->listProviders);
358
359 return VINF_SUCCESS;
360}
361
362void vboxHostChannelDestroy(void)
363{
364 VBOXHOSTCHCTX *pCtx = &g_ctx;
365
366 VBOXHOSTCHPROVIDER *pIter;
367 VBOXHOSTCHPROVIDER *pIterNext;
368 RTListForEachSafe(&pCtx->listProviders, pIter, pIterNext, VBOXHOSTCHPROVIDER, nodeContext)
369 {
370 vhcProviderUnregister(pIter);
371 }
372 pCtx->fInitialized = false;
373}
374
375int vboxHostChannelClientConnect(VBOXHOSTCHCLIENT *pClient)
376{
377 /* A guest client is connecting to the service.
378 * Later the client will use Attach calls to connect to channel providers.
379 * pClient is already zeroed.
380 */
381 pClient->pCtx = &g_ctx;
382
383 RTListInit(&pClient->listChannels);
384 RTListInit(&pClient->listEvents);
385
386 return VINF_SUCCESS;
387}
388
389void vboxHostChannelClientDisconnect(VBOXHOSTCHCLIENT *pClient)
390{
391 /* If there are attached channels, detach them. */
392 VBOXHOSTCHINSTANCE *pIter;
393 VBOXHOSTCHINSTANCE *pIterNext;
394 RTListForEachSafe(&pClient->listChannels, pIter, pIterNext, VBOXHOSTCHINSTANCE, nodeClient)
395 {
396 vhcInstanceDetach(pIter);
397 }
398}
399
400int vboxHostChannelAttach(VBOXHOSTCHCLIENT *pClient,
401 uint32_t *pu32Handle,
402 const char *pszName,
403 uint32_t u32Flags)
404{
405 int rc = VINF_SUCCESS;
406
407 HOSTCHLOG(("HostChannel: Attach: (%d) [%s] 0x%08X\n", pClient->u32ClientID, pszName, u32Flags));
408
409 /* Look if there is a provider. */
410 VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
411
412 if (pProvider)
413 {
414 VBOXHOSTCHINSTANCE *pInstance = NULL;
415
416 rc = vhcInstanceCreate(pClient, &pInstance);
417
418 if (RT_SUCCESS(rc))
419 {
420 void *pvChannel = NULL;
421 rc = pProvider->iface.HostChannelAttach(pProvider->iface.pvProvider,
422 &pvChannel,
423 u32Flags,
424 &g_callbacks, pClient);
425 if (RT_SUCCESS(rc))
426 {
427 vhcProviderAddRef(pProvider);
428 pInstance->pProvider = pProvider;
429
430 pInstance->pClient = pClient;
431 pInstance->pvChannel = pvChannel;
432
433 vhcInstanceAddRef(pInstance); /* Referenced by the list client's channels. */
434 RTListAppend(&pClient->listChannels, &pInstance->nodeClient);
435
436 vhcInstanceAddRef(pInstance); /* Referenced by the list of provider's channels. */
437 RTListAppend(&pProvider->listChannels, &pInstance->nodeProvider);
438
439 *pu32Handle = pInstance->u32Handle;
440
441 HOSTCHLOG(("HostChannel: Attach: (%d) handle %d\n", pClient->u32ClientID, pInstance->u32Handle));
442 }
443
444 vhcInstanceRelease(pInstance);
445 }
446
447 vhcProviderRelease(pProvider);
448 }
449 else
450 {
451 rc = VERR_NOT_SUPPORTED;
452 }
453
454 return rc;
455}
456
457int vboxHostChannelDetach(VBOXHOSTCHCLIENT *pClient,
458 uint32_t u32Handle)
459{
460 HOSTCHLOG(("HostChannel: Detach: (%d) handle %d\n", pClient->u32ClientID, u32Handle));
461
462 int rc = VINF_SUCCESS;
463
464 VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
465
466 if (pInstance)
467 {
468 vhcInstanceDetach(pInstance);
469
470 vhcInstanceRelease(pInstance);
471 }
472 else
473 {
474 rc = VERR_NOT_SUPPORTED;
475 }
476
477 return rc;
478}
479
480int vboxHostChannelSend(VBOXHOSTCHCLIENT *pClient,
481 uint32_t u32Handle,
482 const void *pvData,
483 uint32_t cbData)
484{
485 HOSTCHLOG(("HostChannel: Send: (%d) handle %d, %d bytes\n", pClient->u32ClientID, u32Handle, cbData));
486
487 int rc = VINF_SUCCESS;
488
489 VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
490
491 if ( pInstance
492 && pInstance->pProvider)
493 {
494 pInstance->pProvider->iface.HostChannelSend(pInstance->pvChannel, pvData, cbData);
495
496 vhcInstanceRelease(pInstance);
497 }
498 else
499 {
500 rc = VERR_NOT_SUPPORTED;
501 }
502
503 return rc;
504}
505
506int vboxHostChannelRecv(VBOXHOSTCHCLIENT *pClient,
507 uint32_t u32Handle,
508 void *pvData,
509 uint32_t cbData,
510 uint32_t *pu32SizeReceived,
511 uint32_t *pu32SizeRemaining)
512{
513 HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
514
515 int rc = VINF_SUCCESS;
516
517 VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
518
519 if ( pInstance
520 && pInstance->pProvider)
521 {
522 rc = pInstance->pProvider->iface.HostChannelRecv(pInstance->pvChannel, pvData, cbData,
523 pu32SizeReceived, pu32SizeRemaining);
524
525 HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, recv %d, rem %d\n",
526 pClient->u32ClientID, u32Handle, rc, cbData, *pu32SizeReceived, *pu32SizeRemaining));
527
528 vhcInstanceRelease(pInstance);
529 }
530 else
531 {
532 rc = VERR_NOT_SUPPORTED;
533 }
534
535 return rc;
536}
537
538int vboxHostChannelControl(VBOXHOSTCHCLIENT *pClient,
539 uint32_t u32Handle,
540 uint32_t u32Code,
541 void *pvParm,
542 uint32_t cbParm,
543 void *pvData,
544 uint32_t cbData,
545 uint32_t *pu32SizeDataReturned)
546{
547 HOSTCHLOG(("HostChannel: Control: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
548
549 int rc = VINF_SUCCESS;
550
551 VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFind(pClient, u32Handle);
552
553 if ( pInstance
554 && pInstance->pProvider)
555 {
556 pInstance->pProvider->iface.HostChannelControl(pInstance->pvChannel, u32Code,
557 pvParm, cbParm,
558 pvData, cbData, pu32SizeDataReturned);
559
560 vhcInstanceRelease(pInstance);
561 }
562 else
563 {
564 rc = VERR_NOT_SUPPORTED;
565 }
566
567 return rc;
568}
569
570typedef struct VBOXHOSTCHANNELEVENT
571{
572 RTLISTNODE NodeEvent;
573
574 uint32_t u32ChannelHandle;
575
576 uint32_t u32Id;
577 void *pvEvent;
578 uint32_t cbEvent;
579} VBOXHOSTCHANNELEVENT;
580
581/* This is called under the lock. */
582int vboxHostChannelQueryEvent(VBOXHOSTCHCLIENT *pClient,
583 bool *pfEvent,
584 uint32_t *pu32Handle,
585 uint32_t *pu32Id,
586 void *pvParm,
587 uint32_t cbParm,
588 uint32_t *pcbParmOut)
589{
590 /* Check if there is something in the client's event queue. */
591
592 VBOXHOSTCHANNELEVENT *pEvent = RTListGetFirst(&pClient->listEvents, VBOXHOSTCHANNELEVENT, NodeEvent);
593
594 HOSTCHLOG(("HostChannel: QueryEvent: (%d), event %p\n", pClient->u32ClientID, pEvent));
595
596 if (pEvent)
597 {
598 /* Report the event. */
599 RTListNodeRemove(&pEvent->NodeEvent);
600
601 *pfEvent = true;
602 *pu32Handle = pEvent->u32ChannelHandle;
603 *pu32Id = pEvent->u32Id;
604
605 uint32_t cbToCopy = RT_MIN(cbParm, pEvent->cbEvent);
606
607 HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbParm %d, cbEvent %d\n",
608 pClient->u32ClientID, cbParm, pEvent->cbEvent));
609
610 if (cbToCopy > 0)
611 {
612 memcpy(pvParm, pEvent->pvEvent, cbToCopy);
613 }
614
615 *pcbParmOut = cbToCopy;
616
617 RTMemFree(pEvent);
618 }
619 else
620 {
621 /* Tell the caller that there is no event. */
622 *pfEvent = false;
623 }
624
625 return VINF_SUCCESS;
626}
627
628static DECLCALLBACK(void) HostChannelCallbackEvent(void *pvCallbacks, void *pvChannel,
629 uint32_t u32Id, const void *pvEvent, uint32_t cbEvent)
630{
631 VBOXHOSTCHCLIENT *pClient = (VBOXHOSTCHCLIENT *)pvCallbacks;
632
633 VBOXHOSTCHINSTANCE *pInstance = vhcInstanceFindByChannelPtr(pClient, pvChannel);
634
635 HOSTCHLOG(("HostChannel: CallbackEvent: (%d) instance %p\n",
636 pClient->u32ClientID, pInstance));
637
638 if (!pInstance)
639 {
640#ifdef DEBUG_sunlover
641 AssertFailed();
642#endif
643 return;
644 }
645
646 int rc = vboxHostChannelLock();
647 if (RT_FAILURE(rc))
648 {
649 return;
650 }
651
652 uint32_t u32ChannelHandle = pInstance->u32Handle;
653
654 HOSTCHLOG(("HostChannel: CallbackEvent: (%d) handle %d, async %d, cbEvent %d\n",
655 pClient->u32ClientID, u32ChannelHandle, pClient->fAsync, cbEvent));
656
657 /* Check whether the event is waited. */
658 if (pClient->fAsync)
659 {
660 /* Report the event. */
661 vboxHostChannelReportAsync(pClient, u32ChannelHandle, u32Id, pvEvent, cbEvent);
662
663 pClient->fAsync = false;
664 }
665 else
666 {
667 /* Put it to the queue. */
668 VBOXHOSTCHANNELEVENT *pEvent = (VBOXHOSTCHANNELEVENT *)RTMemAlloc(sizeof(VBOXHOSTCHANNELEVENT) + cbEvent);
669
670 if (pEvent)
671 {
672 pEvent->u32ChannelHandle = u32ChannelHandle;
673 pEvent->u32Id = u32Id;
674
675 if (cbEvent)
676 {
677 pEvent->pvEvent = &pEvent[1];
678 memcpy(pEvent->pvEvent, pvEvent, cbEvent);
679 }
680 else
681 {
682 pEvent->pvEvent = NULL;
683 }
684
685 pEvent->cbEvent = cbEvent;
686
687 if (RT_SUCCESS(rc))
688 {
689 RTListAppend(&pClient->listEvents, &pEvent->NodeEvent);
690 }
691 else
692 {
693 RTMemFree(pEvent);
694 }
695 }
696 }
697
698 vboxHostChannelUnlock();
699}
700
701int vboxHostChannelQuery(VBOXHOSTCHCLIENT *pClient,
702 const char *pszName,
703 uint32_t u32Code,
704 void *pvParm,
705 uint32_t cbParm,
706 void *pvData,
707 uint32_t cbData,
708 uint32_t *pu32SizeDataReturned)
709{
710 HOSTCHLOG(("HostChannel: Query: (%d) name [%s], cbData %d\n", pClient->u32ClientID, pszName, cbData));
711
712 int rc = VINF_SUCCESS;
713
714 /* Look if there is a provider. */
715 VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pClient->pCtx, pszName);
716
717 if (pProvider)
718 {
719 pProvider->iface.HostChannelControl(NULL, u32Code,
720 pvParm, cbParm,
721 pvData, cbData, pu32SizeDataReturned);
722
723 vhcProviderRelease(pProvider);
724 }
725 else
726 {
727 rc = VERR_NOT_SUPPORTED;
728 }
729
730 return rc;
731}
732
733int vboxHostChannelRegister(const char *pszName,
734 const VBOXHOSTCHANNELINTERFACE *pInterface,
735 uint32_t cbInterface)
736{
737 int rc = VINF_SUCCESS;
738
739 VBOXHOSTCHCTX *pCtx = &g_ctx;
740
741 VBOXHOSTCHPROVIDER *pProvider = (VBOXHOSTCHPROVIDER *)RTMemAllocZ(sizeof(VBOXHOSTCHPROVIDER));
742
743 if (pProvider)
744 {
745 pProvider->pCtx = pCtx;
746 pProvider->iface = *pInterface;
747
748 RTListInit(&pProvider->listChannels);
749
750 pProvider->pszName = RTStrDup(pszName);
751 if (pProvider->pszName)
752 {
753 vhcProviderAddRef(pProvider);
754 rc = vhcProviderRegister(pCtx, pProvider);
755 }
756 else
757 {
758 RTMemFree(pProvider);
759 rc = VERR_NO_MEMORY;
760 }
761 }
762 else
763 {
764 rc = VERR_NO_MEMORY;
765 }
766
767 return rc;
768}
769
770int vboxHostChannelUnregister(const char *pszName)
771{
772 int rc = VINF_SUCCESS;
773
774 VBOXHOSTCHCTX *pCtx = &g_ctx;
775
776 VBOXHOSTCHPROVIDER *pProvider = vhcProviderFind(pCtx, pszName);
777
778 if (pProvider)
779 {
780 rc = vhcProviderUnregister(pProvider);
781 vhcProviderRelease(pProvider);
782 }
783
784 return rc;
785}
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette