summaryrefslogtreecommitdiff
path: root/src/VBox/HostServices/GuestControl/service.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/VBox/HostServices/GuestControl/service.cpp')
-rw-r--r--src/VBox/HostServices/GuestControl/service.cpp1821
1 files changed, 1180 insertions, 641 deletions
diff --git a/src/VBox/HostServices/GuestControl/service.cpp b/src/VBox/HostServices/GuestControl/service.cpp
index b4ecaf1a..bbfd8cee 100644
--- a/src/VBox/HostServices/GuestControl/service.cpp
+++ b/src/VBox/HostServices/GuestControl/service.cpp
@@ -4,7 +4,7 @@
*/
/*
- * Copyright (C) 2011-2012 Oracle Corporation
+ * Copyright (C) 2011-2013 Oracle Corporation
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
@@ -49,26 +49,34 @@
* wait for getting its PID returned by the client) as well as cancelling blocking
* host calls in order the client terminated/crashed (HGCM detects disconnected
* clients and reports it to this service's callback).
+ *
+ * Starting at VBox 4.2 the context ID itself consists of a session ID, an object
+ * ID (for example a process or file ID) and a count. This is necessary to not break
+ * compatibility between older hosts and to manage guest session on the host.
*/
/*******************************************************************************
* Header Files *
*******************************************************************************/
-#define LOG_GROUP LOG_GROUP_HGCM
+#ifdef LOG_GROUP
+ #undef LOG_GROUP
+#endif
+#define LOG_GROUP LOG_GROUP_GUEST_CONTROL
#include <VBox/HostServices/GuestControlSvc.h>
#include <VBox/log.h>
-#include <iprt/asm.h> /* For ASMBreakpoint(). */
#include <iprt/assert.h>
#include <iprt/cpp/autores.h>
#include <iprt/cpp/utils.h>
#include <iprt/err.h>
#include <iprt/mem.h>
+#include <iprt/list.h>
#include <iprt/req.h>
#include <iprt/string.h>
#include <iprt/thread.h>
#include <iprt/time.h>
+#include <map>
#include <memory> /* for auto_ptr */
#include <string>
#include <list>
@@ -77,107 +85,836 @@
namespace guestControl {
-/**
- * Structure for holding all clients with their
- * generated host contexts. This is necessary for
- * maintaining the relationship between a client and its context IDs.
- */
-struct ClientContexts
-{
- /** This client ID. */
- uint32_t mClientID;
- /** The list of contexts a client is assigned to. */
- std::list< uint32_t > mContextList;
-
- /** The normal constructor. */
- ClientContexts(uint32_t aClientID)
- : mClientID(aClientID) {}
-};
-/** The client list + iterator type */
-typedef std::list< ClientContexts > ClientContextsList;
-typedef std::list< ClientContexts >::iterator ClientContextsListIter;
-typedef std::list< ClientContexts >::const_iterator ClientContextsListIterConst;
+/** Flag for indicating that the client only is interested in
+ * messages of specific context IDs. */
+#define CLIENTSTATE_FLAG_CONTEXTFILTER RT_BIT(0)
/**
- * Structure for holding an uncompleted guest call.
+ * Structure for maintaining a pending (that is, a deferred and not yet completed)
+ * client command.
*/
-struct ClientWaiter
+typedef struct ClientConnection
{
- /** Client ID; a client can have multiple handles! */
- uint32_t mClientID;
/** The call handle */
VBOXHGCMCALLHANDLE mHandle;
- /** The call parameters */
- VBOXHGCMSVCPARM *mParms;
/** Number of parameters */
uint32_t mNumParms;
-
+ /** The call parameters */
+ VBOXHGCMSVCPARM *mParms;
/** The standard constructor. */
- ClientWaiter() : mClientID(0), mHandle(0), mParms(NULL), mNumParms(0) {}
- /** The normal constructor. */
- ClientWaiter(uint32_t aClientID, VBOXHGCMCALLHANDLE aHandle,
- VBOXHGCMSVCPARM aParms[], uint32_t cParms)
- : mClientID(aClientID), mHandle(aHandle), mParms(aParms), mNumParms(cParms) {}
-};
-/** The guest call list type */
-typedef std::list< ClientWaiter > ClientWaiterList;
-typedef std::list< ClientWaiter >::iterator CallListIter;
-typedef std::list< ClientWaiter >::const_iterator CallListIterConst;
+ ClientConnection(void)
+ : mHandle(0), mNumParms(0), mParms(NULL) {}
+} ClientConnection;
/**
- * Structure for holding a buffered host command.
+ * Structure for holding a buffered host command which has
+ * not been processed yet.
*/
-struct HostCmd
+typedef struct HostCommand
{
+ RTLISTNODE Node;
+
+ uint32_t AddRef(void)
+ {
+#ifdef DEBUG_andy
+ LogFlowFunc(("Adding reference pHostCmd=%p, CID=%RU32, new refCount=%RU32\n",
+ this, mContextID, mRefCount + 1));
+#endif
+ return ++mRefCount;
+ }
+
+ uint32_t Release(void)
+ {
+#ifdef DEBUG_andy
+ LogFlowFunc(("Releasing reference pHostCmd=%p, CID=%RU32, new refCount=%RU32\n",
+ this, mContextID, mRefCount - 1));
+#endif
+ /* Release reference for current command. */
+ Assert(mRefCount);
+ if (--mRefCount == 0)
+ Free();
+
+ return mRefCount;
+ }
+
+ /**
+ * Allocates the command with an HGCM request. Needs to be free'd using Free().
+ *
+ * @return IPRT status code.
+ * @param uMsg Message type.
+ * @param cParms Number of parameters of HGCM request.
+ * @param paParms Array of parameters of HGCM request.
+ */
+ int Allocate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+ {
+ LogFlowFunc(("Allocating pHostCmd=%p, uMsg=%RU32, cParms=%RU32, paParms=%p\n",
+ this, uMsg, cParms, paParms));
+
+ if (!cParms) /* At least one parameter (context ID) must be present. */
+ return VERR_INVALID_PARAMETER;
+
+ AssertPtrReturn(paParms, VERR_INVALID_POINTER);
+
+ /* Paranoia. */
+ if (cParms > 256)
+ cParms = 256;
+
+ int rc = VINF_SUCCESS;
+
+ /*
+ * Don't verify anything here (yet), because this function only buffers
+ * the HGCM data into an internal structure and reaches it back to the guest (client)
+ * in an unmodified state.
+ */
+ mMsgType = uMsg;
+ mParmCount = cParms;
+ if (mParmCount)
+ {
+ mpParms = (VBOXHGCMSVCPARM*)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * mParmCount);
+ if (NULL == mpParms)
+ rc = VERR_NO_MEMORY;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t i = 0; i < mParmCount; i++)
+ {
+ mpParms[i].type = paParms[i].type;
+ switch (paParms[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_32BIT:
+ mpParms[i].u.uint32 = paParms[i].u.uint32;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_64BIT:
+ mpParms[i].u.uint64 = paParms[i].u.uint64;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_PTR:
+ mpParms[i].u.pointer.size = paParms[i].u.pointer.size;
+ if (mpParms[i].u.pointer.size > 0)
+ {
+ mpParms[i].u.pointer.addr = RTMemAlloc(mpParms[i].u.pointer.size);
+ if (NULL == mpParms[i].u.pointer.addr)
+ {
+ rc = VERR_NO_MEMORY;
+ break;
+ }
+ else
+ memcpy(mpParms[i].u.pointer.addr,
+ paParms[i].u.pointer.addr,
+ mpParms[i].u.pointer.size);
+ }
+ else
+ {
+ /* Size is 0 -- make sure we don't have any pointer. */
+ mpParms[i].u.pointer.addr = NULL;
+ }
+ break;
+
+ default:
+ break;
+ }
+ if (RT_FAILURE(rc))
+ break;
+ }
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ /*
+ * Assume that the context ID *always* is the first parameter,
+ * assign the context ID to the command.
+ */
+ rc = mpParms[0].getUInt32(&mContextID);
+
+ /* Set timestamp so that clients can distinguish between already
+ * processed commands and new ones. */
+ mTimestamp = RTTimeNanoTS();
+ }
+
+ LogFlowFunc(("Returned with rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ /**
+ * Frees the buffered HGCM request.
+ *
+ * @return IPRT status code.
+ */
+ void Free(void)
+ {
+ AssertMsg(mRefCount == 0, ("pHostCmd=%p, CID=%RU32 still being used by a client (%RU32 refs), cannot free yet\n",
+ this, mContextID, mRefCount));
+
+ LogFlowFunc(("Freeing host command pHostCmd=%p, CID=%RU32, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
+ this, mContextID, mMsgType, mParmCount, mpParms));
+
+ for (uint32_t i = 0; i < mParmCount; i++)
+ {
+ switch (mpParms[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_PTR:
+ if (mpParms[i].u.pointer.size > 0)
+ RTMemFree(mpParms[i].u.pointer.addr);
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (mpParms)
+ {
+ RTMemFree(mpParms);
+ mpParms = NULL;
+ }
+
+ mParmCount = 0;
+
+ /* Removes the command from its list */
+ RTListNodeRemove(&Node);
+ }
+
+ /**
+ * Copies data from the buffered HGCM request to the current HGCM request.
+ *
+ * @return IPRT status code.
+ * @param paDstParms Array of parameters of HGCM request to fill the data into.
+ * @param cPDstarms Number of parameters the HGCM request can handle.
+ * @param pSrcBuf Parameter buffer to assign.
+ */
+ int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const
+ {
+ LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mContextID=%RU32 (Session %RU32)\n",
+ this, mMsgType, mParmCount, mContextID, VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(mContextID)));
+
+ int rc = VINF_SUCCESS;
+ if (cDstParms != mParmCount)
+ {
+ LogFlowFunc(("Parameter count does not match (got %RU32, expected %RU32)\n",
+ cDstParms, mParmCount));
+ rc = VERR_INVALID_PARAMETER;
+ }
+
+ if (RT_SUCCESS(rc))
+ {
+ for (uint32_t i = 0; i < mParmCount; i++)
+ {
+ if (paDstParms[i].type != mpParms[i].type)
+ {
+ LogFlowFunc(("Parameter %RU32 type mismatch (got %RU32, expected %RU32)\n",
+ i, paDstParms[i].type, mpParms[i].type));
+ rc = VERR_INVALID_PARAMETER;
+ }
+ else
+ {
+ switch (mpParms[i].type)
+ {
+ case VBOX_HGCM_SVC_PARM_32BIT:
+#ifdef DEBUG_andy
+ LogFlowFunc(("\tmpParms[%RU32] = %RU32 (uint32_t)\n",
+ i, mpParms[i].u.uint32));
+#endif
+ paDstParms[i].u.uint32 = mpParms[i].u.uint32;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_64BIT:
+#ifdef DEBUG_andy
+ LogFlowFunc(("\tmpParms[%RU32] = %RU64 (uint64_t)\n",
+ i, mpParms[i].u.uint64));
+#endif
+ paDstParms[i].u.uint64 = mpParms[i].u.uint64;
+ break;
+
+ case VBOX_HGCM_SVC_PARM_PTR:
+ {
+#ifdef DEBUG_andy
+ LogFlowFunc(("\tmpParms[%RU32] = %p (ptr), size = %RU32\n",
+ i, mpParms[i].u.pointer.addr, mpParms[i].u.pointer.size));
+#endif
+ if (!mpParms[i].u.pointer.size)
+ continue; /* Only copy buffer if there actually is something to copy. */
+
+ if (!paDstParms[i].u.pointer.addr)
+ rc = VERR_INVALID_PARAMETER;
+
+ if ( RT_SUCCESS(rc)
+ && paDstParms[i].u.pointer.size < mpParms[i].u.pointer.size)
+ rc = VERR_BUFFER_OVERFLOW;
+
+ if (RT_SUCCESS(rc))
+ {
+ memcpy(paDstParms[i].u.pointer.addr,
+ mpParms[i].u.pointer.addr,
+ mpParms[i].u.pointer.size);
+ }
+
+ break;
+ }
+
+ default:
+ LogFlowFunc(("Parameter %RU32 of type %RU32 is not supported yet\n",
+ i, mpParms[i].type));
+ rc = VERR_NOT_SUPPORTED;
+ break;
+ }
+ }
+
+ if (RT_FAILURE(rc))
+ {
+ LogFlowFunc(("Parameter %RU32 invalid (%Rrc), refusing\n",
+ i, rc));
+ break;
+ }
+ }
+ }
+
+ LogFlowFunc(("Returned with rc=%Rrc\n", rc));
+ return rc;
+ }
+
+ int Assign(const ClientConnection *pConnection)
+ {
+ AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
+
+ int rc;
+
+ LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
+ this, mMsgType, mParmCount, mpParms));
+
+ /* Does the current host command need more parameter space which
+ * the client does not provide yet? */
+ if (mParmCount > pConnection->mNumParms)
+ {
+ LogFlowFunc(("pHostCmd=%p requires %RU32 parms, only got %RU32 from client\n",
+ this, mParmCount, pConnection->mNumParms));
+
+ /*
+ * So this call apparently failed because the guest wanted to peek
+ * how much parameters it has to supply in order to successfully retrieve
+ * this command. Let's tell him so!
+ */
+ rc = VERR_TOO_MUCH_DATA;
+ }
+ else
+ {
+ rc = CopyTo(pConnection->mParms, pConnection->mNumParms);
+
+ /*
+ * Has there been enough parameter space but the wrong parameter types
+ * were submitted -- maybe the client was just asking for the next upcoming
+ * host message?
+ *
+ * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
+ * in every case.
+ */
+ if (RT_FAILURE(rc))
+ rc = VERR_TOO_MUCH_DATA;
+ }
+
+ return rc;
+ }
+
+ int Peek(const ClientConnection *pConnection)
+ {
+ AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("pHostCmd=%p, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
+ this, mMsgType, mParmCount, mpParms));
+
+ if (pConnection->mNumParms >= 2)
+ {
+ pConnection->mParms[0].setUInt32(mMsgType); /* Message ID */
+ pConnection->mParms[1].setUInt32(mParmCount); /* Required parameters for message */
+ }
+ else
+ LogFlowFunc(("Warning: Client has not (yet) submitted enough parameters (%RU32, must be at least 2) to at least peak for the next message\n",
+ pConnection->mNumParms));
+
+ /*
+ * Always return VERR_TOO_MUCH_DATA data here to
+ * keep it compatible with older clients and to
+ * have correct accounting (mHostRc + mHostCmdTries).
+ */
+ return VERR_TOO_MUCH_DATA;
+ }
+
+ /** Reference count for keeping track how many connected
+ * clients still need to process this command until it can
+ * be removed. */
+ uint32_t mRefCount;
/** The context ID this command belongs to. Will be extracted
- * from the HGCM parameters. */
+ * *always* from HGCM parameter [0]. */
uint32_t mContextID;
- /** How many times the host service has tried to deliver this
- * command to the guest. */
- uint32_t mTries;
/** Dynamic structure for holding the HGCM parms */
- VBOXGUESTCTRPARAMBUFFER mParmBuf;
+ uint32_t mMsgType;
+ /** Number of HGCM parameters. */
+ uint32_t mParmCount;
+ /** Array of HGCM parameters. */
+ PVBOXHGCMSVCPARM mpParms;
+ /** Incoming timestamp (us). */
+ uint64_t mTimestamp;
+} HostCommand;
+typedef std::list< HostCommand *> HostCmdList;
+typedef std::list< HostCommand *>::iterator HostCmdListIter;
+typedef std::list< HostCommand *>::const_iterator HostCmdListIterConst;
+/**
+ * Per-client structure used for book keeping/state tracking a
+ * certain host command.
+ */
+typedef struct ClientContext
+{
+ /* Pointer to list node of this command. */
+ HostCommand *mpHostCmd;
/** The standard constructor. */
- HostCmd() : mContextID(0), mTries(0) {}
-};
-/** The host cmd list + iterator type */
-typedef std::list< HostCmd > HostCmdList;
-typedef std::list< HostCmd >::iterator HostCmdListIter;
-typedef std::list< HostCmd >::const_iterator HostCmdListIterConst;
+ ClientContext(void) : mpHostCmd(NULL) {}
+ /** Internal constrcutor. */
+ ClientContext(HostCommand *pHostCmd) : mpHostCmd(pHostCmd) {}
+} ClientContext;
+typedef std::map< uint32_t, ClientContext > ClientContextMap;
+typedef std::map< uint32_t, ClientContext >::iterator ClientContextMapIter;
+typedef std::map< uint32_t, ClientContext >::const_iterator ClientContextMapIterConst;
+
+/**
+ * Structure for holding a connected guest client
+ * state.
+ */
+typedef struct ClientState
+{
+ ClientState(void)
+ : mSvcHelpers(NULL),
+ mID(0),
+ mFlags(0),
+ mFilterMask(0), mFilterValue(0),
+ mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0),
+ mHostCmdTS(0),
+ mIsPending(false),
+ mPeekCount(0) { }
+
+ ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers, uint32_t uClientID)
+ : mSvcHelpers(pSvcHelpers),
+ mID(uClientID),
+ mFlags(0),
+ mFilterMask(0), mFilterValue(0),
+ mHostCmdRc(VINF_SUCCESS), mHostCmdTries(0),
+ mHostCmdTS(0),
+ mIsPending(false),
+ mPeekCount(0){ }
+
+ void DequeueAll(void)
+ {
+ HostCmdListIter curItem = mHostCmdList.begin();
+ while (curItem != mHostCmdList.end())
+ curItem = Dequeue(curItem);
+ }
+
+ void DequeueCurrent(void)
+ {
+ HostCmdListIter curCmd = mHostCmdList.begin();
+ if (curCmd != mHostCmdList.end())
+ Dequeue(curCmd);
+ }
+
+ HostCmdListIter Dequeue(HostCmdListIter &curItem)
+ {
+ HostCommand *pHostCmd = (*curItem);
+ AssertPtr(pHostCmd);
+
+ if (pHostCmd->Release() == 0)
+ {
+ LogFlowFunc(("[Client %RU32] Destroying pHostCmd=%p\n",
+ mID, (*curItem)));
+
+ delete pHostCmd;
+ pHostCmd = NULL;
+ }
+
+ HostCmdListIter nextItem = mHostCmdList.erase(curItem);
+
+ /* Reset everything else. */
+ mHostCmdRc = VINF_SUCCESS;
+ mHostCmdTries = 0;
+ mPeekCount = 0;
+
+ return nextItem;
+ }
+
+ int EnqueueCommand(HostCommand *pHostCmd)
+ {
+ AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+
+ try
+ {
+ mHostCmdList.push_back(pHostCmd);
+ pHostCmd->AddRef();
+ }
+ catch (std::bad_alloc)
+ {
+ rc = VERR_NO_MEMORY;
+ }
+
+ return rc;
+ }
+
+ bool WantsHostCommand(const HostCommand *pHostCmd) const
+ {
+ AssertPtrReturn(pHostCmd, false);
+
+#ifdef DEBUG_andy
+ LogFlowFunc(("mHostCmdTS=%RU64, pHostCmdTS=%RU64\n",
+ mHostCmdTS, pHostCmd->mTimestamp));
+#endif
+
+ /* Only process newer commands. */
+ if (pHostCmd->mTimestamp <= mHostCmdTS)
+ return false;
+
+ /*
+ * If a sesseion filter is set, only obey those commands we're interested in
+ * by applying our context ID filter mask and compare the result with the
+ * original context ID.
+ */
+ bool fWant;
+ if (mFlags & CLIENTSTATE_FLAG_CONTEXTFILTER)
+ {
+ fWant = (pHostCmd->mContextID & mFilterMask) == mFilterValue;
+ }
+ else /* Client is interested in all commands. */
+ fWant = true;
+
+ LogFlowFunc(("[Client %RU32] mFlags=0x%x, mContextID=%RU32 (session %RU32), mFilterMask=0x%x, mFilterValue=%RU32, fWant=%RTbool\n",
+ mID, mFlags, pHostCmd->mContextID,
+ VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostCmd->mContextID),
+ mFilterMask, mFilterValue, fWant));
+
+ return fWant;
+ }
+
+ int SetPending(const ClientConnection *pConnection)
+ {
+ AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
+
+ if (mIsPending)
+ {
+ LogFlowFunc(("[Client %RU32] Already is in pending mode\n", mID));
+
+ /*
+ * Signal that we don't and can't return yet.
+ */
+ return VINF_HGCM_ASYNC_EXECUTE;
+ }
+
+ if (mHostCmdList.empty())
+ {
+ AssertMsg(mIsPending == false,
+ ("Client ID=%RU32 already is pending but tried to receive a new host command\n", mID));
+
+ mPendingCon.mHandle = pConnection->mHandle;
+ mPendingCon.mNumParms = pConnection->mNumParms;
+ mPendingCon.mParms = pConnection->mParms;
+
+ mIsPending = true;
+
+ LogFlowFunc(("[Client %RU32] Is now in pending mode\n", mID));
+
+ /*
+ * Signal that we don't and can't return yet.
+ */
+ return VINF_HGCM_ASYNC_EXECUTE;
+ }
+
+ /*
+ * Signal that there already is a connection pending.
+ * Shouldn't happen in daily usage.
+ */
+ AssertMsgFailed(("Client already has a connection pending\n"));
+ return VERR_SIGNAL_PENDING;
+ }
+
+ int Run(const ClientConnection *pConnection,
+ HostCommand *pHostCmd)
+ {
+ AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
+
+ int rc = VINF_SUCCESS;
+
+ LogFlowFunc(("[Client %RU32] pConnection=%p, mHostCmdRc=%Rrc, mHostCmdTries=%RU32, mPeekCount=%RU32\n",
+ mID, pConnection, mHostCmdRc, mHostCmdTries, mPeekCount));
+
+ mHostCmdRc = SendReply(pConnection, pHostCmd);
+ LogFlowFunc(("[Client %RU32] Processing pHostCmd=%p ended with rc=%Rrc\n",
+ mID, pHostCmd, mHostCmdRc));
+
+ bool fRemove = false;
+ if (RT_FAILURE(mHostCmdRc))
+ {
+ mHostCmdTries++;
+
+ /*
+ * If the client understood the message but supplied too little buffer space
+ * don't send this message again and drop it after 6 unsuccessful attempts.
+ *
+ * Note: Due to legacy reasons this the retry counter has to be even because on
+ * every peek there will be the actual command retrieval from the client side.
+ * To not get the actual command if the client actually only wants to peek for
+ * the next command, there needs to be two rounds per try, e.g. 3 rounds = 6 tries.
+ *
+ ** @todo Fix the mess stated above. GUEST_MSG_WAIT should be become GUEST_MSG_PEEK, *only*
+ * (and every time) returning the next upcoming host command (if any, blocking). Then
+ * it's up to the client what to do next, either peeking again or getting the actual
+ * host command via an own GUEST_ type message.
+ */
+ if (mHostCmdRc == VERR_TOO_MUCH_DATA)
+ {
+ if (mHostCmdTries == 6)
+ fRemove = true;
+ }
+ /* Client did not understand the message or something else weird happened. Try again one
+ * more time and drop it if it didn't get handled then. */
+ else if (mHostCmdTries > 1)
+ fRemove = true;
+ }
+ else
+ fRemove = true; /* Everything went fine, remove it. */
+
+ LogFlowFunc(("[Client %RU32] Tried pHostCmd=%p for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n",
+ mID, pHostCmd, mHostCmdTries, mHostCmdRc, fRemove));
+
+ if (RT_SUCCESS(rc))
+ rc = mHostCmdRc;
+
+ if (fRemove)
+ {
+ /** @todo Fix this (slow) lookup. Too late today. */
+ HostCmdListIter curItem = mHostCmdList.begin();
+ while (curItem != mHostCmdList.end())
+ {
+ if ((*curItem) == pHostCmd)
+ {
+ Dequeue(curItem);
+ break;
+ }
+
+ curItem++;
+ }
+ }
+
+ LogFlowFunc(("[Client %RU32] Returned with rc=%Rrc\n", mID, rc));
+ return rc;
+ }
+
+ int RunCurrent(const ClientConnection *pConnection)
+ {
+ AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
+
+ int rc;
+ if (mHostCmdList.empty())
+ {
+ rc = SetPending(pConnection);
+ }
+ else
+ {
+ AssertMsgReturn(!mIsPending,
+ ("Client ID=%RU32 still is in pending mode; can't use another connection\n", mID), VERR_INVALID_PARAMETER);
+
+ HostCmdListIter curCmd = mHostCmdList.begin();
+ Assert(curCmd != mHostCmdList.end());
+ HostCommand *pHostCmd = (*curCmd);
+ AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
+
+ rc = Run(pConnection, pHostCmd);
+ }
+
+ return rc;
+ }
+
+ int Wakeup(void)
+ {
+ int rc = VINF_NO_CHANGE;
+
+ if (mIsPending)
+ {
+ LogFlowFunc(("[Client %RU32] Waking up ...\n", mID));
+
+ rc = VINF_SUCCESS;
+
+ HostCmdListIter curCmd = mHostCmdList.begin();
+ if (curCmd != mHostCmdList.end())
+ {
+ HostCommand *pHostCmd = (*curCmd);
+ AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
+
+ LogFlowFunc(("[Client %RU32] Current host command is pHostCmd=%p, CID=%RU32, cmdType=%RU32, cmdParms=%RU32, refCount=%RU32\n",
+ mID, pHostCmd, pHostCmd->mContextID, pHostCmd->mMsgType, pHostCmd->mParmCount, pHostCmd->mRefCount));
+
+ rc = Run(&mPendingCon, pHostCmd);
+ }
+ else
+ AssertMsgFailed(("Waking up client ID=%RU32 with no host command in queue is a bad idea\n", mID));
+
+ return rc;
+ }
+
+ return VINF_NO_CHANGE;
+ }
+
+ int CancelWaiting(int rcPending)
+ {
+ LogFlowFunc(("[Client %RU32] Cancelling waiting with %Rrc, isPending=%RTbool, pendingNumParms=%RU32, flags=%x\n",
+ mID, rcPending, mIsPending, mPendingCon.mNumParms, mFlags));
+
+ int rc;
+ if ( mIsPending
+ && mPendingCon.mNumParms >= 2)
+ {
+ mPendingCon.mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
+ mPendingCon.mParms[1].setUInt32(0); /* Required parameters for message. */
+
+ AssertPtr(mSvcHelpers);
+ mSvcHelpers->pfnCallComplete(mPendingCon.mHandle, rcPending);
+
+ mIsPending = false;
+
+ rc = VINF_SUCCESS;
+ }
+ else if (mPendingCon.mNumParms < 2)
+ rc = VERR_BUFFER_OVERFLOW;
+ else /** @todo Enqueue command instead of dropping? */
+ rc = VERR_WRONG_ORDER;
+
+ return rc;
+ }
+
+ int SendReply(const ClientConnection *pConnection,
+ HostCommand *pHostCmd)
+ {
+ AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
+ AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
+
+ int rc;
+ /* If the client is in pending mode, always send back
+ * the peek result first. */
+ if (mIsPending)
+ {
+ rc = pHostCmd->Peek(pConnection);
+ mPeekCount++;
+ }
+ else
+ {
+ /* If this is the very first peek, make sure to *always* give back the peeking answer
+ * instead of the actual command, even if this command would fit into the current
+ * connection buffer. */
+ if (!mPeekCount)
+ {
+ rc = pHostCmd->Peek(pConnection);
+ mPeekCount++;
+ }
+ else
+ {
+ /* Try assigning the host command to the client and store the
+ * result code for later use. */
+ rc = pHostCmd->Assign(pConnection);
+ if (RT_FAILURE(rc)) /* If something failed, let the client peek (again). */
+ {
+ rc = pHostCmd->Peek(pConnection);
+ mPeekCount++;
+ }
+ else
+ mPeekCount = 0;
+ }
+ }
+
+ /* Reset pending status. */
+ mIsPending = false;
+
+ /* In any case the client did something, so complete
+ * the pending call with the result we just got. */
+ AssertPtr(mSvcHelpers);
+ mSvcHelpers->pfnCallComplete(pConnection->mHandle, rc);
+
+ LogFlowFunc(("[Client %RU32] mPeekCount=%RU32, pConnection=%p, pHostCmd=%p, replyRc=%Rrc\n",
+ mID, mPeekCount, pConnection, pHostCmd, rc));
+ return rc;
+ }
+
+ PVBOXHGCMSVCHELPERS mSvcHelpers;
+ /** The client's ID. */
+ uint32_t mID;
+ /** Client flags. @sa CLIENTSTATE_FLAG_ flags. */
+ uint32_t mFlags;
+ /** The context ID filter mask, if any. */
+ uint32_t mFilterMask;
+ /** The context ID filter value, if any. */
+ uint32_t mFilterValue;
+ /** Host command list to process. */
+ HostCmdList mHostCmdList;
+ /** Last (most recent) rc after handling the
+ * host command. */
+ int mHostCmdRc;
+ /** How many times the host service has tried to deliver this
+ * command to the according client. */
+ uint32_t mHostCmdTries;
+ /** Timestamp (us) of last host command processed. */
+ uint64_t mHostCmdTS;
+ /**
+ * Flag indicating whether the client currently is pending.
+ * This means the client waits for a new host command to reply
+ * and won't return from the waiting call until a new host
+ * command is available.
+ */
+ bool mIsPending;
+ /**
+ * This is necessary for being compatible with older
+ * Guest Additions. In case there are commands which only
+ * have two (2) parameters and therefore would fit into the
+ * GUEST_MSG_WAIT reply immediately, we now can make sure
+ * that the client first gets back the GUEST_MSG_WAIT results
+ * first.
+ */
+ uint32_t mPeekCount;
+ /** The client's pending connection. */
+ ClientConnection mPendingCon;
+} ClientState;
+typedef std::map< uint32_t, ClientState > ClientStateMap;
+typedef std::map< uint32_t, ClientState >::iterator ClientStateMapIter;
+typedef std::map< uint32_t, ClientState >::const_iterator ClientStateMapIterConst;
/**
* Class containing the shared information service functionality.
*/
class Service : public RTCNonCopyable
{
+
private:
+
/** Type definition for use in callback functions. */
typedef Service SELF;
/** HGCM helper functions. */
PVBOXHGCMSVCHELPERS mpHelpers;
- /*
+ /**
* Callback function supplied by the host for notification of updates
* to properties.
*/
PFNHGCMSVCEXT mpfnHostCallback;
/** User data pointer to be supplied to the host callback function. */
void *mpvHostData;
- /** The deferred calls list. */
- ClientWaiterList mClientWaiterList;
- /** The host command list. */
- HostCmdList mHostCmds;
- /** Client contexts list. */
- ClientContextsList mClientContextsList;
- /** Number of connected clients. */
- uint32_t mNumClients;
+ /** List containing all buffered host commands. */
+ RTLISTANCHOR mHostCmdList;
+ /** Map containing all connected clients. The primary key contains
+ * the HGCM client ID to identify the client. */
+ ClientStateMap mClientStateMap;
public:
explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
: mpHelpers(pHelpers)
, mpfnHostCallback(NULL)
, mpvHostData(NULL)
- , mNumClients(0)
{
+ RTListInit(&mHostCmdList);
}
/**
@@ -204,11 +941,9 @@ public:
void *pvClient)
{
AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
- LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
SELF *pSelf = reinterpret_cast<SELF *>(pvService);
- int rc = pSelf->clientConnect(u32ClientID, pvClient);
- LogFlowFunc (("rc=%Rrc\n", rc));
- return rc;
+ AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
+ return pSelf->clientConnect(u32ClientID, pvClient);
}
/**
@@ -220,11 +955,9 @@ public:
void *pvClient)
{
AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
- LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
SELF *pSelf = reinterpret_cast<SELF *>(pvService);
- int rc = pSelf->clientDisconnect(u32ClientID, pvClient);
- LogFlowFunc (("rc=%Rrc\n", rc));
- return rc;
+ AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
+ return pSelf->clientDisconnect(u32ClientID, pvClient);
}
/**
@@ -240,10 +973,9 @@ public:
VBOXHGCMSVCPARM paParms[])
{
AssertLogRelReturnVoid(VALID_PTR(pvService));
- LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturnVoid(pSelf);
pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
- LogFlowFunc (("returning\n"));
}
/**
@@ -256,11 +988,9 @@ public:
VBOXHGCMSVCPARM paParms[])
{
AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
- LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
SELF *pSelf = reinterpret_cast<SELF *>(pvService);
- int rc = pSelf->hostCall(u32Function, cParms, paParms);
- LogFlowFunc (("rc=%Rrc\n", rc));
- return rc;
+ AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
+ return pSelf->hostCall(u32Function, cParms, paParms);
}
/**
@@ -273,217 +1003,31 @@ public:
{
AssertLogRelReturn(VALID_PTR(pvService), VERR_INVALID_PARAMETER);
SELF *pSelf = reinterpret_cast<SELF *>(pvService);
+ AssertPtrReturn(pSelf, VERR_INVALID_POINTER);
pSelf->mpfnHostCallback = pfnExtension;
pSelf->mpvHostData = pvExtension;
return VINF_SUCCESS;
}
+
private:
- int paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
- void paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf);
- int paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, PVBOXGUESTCTRPARAMBUFFER pSrcBuf);
+
int prepareExecute(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
int clientConnect(uint32_t u32ClientID, void *pvClient);
int clientDisconnect(uint32_t u32ClientID, void *pvClient);
- int assignHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
- int retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientSetMsgFilterSet(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientSetMsgFilterUnset(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int clientSkipMsg(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
int cancelHostCmd(uint32_t u32ContextID);
- int cancelPendingWaits(uint32_t u32ClientID);
- int notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
- int processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
- void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
- void *pvClient, uint32_t eFunction, uint32_t cParms,
- VBOXHGCMSVCPARM paParms[]);
+ int cancelPendingWaits(uint32_t u32ClientID, int rcPending);
+ int hostCallback(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
- int uninit();
+ int sessionClose(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
+ int uninit(void);
};
-
-/**
- * Stores a HGCM request in an internal buffer. Needs to be free'd using paramBufferFree().
- *
- * @return IPRT status code.
- * @param pBuf Buffer to store the HGCM request into.
- * @param uMsg Message type.
- * @param cParms Number of parameters of HGCM request.
- * @param paParms Array of parameters of HGCM request.
- */
-int Service::paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
-{
- AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
- if (cParms)
- AssertPtrReturn(paParms, VERR_INVALID_POINTER);
-
- /* Paranoia. */
- if (cParms > 256)
- cParms = 256;
-
- int rc = VINF_SUCCESS;
-
- /*
- * Don't verify anything here (yet), because this function only buffers
- * the HGCM data into an internal structure and reaches it back to the guest (client)
- * in an unmodified state.
- */
- pBuf->uMsg = uMsg;
- pBuf->uParmCount = cParms;
- if (pBuf->uParmCount)
- {
- pBuf->pParms = (VBOXHGCMSVCPARM*)RTMemAlloc(sizeof(VBOXHGCMSVCPARM) * pBuf->uParmCount);
- if (NULL == pBuf->pParms)
- rc = VERR_NO_MEMORY;
- }
-
- if (RT_SUCCESS(rc))
- {
- for (uint32_t i = 0; i < pBuf->uParmCount; i++)
- {
- pBuf->pParms[i].type = paParms[i].type;
- switch (paParms[i].type)
- {
- case VBOX_HGCM_SVC_PARM_32BIT:
- pBuf->pParms[i].u.uint32 = paParms[i].u.uint32;
- break;
-
- case VBOX_HGCM_SVC_PARM_64BIT:
- /* Not supported yet. */
- break;
-
- case VBOX_HGCM_SVC_PARM_PTR:
- pBuf->pParms[i].u.pointer.size = paParms[i].u.pointer.size;
- if (pBuf->pParms[i].u.pointer.size > 0)
- {
- pBuf->pParms[i].u.pointer.addr = RTMemAlloc(pBuf->pParms[i].u.pointer.size);
- if (NULL == pBuf->pParms[i].u.pointer.addr)
- {
- rc = VERR_NO_MEMORY;
- break;
- }
- else
- memcpy(pBuf->pParms[i].u.pointer.addr,
- paParms[i].u.pointer.addr,
- pBuf->pParms[i].u.pointer.size);
- }
- else
- {
- /* Size is 0 -- make sure we don't have any pointer. */
- pBuf->pParms[i].u.pointer.addr = NULL;
- }
- break;
-
- default:
- break;
- }
- if (RT_FAILURE(rc))
- break;
- }
- }
- return rc;
-}
-
-/**
- * Frees a buffered HGCM request.
- *
- * @return IPRT status code.
- * @param pBuf Parameter buffer to free.
- */
-void Service::paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf)
-{
- AssertPtr(pBuf);
- for (uint32_t i = 0; i < pBuf->uParmCount; i++)
- {
- switch (pBuf->pParms[i].type)
- {
- case VBOX_HGCM_SVC_PARM_PTR:
- if (pBuf->pParms[i].u.pointer.size > 0)
- RTMemFree(pBuf->pParms[i].u.pointer.addr);
- break;
- }
- }
- if (pBuf->uParmCount)
- {
- RTMemFree(pBuf->pParms);
- pBuf->uParmCount = 0;
- }
-}
-
-/**
- * Copies data from a buffered HGCM request to the current HGCM request.
- *
- * @return IPRT status code.
- * @param paDstParms Array of parameters of HGCM request to fill the data into.
- * @param cPDstarms Number of parameters the HGCM request can handle.
- * @param pSrcBuf Parameter buffer to assign.
- */
-int Service::paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, PVBOXGUESTCTRPARAMBUFFER pSrcBuf)
-{
- AssertPtr(pSrcBuf);
- int rc = VINF_SUCCESS;
- if (cDstParms != pSrcBuf->uParmCount)
- {
- LogFlowFunc(("Parameter count does not match (got %u, expected %u)\n",
- cDstParms, pSrcBuf->uParmCount));
- rc = VERR_INVALID_PARAMETER;
- }
- else
- {
- for (uint32_t i = 0; i < pSrcBuf->uParmCount; i++)
- {
- if (paDstParms[i].type != pSrcBuf->pParms[i].type)
- {
- LogFlowFunc(("Parameter %u type mismatch (got %u, expected %u)\n",
- i, paDstParms[i].type, pSrcBuf->pParms[i].type));
- rc = VERR_INVALID_PARAMETER;
- }
- else
- {
- switch (pSrcBuf->pParms[i].type)
- {
- case VBOX_HGCM_SVC_PARM_32BIT:
- paDstParms[i].u.uint32 = pSrcBuf->pParms[i].u.uint32;
- break;
-
- case VBOX_HGCM_SVC_PARM_PTR:
- {
- if (!pSrcBuf->pParms[i].u.pointer.size)
- continue; /* Only copy buffer if there actually is something to copy. */
-
- if (!paDstParms[i].u.pointer.addr)
- rc = VERR_INVALID_PARAMETER;
-
- if (paDstParms[i].u.pointer.size < pSrcBuf->pParms[i].u.pointer.size)
- rc = VERR_BUFFER_OVERFLOW;
-
- if (RT_SUCCESS(rc))
- {
- memcpy(paDstParms[i].u.pointer.addr,
- pSrcBuf->pParms[i].u.pointer.addr,
- pSrcBuf->pParms[i].u.pointer.size);
- }
-
- break;
- }
-
- case VBOX_HGCM_SVC_PARM_64BIT:
- /* Fall through is intentional. */
- default:
- LogFlowFunc(("Parameter %u of type %u is not supported yet\n",
- i, pSrcBuf->pParms[i].type));
- rc = VERR_NOT_SUPPORTED;
- break;
- }
- }
-
- if (RT_FAILURE(rc))
- {
- LogFlowFunc(("Parameter %u invalid (rc=%Rrc), refusing\n",
- i, rc));
- break;
- }
- }
- }
- return rc;
-}
-
/**
* Handles a client which just connected.
*
@@ -493,11 +1037,19 @@ int Service::paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms,
*/
int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
{
- LogFlowFunc(("New client (%ld) connected\n", u32ClientID));
- if (mNumClients < UINT32_MAX)
- mNumClients++;
- else
- AssertMsgFailed(("Max. number of clients reached\n"));
+ LogFlowFunc(("[Client %RU32] Connected\n", u32ClientID));
+#ifdef VBOX_STRICT
+ ClientStateMapIterConst it = mClientStateMap.find(u32ClientID);
+ if (it != mClientStateMap.end())
+ {
+ AssertMsgFailed(("Client with ID=%RU32 already connected when it should not\n",
+ u32ClientID));
+ return VERR_ALREADY_EXISTS;
+ }
+#endif
+ ClientState clientState(mpHelpers, u32ClientID);
+ mClientStateMap[u32ClientID] = clientState;
+ /** @todo Exception handling! */
return VINF_SUCCESS;
}
@@ -512,141 +1064,63 @@ int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
*/
int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient)
{
- LogFlowFunc(("Client (ID=%u, %u clients total) disconnected\n",
- u32ClientID, mNumClients));
- Assert(mNumClients > 0);
- mNumClients--;
-
- /* If this was the last connected (guest) client we need to
- * unblock all eventually queued up (waiting) host calls. */
- bool fAllClientsDisconnected = mNumClients == 0;
- if (fAllClientsDisconnected)
- LogFlowFunc(("No connected clients left, notifying all queued up callbacks\n"));
+ LogFlowFunc(("[Client %RU32] Disconnected (%zu clients total)\n",
+ u32ClientID, mClientStateMap.size()));
+
+ AssertMsg(mClientStateMap.size(),
+ ("No clients in list anymore when there should (client ID=%RU32)\n", u32ClientID));
- /*
- * Throw out all stale clients.
- */
int rc = VINF_SUCCESS;
- CallListIter itCall = mClientWaiterList.begin();
- while (itCall != mClientWaiterList.end())
- {
- if (itCall->mClientID == u32ClientID)
- {
- itCall = mClientWaiterList.erase(itCall);
- }
- else
- itCall++;
- }
+ ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
+ AssertMsg(itClientState != mClientStateMap.end(),
+ ("Clients ID=%RU32 not found in client list when it should be there\n", u32ClientID));
- ClientContextsListIter itContextList = mClientContextsList.begin();
- while ( itContextList != mClientContextsList.end()
- && RT_SUCCESS(rc))
+ if (itClientState != mClientStateMap.end())
{
- /*
- * Unblock/call back all queued items of the specified client
- * or for all items in case there is no waiting client around
- * anymore.
- */
- if ( itContextList->mClientID == u32ClientID
- || fAllClientsDisconnected)
- {
- std::list< uint32_t >::iterator itContext = itContextList->mContextList.begin();
- while (itContext != itContextList->mContextList.end())
- {
- uint32_t uContextID = (*itContext);
-
- /*
- * Notify the host that clients with u32ClientID are no longer
- * around and need to be cleaned up (canceling waits etc).
- */
- LogFlowFunc(("Notifying CID=%u of disconnect ...\n", uContextID));
- rc = cancelHostCmd(uContextID);
- if (RT_FAILURE(rc))
- {
- LogFlowFunc(("Cancelling of CID=%u failed with rc=%Rrc\n",
- uContextID, rc));
- /* Keep going. */
- }
+ itClientState->second.DequeueAll();
- itContext++;
- }
- itContextList = mClientContextsList.erase(itContextList);
- }
- else
- itContextList++;
+ mClientStateMap.erase(itClientState);
}
+ bool fAllClientsDisconnected = mClientStateMap.size() == 0;
if (fAllClientsDisconnected)
{
+ LogFlowFunc(("All clients disconnected, cancelling all host commands ...\n"));
+
/*
* If all clients disconnected we also need to make sure that all buffered
* host commands need to be notified, because Main is waiting a notification
* via a (multi stage) progress object.
*/
- HostCmdListIter itHostCmd;
- for (itHostCmd = mHostCmds.begin(); itHostCmd != mHostCmds.end(); itHostCmd++)
+ HostCommand *pCurCmd = RTListGetFirst(&mHostCmdList, HostCommand, Node);
+ while (pCurCmd)
{
- rc = cancelHostCmd(itHostCmd->mContextID);
- if (RT_FAILURE(rc))
+ HostCommand *pNext = RTListNodeGetNext(&pCurCmd->Node, HostCommand, Node);
+ bool fLast = RTListNodeIsLast(&mHostCmdList, &pCurCmd->Node);
+
+ int rc2 = cancelHostCmd(pCurCmd->mContextID);
+ if (RT_FAILURE(rc2))
{
- LogFlowFunc(("Cancelling of buffered CID=%u failed with rc=%Rrc\n",
- itHostCmd->mContextID, rc));
+ LogFlowFunc(("Cancelling host command with CID=%u (refCount=%RU32) failed with rc=%Rrc\n",
+ pCurCmd->mContextID, pCurCmd->mRefCount, rc2));
/* Keep going. */
}
- paramBufferFree(&itHostCmd->mParmBuf);
- }
-
- mHostCmds.clear();
- }
-
- return rc;
-}
+ while (pCurCmd->Release())
+ ;
+ delete pCurCmd;
+ pCurCmd = NULL;
-/**
- * Assigns a specified host command to a client.
- *
- * @return IPRT status code.
- * @param pCmd Host command to send.
- * @param callHandle Call handle of the client to send the command to.
- * @param cParms Number of parameters.
- * @param paParms Array of parameters.
- */
-int Service::assignHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
-{
- AssertPtrReturn(pCmd, VERR_INVALID_POINTER);
- int rc;
+ if (fLast)
+ break;
- /* Does the current host command need more parameter space which
- * the client does not provide yet? */
- if (pCmd->mParmBuf.uParmCount > cParms)
- {
- paParms[0].setUInt32(pCmd->mParmBuf.uMsg); /* Message ID */
- paParms[1].setUInt32(pCmd->mParmBuf.uParmCount); /* Required parameters for message */
+ pCurCmd = pNext;
+ }
- /*
- * So this call apparently failed because the guest wanted to peek
- * how much parameters it has to supply in order to successfully retrieve
- * this command. Let's tell him so!
- */
- rc = VERR_TOO_MUCH_DATA;
- }
- else
- {
- rc = paramBufferAssign(paParms, cParms, &pCmd->mParmBuf);
-
- /* Has there been enough parameter space but the wrong parameter types
- * were submitted -- maybe the client was just asking for the next upcoming
- * host message?
- *
- * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
- * in every case. */
- if (RT_FAILURE(rc))
- rc = VERR_TOO_MUCH_DATA;
+ Assert(RTListIsEmpty(&mHostCmdList));
}
- LogFlowFunc(("Returned with rc=%Rrc\n", rc));
return rc;
}
@@ -660,91 +1134,125 @@ int Service::assignHostCmdToGuest(HostCmd *pCmd, VBOXHGCMCALLHANDLE callHandle,
* @param cParms Number of parameters.
* @param paParms Array of parameters.
*/
-int Service::retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
- uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+int Service::clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
{
- int rc = VINF_SUCCESS;
-
/*
* Lookup client in our list so that we can assign the context ID of
* a command to that client.
*/
- std::list< ClientContexts >::reverse_iterator it = mClientContextsList.rbegin();
- while (it != mClientContextsList.rend())
- {
- if (it->mClientID == u32ClientID)
- break;
- it++;
- }
+ ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
+ AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
+ u32ClientID));
+ if (itClientState == mClientStateMap.end())
+ return VERR_NOT_FOUND; /* Should never happen. */
- /* Not found? Add client to list. */
- if (it == mClientContextsList.rend())
- {
- mClientContextsList.push_back(ClientContexts(u32ClientID));
- it = mClientContextsList.rbegin();
- }
- Assert(it != mClientContextsList.rend());
+ ClientState &clientState = itClientState->second;
+
+ /* Use the current (inbound) connection. */
+ ClientConnection thisCon;
+ thisCon.mHandle = callHandle;
+ thisCon.mNumParms = cParms;
+ thisCon.mParms = paParms;
+
+ return clientState.RunCurrent(&thisCon);
+}
+int Service::clientSetMsgFilterSet(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
/*
- * If host command list is empty (nothing to do right now) just
- * defer the call until we got something to do (makes the client
- * wait, depending on the flags set).
+ * Lookup client in our list so that we can assign the context ID of
+ * a command to that client.
*/
- if (mHostCmds.empty()) /* If command list is empty, defer ... */
- {
- mClientWaiterList.push_back(ClientWaiter(u32ClientID, callHandle, paParms, cParms));
- rc = VINF_HGCM_ASYNC_EXECUTE;
- }
- else
+ ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
+ AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
+ u32ClientID));
+ if (itClientState == mClientStateMap.end())
+ return VERR_NOT_FOUND; /* Should never happen. */
+
+ if (cParms != 4)
+ return VERR_INVALID_PARAMETER;
+
+ uint32_t uValue, uMaskAdd, uMaskRemove;
+ int rc = paParms[0].getUInt32(&uValue);
+ if (RT_SUCCESS(rc))
+ rc = paParms[1].getUInt32(&uMaskAdd);
+ if (RT_SUCCESS(rc))
+ rc = paParms[2].getUInt32(&uMaskRemove);
+ /** @todo paParm[3] (flags) not used yet. */
+ if (RT_SUCCESS(rc))
{
- /*
- * Get the next unassigned host command in the list.
- */
- HostCmd &curCmd = mHostCmds.front();
- rc = assignHostCmdToGuest(&curCmd, callHandle, cParms, paParms);
- if (RT_SUCCESS(rc))
- {
- /* Remember which client processes which context (for
- * later reference & cleanup). */
- /// @todo r=bird: check if already in the list.
- /// @todo Use a map instead of a list?
- it->mContextList.push_back(curCmd.mContextID);
-
- /* Only if the guest really got and understood the message remove it from the list. */
- paramBufferFree(&curCmd.mParmBuf);
- mHostCmds.pop_front();
- }
- else
- {
- bool fRemoveCmd = false;
- uint32_t uTries = curCmd.mTries++;
-
- /* If the client understood the message but supplied too little buffer space
- * don't send this message again and drop it after 3 unsuccessful attempts.
- * The host then should take care of next actions (maybe retry it with a smaller buffer). */
- if ( rc == VERR_BUFFER_OVERFLOW
- && uTries >= 3)
- {
- fRemoveCmd = true;
- }
- /* Client did not understand the message or something else weird happened. Try again one
- * more time and drop it if it didn't get handled then. */
- else if (uTries > 1)
- fRemoveCmd = true;
-
- if (fRemoveCmd)
- {
- paramBufferFree(&curCmd.mParmBuf);
- mHostCmds.pop_front();
- }
- }
+ ClientState &clientState = itClientState->second;
+
+ clientState.mFlags |= CLIENTSTATE_FLAG_CONTEXTFILTER;
+ if (uMaskAdd)
+ clientState.mFilterMask |= uMaskAdd;
+ if (uMaskRemove)
+ clientState.mFilterMask &= ~uMaskRemove;
+
+ clientState.mFilterValue = uValue;
+
+ LogFlowFunc(("[Client %RU32] Setting message filterMask=0x%x, filterVal=%RU32 set (flags=0x%x, maskAdd=0x%x, maskRemove=0x%x)\n",
+ u32ClientID, clientState.mFilterMask, clientState.mFilterValue,
+ clientState.mFlags, uMaskAdd, uMaskRemove));
}
+
return rc;
}
+int Service::clientSetMsgFilterUnset(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Lookup client in our list so that we can assign the context ID of
+ * a command to that client.
+ */
+ ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
+ AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
+ u32ClientID));
+ if (itClientState == mClientStateMap.end())
+ return VERR_NOT_FOUND; /* Should never happen. */
+
+ if (cParms != 1)
+ return VERR_INVALID_PARAMETER;
+
+ ClientState &clientState = itClientState->second;
+
+ clientState.mFlags &= ~CLIENTSTATE_FLAG_CONTEXTFILTER;
+ clientState.mFilterMask = 0;
+ clientState.mFilterValue = 0;
+
+ LogFlowFunc(("[Client %RU32} Unset message filter\n", u32ClientID));
+ return VINF_SUCCESS;
+}
+
+int Service::clientSkipMsg(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
+ uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+{
+ /*
+ * Lookup client in our list so that we can assign the context ID of
+ * a command to that client.
+ */
+ ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
+ AssertMsg(itClientState != mClientStateMap.end(), ("Client ID=%RU32 not found when it should be present\n",
+ u32ClientID));
+ if (itClientState == mClientStateMap.end())
+ return VERR_NOT_FOUND; /* Should never happen. */
+
+ if (cParms != 1)
+ return VERR_INVALID_PARAMETER;
+
+ LogFlowFunc(("[Client %RU32] Skipping current message ...\n", u32ClientID));
+
+ itClientState->second.DequeueCurrent();
+
+ return VINF_SUCCESS;
+}
+
/**
- * Cancels a buffered host command to unblock waits on Main side
- * (via (multi stage) progress objects.
+ * Cancels a buffered host command to unblock waiting on Main side
+ * via callbacks.
*
* @return IPRT status code.
* @param u32ContextID Context ID of host command to cancel.
@@ -755,14 +1263,11 @@ int Service::cancelHostCmd(uint32_t u32ContextID)
LogFlowFunc(("Cancelling CID=%u ...\n", u32ContextID));
- CALLBACKDATACLIENTDISCONNECTED data;
- data.hdr.u32Magic = CALLBACKDATAMAGIC_CLIENT_DISCONNECTED;
- data.hdr.u32ContextID = u32ContextID;
-
- AssertPtr(mpfnHostCallback);
- AssertPtr(mpvHostData);
+ uint32_t cParms = 0;
+ VBOXHGCMSVCPARM arParms[2];
+ arParms[cParms++].setUInt32(u32ContextID);
- return mpfnHostCallback(mpvHostData, GUEST_DISCONNECTED, (void *)(&data), sizeof(data));
+ return hostCallback(GUEST_DISCONNECTED, cParms, arParms);
}
/**
@@ -771,28 +1276,15 @@ int Service::cancelHostCmd(uint32_t u32ContextID)
*
* @return IPRT status code.
* @param u32ClientID The client's ID.
+ * @param rcPending Result code for completing pending operation.
*/
-int Service::cancelPendingWaits(uint32_t u32ClientID)
+int Service::cancelPendingWaits(uint32_t u32ClientID, int rcPending)
{
- int rc = VINF_SUCCESS;
- CallListIter it = mClientWaiterList.begin();
- while (it != mClientWaiterList.end())
- {
- if (it->mClientID == u32ClientID)
- {
- if (it->mNumParms >= 2)
- {
- it->mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
- it->mParms[1].setUInt32(0); /* Required parameters for message. */
- }
- if (mpHelpers)
- mpHelpers->pfnCallComplete(it->mHandle, rc);
- it = mClientWaiterList.erase(it);
- }
- else
- it++;
- }
- return rc;
+ ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
+ if (itClientState != mClientStateMap.end())
+ return itClientState->second.CancelWaiting(rcPending);
+
+ return VINF_SUCCESS;
}
/**
@@ -804,67 +1296,27 @@ int Service::cancelPendingWaits(uint32_t u32ClientID)
* @param cParms Number of parameters.
* @param paParms Array of parameters.
*/
-int Service::notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+int Service::hostCallback(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
{
LogFlowFunc(("eFunction=%ld, cParms=%ld, paParms=%p\n",
eFunction, cParms, paParms));
- int rc = VINF_SUCCESS;
- if ( eFunction == GUEST_EXEC_SEND_STATUS
- && cParms == 5)
- {
- CALLBACKDATAEXECSTATUS data;
- data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_STATUS;
- paParms[0].getUInt32(&data.hdr.u32ContextID);
-
- paParms[1].getUInt32(&data.u32PID);
- paParms[2].getUInt32(&data.u32Status);
- paParms[3].getUInt32(&data.u32Flags);
- paParms[4].getPointer(&data.pvData, &data.cbData);
-
- if (mpfnHostCallback)
- rc = mpfnHostCallback(mpvHostData, eFunction,
- (void *)(&data), sizeof(data));
- }
- else if ( eFunction == GUEST_EXEC_SEND_OUTPUT
- && cParms == 5)
- {
- CALLBACKDATAEXECOUT data;
- data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_OUT;
- paParms[0].getUInt32(&data.hdr.u32ContextID);
-
- paParms[1].getUInt32(&data.u32PID);
- paParms[2].getUInt32(&data.u32HandleId);
- paParms[3].getUInt32(&data.u32Flags);
- paParms[4].getPointer(&data.pvData, &data.cbData);
-
- if (mpfnHostCallback)
- rc = mpfnHostCallback(mpvHostData, eFunction,
- (void *)(&data), sizeof(data));
- }
- else if ( eFunction == GUEST_EXEC_SEND_INPUT_STATUS
- && cParms == 5)
+
+ int rc;
+ if (mpfnHostCallback)
{
- CALLBACKDATAEXECINSTATUS data;
- data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_IN_STATUS;
- paParms[0].getUInt32(&data.hdr.u32ContextID);
-
- paParms[1].getUInt32(&data.u32PID);
- paParms[2].getUInt32(&data.u32Status);
- paParms[3].getUInt32(&data.u32Flags);
- paParms[4].getUInt32(&data.cbProcessed);
-
- if (mpfnHostCallback)
- rc = mpfnHostCallback(mpvHostData, eFunction,
- (void *)(&data), sizeof(data));
+ VBOXGUESTCTRLHOSTCALLBACK data(cParms, paParms);
+ rc = mpfnHostCallback(mpvHostData, eFunction,
+ (void *)(&data), sizeof(data));
}
else
rc = VERR_NOT_SUPPORTED;
- LogFlowFunc(("returning %Rrc\n", rc));
+
+ LogFlowFunc(("Returning rc=%Rrc\n", rc));
return rc;
}
/**
- * Processes a command receiveed from the host side and re-routes it to
+ * Processes a command received from the host side and re-routes it to
* a connect client on the guest.
*
* @return IPRT status code.
@@ -872,7 +1324,7 @@ int Service::notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paP
* @param cParms Number of parameters.
* @param paParms Array of parameters.
*/
-int Service::processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
+int Service::hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
{
/*
* If no client is connected at all we don't buffer any host commands
@@ -880,68 +1332,65 @@ int Service::processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM
* waiting for a response from the guest side in case VBoxService on
* the guest is not running/system is messed up somehow.
*/
- if (mNumClients == 0)
+ if (mClientStateMap.size() == 0)
return VERR_NOT_FOUND;
- HostCmd newCmd;
- int rc = paramBufferAllocate(&newCmd.mParmBuf, eFunction, cParms, paParms);
- if ( RT_SUCCESS(rc)
- && cParms) /* Make sure we at least get one parameter (that is, the context ID). */
+
+ int rc;
+
+ HostCommand *pHostCmd = NULL;
+ try
{
- /*
- * Assume that the context ID *always* is the first parameter,
- * assign the context ID to the command.
- */
- newCmd.mParmBuf.pParms[0].getUInt32(&newCmd.mContextID);
+ pHostCmd = new HostCommand();
+ rc = pHostCmd->Allocate(eFunction, cParms, paParms);
+ if (RT_SUCCESS(rc))
+ /* rc = */ RTListAppend(&mHostCmdList, &pHostCmd->Node);
+ }
+ catch (std::bad_alloc)
+ {
+ rc = VERR_NO_MEMORY;
}
- else if (!cParms)
- rc = VERR_INVALID_PARAMETER;
if (RT_SUCCESS(rc))
{
- LogFlowFunc(("Handling host command CID = %u\n",
- newCmd.mContextID));
-
- bool fProcessed = false;
+ LogFlowFunc(("Handling host command CID=%RU32, eFunction=%RU32, cParms=%RU32, paParms=%p, numClients=%zu\n",
+ pHostCmd->mContextID, eFunction, cParms, paParms, mClientStateMap.size()));
- /* Can we wake up a waiting client on guest? */
- if (!mClientWaiterList.empty())
+ /*
+ * Wake up all pending clients which are interested in this
+ * host command.
+ */
+#ifdef DEBUG
+ uint32_t uClientsWokenUp = 0;
+#endif
+ ClientStateMapIter itClientState = mClientStateMap.begin();
+ AssertMsg(itClientState != mClientStateMap.end(), ("Client state map is empty when it should not\n"));
+ while (itClientState != mClientStateMap.end())
{
- ClientWaiter guest = mClientWaiterList.front();
- rc = assignHostCmdToGuest(&newCmd,
- guest.mHandle, guest.mNumParms, guest.mParms);
+ ClientState &clientState = itClientState->second;
- /* In any case the client did something, so wake up and remove from list. */
- AssertPtr(mpHelpers);
- mpHelpers->pfnCallComplete(guest.mHandle, rc);
- mClientWaiterList.pop_front();
-
- /*
- * If we got back an error (like VERR_TOO_MUCH_DATA or VERR_BUFFER_OVERFLOW)
- * we buffer the host command in the next block and return success to the host.
- */
- if (RT_FAILURE(rc))
+ /* If a client indicates that it it wants the new host command,
+ * add a reference to not delete it.*/
+ if (clientState.WantsHostCommand(pHostCmd))
{
- rc = VINF_SUCCESS;
+ clientState.EnqueueCommand(pHostCmd);
+
+ int rc2 = clientState.Wakeup();
+ if (RT_FAILURE(rc2))
+ LogFlowFunc(("Waking up client ID=%RU32 failed with rc=%Rrc\n",
+ itClientState->first, rc2));
+#ifdef DEBUG
+ uClientsWokenUp++;
+#endif
}
- else /* If command was understood by the client, free and remove from host commands list. */
- {
- LogFlowFunc(("Host command CID = %u processed with rc=%Rrc\n",
- newCmd.mContextID, rc));
- paramBufferFree(&newCmd.mParmBuf);
- }
+ itClientState++;
}
- if (!fProcessed)
- {
- LogFlowFunc(("Buffering host command CID = %u (rc=%Rrc)\n",
- newCmd.mContextID, rc));
-
- mHostCmds.push_back(newCmd);
- }
+#ifdef DEBUG
+ LogFlowFunc(("%RU32 clients have been woken up\n", uClientsWokenUp));
+#endif
}
- LogFlowFunc(("Returned with rc=%Rrc\n", rc));
return rc;
}
@@ -959,51 +1408,91 @@ void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
VBOXHGCMSVCPARM paParms[])
{
int rc = VINF_SUCCESS;
- LogFlowFunc(("u32ClientID = %u, fn = %u, cParms = %u, paParms = 0x%p\n",
+ LogFlowFunc(("[Client %RU32] eFunction=%RU32, cParms=%RU32, paParms=0x%p\n",
u32ClientID, eFunction, cParms, paParms));
try
{
- switch (eFunction)
+ /*
+ * The guest asks the host for the next message to process.
+ */
+ if (eFunction == GUEST_MSG_WAIT)
{
- /*
- * The guest asks the host for the next message to process.
- */
- case GUEST_GET_HOST_MSG:
- LogFlowFunc(("GUEST_GET_HOST_MSG\n"));
- rc = retrieveNextHostCmd(u32ClientID, callHandle, cParms, paParms);
- break;
-
- /*
- * The guest wants to shut down and asks us (this service) to cancel
- * all blocking pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
- * guest can gracefully shut down.
- */
- case GUEST_CANCEL_PENDING_WAITS:
- LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
- rc = cancelPendingWaits(u32ClientID);
- break;
-
- /*
- * For all other regular commands we call our notifyHost
- * function. If the current command does not support notifications
- * notifyHost will return VERR_NOT_SUPPORTED.
- */
- default:
- rc = notifyHost(eFunction, cParms, paParms);
- break;
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_GET\n", u32ClientID));
+ rc = clientGetCommand(u32ClientID, callHandle, cParms, paParms);
}
- if (rc != VINF_HGCM_ASYNC_EXECUTE)
+ else
{
- /* Tell the client that the call is complete (unblocks waiting). */
- AssertPtr(mpHelpers);
- mpHelpers->pfnCallComplete(callHandle, rc);
+ switch (eFunction)
+ {
+ /*
+ * A client wants to shut down and asks us (this service) to cancel
+ * all blocking/pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
+ * client can gracefully shut down.
+ */
+ case GUEST_CANCEL_PENDING_WAITS:
+ LogFlowFunc(("[Client %RU32] GUEST_CANCEL_PENDING_WAITS\n", u32ClientID));
+ rc = cancelPendingWaits(u32ClientID, VINF_SUCCESS /* Pending result */);
+ break;
+
+ /*
+ * The guest only wants certain messages set by the filter mask(s).
+ * Since VBox 4.3+.
+ */
+ case GUEST_MSG_FILTER_SET:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_SET\n", u32ClientID));
+ rc = clientSetMsgFilterSet(u32ClientID, callHandle, cParms, paParms);
+ break;
+
+ /*
+ * Unsetting the message filter flag.
+ */
+ case GUEST_MSG_FILTER_UNSET:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_FILTER_UNSET\n", u32ClientID));
+ rc = clientSetMsgFilterUnset(u32ClientID, callHandle, cParms, paParms);
+ break;
+
+ /*
+ * The guest only wants skip the currently assigned messages. Neded
+ * for dropping its assigned reference of the current assigned host
+ * command in queue.
+ * Since VBox 4.3+.
+ */
+ case GUEST_MSG_SKIP:
+ LogFlowFunc(("[Client %RU32] GUEST_MSG_SKIP\n", u32ClientID));
+ rc = clientSkipMsg(u32ClientID, callHandle, cParms, paParms);
+ break;
+
+ /*
+ * The guest wants to close specific guest session. This is handy for
+ * shutting down dedicated guest session processes from another process.
+ */
+ case GUEST_SESSION_CLOSE:
+ LogFlowFunc(("[Client %RU32] GUEST_SESSION_CLOSE\n", u32ClientID));
+ rc = sessionClose(u32ClientID, callHandle, cParms, paParms);
+ break;
+
+ /*
+ * For all other regular commands we call our hostCallback
+ * function. If the current command does not support notifications,
+ * notifyHost will return VERR_NOT_SUPPORTED.
+ */
+ default:
+ rc = hostCallback(eFunction, cParms, paParms);
+ break;
+ }
+
+ if (rc != VINF_HGCM_ASYNC_EXECUTE)
+ {
+ /* Tell the client that the call is complete (unblocks waiting). */
+ AssertPtr(mpHelpers);
+ mpHelpers->pfnCallComplete(callHandle, rc);
+ }
}
}
catch (std::bad_alloc)
{
rc = VERR_NO_MEMORY;
}
- LogFlowFunc(("rc = %Rrc\n", rc));
}
/**
@@ -1014,25 +1503,75 @@ void Service::call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
int Service::hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
{
int rc = VERR_NOT_SUPPORTED;
- LogFlowFunc(("fn = %u, cParms = %u, paParms = 0x%p\n",
+ LogFlowFunc(("fn=%RU32, cParms=%RU32, paParms=0x%p\n",
eFunction, cParms, paParms));
try
{
- rc = processHostCmd(eFunction, cParms, paParms);
+ switch (eFunction)
+ {
+ /**
+ * Host
+ */
+ case HOST_CANCEL_PENDING_WAITS:
+ {
+ LogFlowFunc(("HOST_CANCEL_PENDING_WAITS\n"));
+ ClientStateMapIter itClientState = mClientStateMap.begin();
+ while (itClientState != mClientStateMap.end())
+ {
+ int rc2 = itClientState->second.CancelWaiting(VINF_SUCCESS /* Pending rc. */);
+ if (RT_FAILURE(rc2))
+ LogFlowFunc(("Cancelling waiting for client ID=%RU32 failed with rc=%Rrc",
+ itClientState->first, rc2));
+ itClientState++;
+ }
+ rc = VINF_SUCCESS;
+ break;
+ }
+
+ default:
+ rc = hostProcessCommand(eFunction, cParms, paParms);
+ break;
+ }
}
catch (std::bad_alloc)
{
rc = VERR_NO_MEMORY;
}
- LogFlowFunc(("rc = %Rrc\n", rc));
return rc;
}
-int Service::uninit()
+/**
+ * Client asks another client (guest) session to close.
+ *
+ * @return IPRT status code.
+ * @param u32ClientID The client's ID.
+ * @param callHandle The client's call handle.
+ * @param cParms Number of parameters.
+ * @param paParms Array of parameters.
+ */
+int Service::sessionClose(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
{
- Assert(mHostCmds.empty());
+ if (cParms < 2)
+ return VERR_INVALID_PARAMETER;
+ uint32_t uContextID, uFlags;
+ int rc = paParms[0].getUInt32(&uContextID);
+ if (RT_SUCCESS(rc))
+ rc = paParms[1].getUInt32(&uFlags);
+
+ uint32_t uSessionID = VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(uContextID);
+
+ if (RT_SUCCESS(rc))
+ rc = hostProcessCommand(HOST_SESSION_CLOSE, cParms, paParms);
+
+ LogFlowFunc(("Closing guest session ID=%RU32 (from client ID=%RU32) returned with rc=%Rrc\n",
+ uSessionID, u32ClientID, rc));
+ return rc;
+}
+
+int Service::uninit()
+{
return VINF_SUCCESS;
}
@@ -1043,22 +1582,22 @@ using guestControl::Service;
/**
* @copydoc VBOXHGCMSVCLOAD
*/
-extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *ptable)
+extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable)
{
int rc = VINF_SUCCESS;
- LogFlowFunc(("ptable = %p\n", ptable));
+ LogFlowFunc(("pTable=%p\n", pTable));
- if (!VALID_PTR(ptable))
+ if (!VALID_PTR(pTable))
{
rc = VERR_INVALID_PARAMETER;
}
else
{
- LogFlowFunc(("ptable->cbSize = %d, ptable->u32Version = 0x%08X\n", ptable->cbSize, ptable->u32Version));
+ LogFlowFunc(("pTable->cbSize=%d, pTable->u32Version=0x%08X\n", pTable->cbSize, pTable->u32Version));
- if ( ptable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
- || ptable->u32Version != VBOX_HGCM_SVC_VERSION)
+ if ( pTable->cbSize != sizeof (VBOXHGCMSVCFNTABLE)
+ || pTable->u32Version != VBOX_HGCM_SVC_VERSION)
{
rc = VERR_VERSION_MISMATCH;
}
@@ -1067,7 +1606,7 @@ extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pta
std::auto_ptr<Service> apService;
/* No exceptions may propagate outside. */
try {
- apService = std::auto_ptr<Service>(new Service(ptable->pHelpers));
+ apService = std::auto_ptr<Service>(new Service(pTable->pHelpers));
} catch (int rcThrown) {
rc = rcThrown;
} catch (...) {
@@ -1080,25 +1619,25 @@ extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pta
* We don't need an additional client data area on the host,
* because we're a class which can have members for that :-).
*/
- ptable->cbClient = 0;
+ pTable->cbClient = 0;
/* Register functions. */
- ptable->pfnUnload = Service::svcUnload;
- ptable->pfnConnect = Service::svcConnect;
- ptable->pfnDisconnect = Service::svcDisconnect;
- ptable->pfnCall = Service::svcCall;
- ptable->pfnHostCall = Service::svcHostCall;
- ptable->pfnSaveState = NULL; /* The service is stateless, so the normal */
- ptable->pfnLoadState = NULL; /* construction done before restoring suffices */
- ptable->pfnRegisterExtension = Service::svcRegisterExtension;
+ pTable->pfnUnload = Service::svcUnload;
+ pTable->pfnConnect = Service::svcConnect;
+ pTable->pfnDisconnect = Service::svcDisconnect;
+ pTable->pfnCall = Service::svcCall;
+ pTable->pfnHostCall = Service::svcHostCall;
+ pTable->pfnSaveState = NULL; /* The service is stateless, so the normal */
+ pTable->pfnLoadState = NULL; /* construction done before restoring suffices */
+ pTable->pfnRegisterExtension = Service::svcRegisterExtension;
/* Service specific initialization. */
- ptable->pvService = apService.release();
+ pTable->pvService = apService.release();
}
}
}
- LogFlowFunc(("returning %Rrc\n", rc));
+ LogFlowFunc(("Returning %Rrc\n", rc));
return rc;
}