diff options
Diffstat (limited to 'src/VBox/HostServices/GuestControl/service.cpp')
| -rw-r--r-- | src/VBox/HostServices/GuestControl/service.cpp | 1821 |
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; } |
