/** @file
* Host channel.
*/
/*
* Copyright (C) 2012 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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 "HostChannel.h"
/* A registered provider of channels. */
typedef struct VBOXHOSTCHPROVIDER
{
char *pszName;
/* An established channel. */
typedef struct VBOXHOSTCHINSTANCE
{
RTLISTNODE nodeProvider; /* In the provider, needed for cleanup when the provider is unregistered. */
struct VBOXHOSTCHCTX
{
bool fInitialized;
};
/* 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
{
VBOXHOSTCHCLIENT *pClient; /* The client which uses the channel, NULL when the client does not exist. */
/* Only one service instance is supported. */
{
};
/*
* Provider management.
*/
{
}
{
}
{
Assert(c >= 0);
if (c == 0)
{
}
}
{
if (RT_SUCCESS(rc))
{
{
{
break;
}
}
}
return pProvider;
}
{
if (RT_SUCCESS(rc))
{
/* @todo check a duplicate. */
}
if (RT_FAILURE(rc))
{
}
return rc;
}
{
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? */
}
return rc;
}
/*
* Select an unique handle for the new channel.
* Works under the lock.
*/
{
bool fOver = false;
for(;;)
{
if (u32Handle == 0)
{
if (fOver)
{
return VERR_NOT_SUPPORTED;
}
fOver = true;
continue;
}
{
{
pDuplicate = pIter;
break;
}
}
if (pDuplicate == NULL)
{
*pu32Handle = u32Handle;
break;
}
}
return VINF_SUCCESS;
}
/*
* Channel instance management.
*/
{
}
{
}
{
Assert(c >= 0);
if (c == 0)
{
}
}
{
if (pInstance)
{
rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/* Used by the client, that is in the list of channels. */
/* Add to the list of created channel instances. It is inactive while pClient is 0. */
/* Return to the caller. */
*ppInstance = pInstance;
}
}
if (RT_FAILURE(rc))
{
}
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
{
if (RT_SUCCESS(rc))
{
{
{
break;
}
}
}
return pInstance;
}
{
{
return NULL;
}
if (RT_SUCCESS(rc))
{
{
{
break;
}
}
}
return pInstance;
}
{
{
}
if (RT_SUCCESS(rc))
{
}
}
/*
* Channel callback contexts.
*/
{
VBOXHOSTCHCALLBACKCTX *pCallbackCtx = (VBOXHOSTCHCALLBACKCTX *)RTMemAllocZ(sizeof(VBOXHOSTCHCALLBACKCTX));
if (pCallbackCtx != NULL)
{
/* The callback context is accessed by the providers threads. */
rc = vboxHostChannelLock();
if (RT_SUCCESS(rc))
{
}
else
{
}
}
else
{
rc = VERR_NO_MEMORY;
}
if (RT_SUCCESS(rc))
{
}
return rc;
}
{
if (RT_SUCCESS(rc))
{
{
/* The callback is associated with a client.
* Check that the callback is in the list and remove it from the list.
*/
bool fFound = false;
{
if (pIter == pCallbackCtx)
{
fFound = true;
break;
}
}
if (fFound)
{
}
else
{
AssertFailed();
}
}
else
{
/* It is not in the clients anymore. May be the client has been disconnected.
* Just free the memory.
*/
}
}
if (RT_SUCCESS(rc))
{
}
return rc;
}
/*
* Host channel service functions.
*/
int vboxHostChannelInit(void)
{
if (pCtx->fInitialized)
{
return VERR_NOT_SUPPORTED;
}
pCtx->fInitialized = true;
return VINF_SUCCESS;
}
void vboxHostChannelDestroy(void)
{
{
}
pCtx->fInitialized = false;
}
{
/* A guest client is connecting to the service.
* Later the client will use Attach calls to connect to channel providers.
* pClient is already zeroed.
*/
return VINF_SUCCESS;
}
{
/* Clear the list of contexts and prevent acceess to the client. */
if (RT_SUCCESS(rc))
{
{
}
}
/* If there are attached channels, detach them. */
{
}
}
const char *pszName,
{
/* Look if there is a provider. */
if (pProvider)
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
if (RT_SUCCESS(rc))
{
/* It is already in the channels list of the client. */
}
if (RT_FAILURE(rc))
{
}
}
if (RT_FAILURE(rc))
{
}
}
}
else
{
}
return rc;
}
{
if (pInstance)
{
}
else
{
}
return rc;
}
const void *pvData,
{
HOSTCHLOG(("HostChannel: Send: (%d) handle %d, %d bytes\n", pClient->u32ClientID, u32Handle, cbData));
if (pInstance)
{
{
}
}
else
{
}
return rc;
}
void *pvData,
{
HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
if (pInstance)
{
{
HOSTCHLOG(("HostChannel: Recv: (%d) handle %d, rc %Rrc, recv %d, rem %d\n",
}
}
else
{
}
return rc;
}
void *pvParm,
void *pvData,
{
HOSTCHLOG(("HostChannel: Control: (%d) handle %d, cbData %d\n", pClient->u32ClientID, u32Handle, cbData));
if (pInstance)
{
{
}
}
else
{
}
return rc;
}
typedef struct VBOXHOSTCHANNELEVENT
{
void *pvEvent;
bool *pfEvent,
{
if (RT_FAILURE(rc))
{
return rc;
}
{
/* If there is a wait request already, cancel it. */
}
/* Check if there is something in the client's event queue. */
VBOXHOSTCHANNELEVENT *pEvent = RTListGetFirst(&pClient->listEvents, VBOXHOSTCHANNELEVENT, NodeEvent);
if (pEvent)
{
/* Report the event. */
HOSTCHLOG(("HostChannel: QueryEvent: (%d), cbEvent %d\n",
*pfEvent = true;
}
else
{
/* No event available at the time. Process asynchronously. */
/* Tell the caller that there is no event. */
*pfEvent = false;
}
return rc;
}
{
if (RT_SUCCESS(rc))
{
{
/* If there is a wait request alredy, cancel it. */
}
}
return rc;
}
/* @thread provider */
{
if (RT_FAILURE(rc))
{
return;
}
/* Check that the structure is still associated with a client.
* The client can disconnect and will be invalid.
*/
{
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client gone.\n"));
/* The client does not exist anymore, skip the event. */
return;
}
bool fFound = false;
{
if (pIter == pCallbackCtx)
{
fFound = true;
break;
}
}
if (!fFound)
{
AssertFailed();
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: client does not have the context.\n"));
/* The context is not in the list of contexts. Skip the event. */
return;
}
HOSTCHLOG(("HostChannel: CallbackEvent[%p]: (%d) instance %p\n",
if (!pInstance)
{
/* Instance was already detached. Skip the event. */
return;
}
HOSTCHLOG(("HostChannel: CallbackEvent: (%d) handle %d, async %d, cbEvent %d\n",
/* Check whether the event is waited. */
{
/* Report the event. */
}
else
{
/* Put it to the queue. */
VBOXHOSTCHANNELEVENT *pEvent = (VBOXHOSTCHANNELEVENT *)RTMemAlloc(sizeof(VBOXHOSTCHANNELEVENT) + cbEvent);
if (pEvent)
{
if (cbEvent)
{
}
else
{
}
}
}
}
/* @thread provider */
{
}
const char *pszName,
void *pvParm,
void *pvData,
{
HOSTCHLOG(("HostChannel: Query: (%d) name [%s], cbData %d\n", pClient->u32ClientID, pszName, cbData));
/* Look if there is a provider. */
if (pProvider)
{
}
else
{
}
return rc;
}
const VBOXHOSTCHANNELINTERFACE *pInterface,
{
if (pProvider)
{
{
}
else
{
rc = VERR_NO_MEMORY;
}
}
else
{
rc = VERR_NO_MEMORY;
}
return rc;
}
{
if (pProvider)
{
}
return rc;
}