VirtualBox

Changeset 44863 in vbox for trunk/src/VBox/HostServices


Ignore:
Timestamp:
Feb 28, 2013 12:18:17 PM (12 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
84015
Message:

GuestCtrl: Infrastructure changes for handling and executing dedicated guest sessions and protocol versioning (untested, work in progress).

Location:
trunk/src/VBox
Files:
3 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox

    • Property svn:mergeinfo set to (toggle deleted branches)
      /branches/VBox-3.0/src/VBox58652,​70973
      /branches/VBox-3.2/src/VBox66309,​66318
      /branches/VBox-4.0/src/VBox70873
      /branches/VBox-4.1/src/VBox74233,​78414,​78691,​81841,​82127
      /branches/andy/guestctrl20/src/VBox78916,​78930
      /branches/dsen/gui/src/VBox79076-79078,​79089,​79109-79110,​79112-79113,​79127-79130,​79134,​79141,​79151,​79155,​79157-79159,​79193,​79197
      /branches/dsen/gui2/src/VBox79224,​79228,​79233,​79235,​79258,​79262-79263,​79273,​79341,​79345,​79354,​79357,​79387-79388,​79559-79569,​79572-79573,​79578,​79581-79582,​79590-79591,​79598-79599,​79602-79603,​79605-79606,​79632,​79635,​79637,​79644
      /branches/dsen/gui3/src/VBox79645-79692
  • trunk/src/VBox/HostServices/GuestControl/service.cpp

    r44528 r44863  
    5050 * host calls in order the client terminated/crashed (HGCM detects disconnected
    5151 * clients and reports it to this service's callback).
     52 *
     53 * Starting at VBox 4.2 the context ID itself consists of a session ID, an object
     54 * ID (for example a process or file ID) and a count. This is necessary to not break
     55 * compatibility between older hosts and to manage guest session on the host.
    5256 */
    5357
     
    5963
    6064#include <VBox/log.h>
    61 #include <iprt/asm.h> /* For ASMBreakpoint(). */
    6265#include <iprt/assert.h>
    6366#include <iprt/cpp/autores.h>
     
    6568#include <iprt/err.h>
    6669#include <iprt/mem.h>
     70#include <iprt/list.h>
    6771#include <iprt/req.h>
    6872#include <iprt/string.h>
     
    7074#include <iprt/time.h>
    7175
     76#include <map>
    7277#include <memory>  /* for auto_ptr */
    7378#include <string>
     
    7883namespace guestControl {
    7984
     85/** Flag for indicating that the client only is interested in
     86 *  messages for specific contexts. */
     87#define CLIENTSTATE_FLAG_CONTEXTFILTER      RT_BIT(0)
     88
     89/**
     90 * Structure for maintaining a pending (that is, a deferred and not yet completed)
     91 * client command.
     92 */
     93typedef struct ClientConnection
     94{
     95    /** The call handle */
     96    VBOXHGCMCALLHANDLE mHandle;
     97    /** Number of parameters */
     98    uint32_t mNumParms;
     99    /** The call parameters */
     100    VBOXHGCMSVCPARM *mParms;
     101    /** The standard constructor. */
     102    ClientConnection(void) : mHandle(0), mNumParms(0), mParms(NULL) {}
     103} ClientConnection;
     104
     105/**
     106 * Structure for holding a buffered host command which has
     107 * not been processed yet.
     108 */
     109typedef struct HostCommand
     110{
     111    RTLISTNODE Node;
     112
     113    uint32_t AddRef(void)
     114    {
     115        return ++mRefCount;
     116    }
     117
     118    uint32_t Release(void)
     119    {
     120        LogFlowFunc(("Releasing CID=%RU32, refCount=%RU32\n",
     121                     mContextID, mRefCount));
     122
     123        /* Release reference for current command. */
     124        Assert(mRefCount);
     125        if (--mRefCount == 0)
     126            Free();
     127
     128        return mRefCount;
     129    }
     130
     131    /**
     132     * Allocates the command with an HGCM request. Needs to be free'd using Free().
     133     *
     134     * @return  IPRT status code.
     135     * @param   uMsg                    Message type.
     136     * @param   cParms                  Number of parameters of HGCM request.
     137     * @param   paParms                 Array of parameters of HGCM request.
     138     */
     139    int Allocate(uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
     140    {
     141        LogFlowFunc(("Allocating uMsg=%RU32, cParms=%RU32, paParms=%p\n",
     142                     uMsg, cParms, paParms));
     143
     144        if (!cParms) /* At least one parameter (context ID) must be present. */
     145            return VERR_INVALID_PARAMETER;
     146
     147        AssertPtrReturn(paParms, VERR_INVALID_POINTER);
     148
     149        /* Paranoia. */
     150        if (cParms > 256)
     151            cParms = 256;
     152
     153        int rc = VINF_SUCCESS;
     154
     155        /*
     156         * Don't verify anything here (yet), because this function only buffers
     157         * the HGCM data into an internal structure and reaches it back to the guest (client)
     158         * in an unmodified state.
     159         */
     160        mMsgType = uMsg;
     161        mParmCount = cParms;
     162        if (mParmCount)
     163        {
     164            mpParms = (VBOXHGCMSVCPARM*)RTMemAllocZ(sizeof(VBOXHGCMSVCPARM) * mParmCount);
     165            if (NULL == mpParms)
     166                rc = VERR_NO_MEMORY;
     167        }
     168
     169        if (RT_SUCCESS(rc))
     170        {
     171            for (uint32_t i = 0; i < mParmCount; i++)
     172            {
     173                mpParms[i].type = paParms[i].type;
     174                switch (paParms[i].type)
     175                {
     176                    case VBOX_HGCM_SVC_PARM_32BIT:
     177                        mpParms[i].u.uint32 = paParms[i].u.uint32;
     178                        break;
     179
     180                    case VBOX_HGCM_SVC_PARM_64BIT:
     181                        /* Not supported yet. */
     182                        break;
     183
     184                    case VBOX_HGCM_SVC_PARM_PTR:
     185                        mpParms[i].u.pointer.size = paParms[i].u.pointer.size;
     186                        if (mpParms[i].u.pointer.size > 0)
     187                        {
     188                            mpParms[i].u.pointer.addr = RTMemAlloc(mpParms[i].u.pointer.size);
     189                            if (NULL == mpParms[i].u.pointer.addr)
     190                            {
     191                                rc = VERR_NO_MEMORY;
     192                                break;
     193                            }
     194                            else
     195                                memcpy(mpParms[i].u.pointer.addr,
     196                                       paParms[i].u.pointer.addr,
     197                                       mpParms[i].u.pointer.size);
     198                        }
     199                        else
     200                        {
     201                            /* Size is 0 -- make sure we don't have any pointer. */
     202                            mpParms[i].u.pointer.addr = NULL;
     203                        }
     204                        break;
     205
     206                    default:
     207                        break;
     208                }
     209                if (RT_FAILURE(rc))
     210                    break;
     211            }
     212        }
     213
     214        if (RT_SUCCESS(rc))
     215        {
     216            /*
     217             * Assume that the context ID *always* is the first parameter,
     218             * assign the context ID to the command.
     219             */
     220            rc = mpParms[0].getUInt32(&mContextID);
     221        }
     222
     223        LogFlowFunc(("Returned with rc=%Rrc\n", rc));
     224        return rc;
     225    }
     226
     227    /**
     228     * Frees the buffered HGCM request.
     229     *
     230     * @return  IPRT status code.
     231     */
     232    void Free(void)
     233    {
     234        AssertMsg(mRefCount == 0, ("Command still being used by a client (%RU32 refs), cannot free yet\n",
     235                                   mRefCount));
     236
     237        LogFlowFunc(("Freeing host command CID=%RU32, mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
     238                     mContextID, mMsgType, mParmCount, mpParms));
     239
     240        for (uint32_t i = 0; i < mParmCount; i++)
     241        {
     242            switch (mpParms[i].type)
     243            {
     244                case VBOX_HGCM_SVC_PARM_PTR:
     245                    if (mpParms[i].u.pointer.size > 0)
     246                        RTMemFree(mpParms[i].u.pointer.addr);
     247                    break;
     248            }
     249        }
     250
     251        if (mpParms)
     252            RTMemFree(mpParms);
     253
     254        mParmCount = 0;
     255    }
     256
     257    /**
     258     * Copies data from the buffered HGCM request to the current HGCM request.
     259     *
     260     * @return  IPRT status code.
     261     * @param   paDstParms              Array of parameters of HGCM request to fill the data into.
     262     * @param   cPDstarms               Number of parameters the HGCM request can handle.
     263     * @param   pSrcBuf                 Parameter buffer to assign.
     264     */
     265    int CopyTo(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms) const
     266    {
     267        int rc = VINF_SUCCESS;
     268        if (cDstParms != mParmCount)
     269        {
     270            LogFlowFunc(("Parameter count does not match (got %RU32, expected %RU32)\n",
     271                         cDstParms, mParmCount));
     272            rc = VERR_INVALID_PARAMETER;
     273        }
     274        else
     275        {
     276            for (uint32_t i = 0; i < mParmCount; i++)
     277            {
     278                if (paDstParms[i].type != mpParms[i].type)
     279                {
     280                    LogFlowFunc(("Parameter %RU32 type mismatch (got %RU32, expected %RU32)\n",
     281                                 i, paDstParms[i].type, mpParms[i].type));
     282                    rc = VERR_INVALID_PARAMETER;
     283                }
     284                else
     285                {
     286                    switch (mpParms[i].type)
     287                    {
     288                        case VBOX_HGCM_SVC_PARM_32BIT:
     289                            paDstParms[i].u.uint32 = mpParms[i].u.uint32;
     290                            break;
     291
     292                        case VBOX_HGCM_SVC_PARM_PTR:
     293                        {
     294                            if (!mpParms[i].u.pointer.size)
     295                                continue; /* Only copy buffer if there actually is something to copy. */
     296
     297                            if (!paDstParms[i].u.pointer.addr)
     298                                rc = VERR_INVALID_PARAMETER;
     299
     300                            if (paDstParms[i].u.pointer.size < mpParms[i].u.pointer.size)
     301                                rc = VERR_BUFFER_OVERFLOW;
     302
     303                            if (RT_SUCCESS(rc))
     304                            {
     305                                memcpy(paDstParms[i].u.pointer.addr,
     306                                       mpParms[i].u.pointer.addr,
     307                                       mpParms[i].u.pointer.size);
     308                            }
     309
     310                            break;
     311                        }
     312
     313                        case VBOX_HGCM_SVC_PARM_64BIT:
     314                            /* Fall through is intentional. */
     315                        default:
     316                            LogFlowFunc(("Parameter %RU32 of type %RU32 is not supported yet\n",
     317                                         i, mpParms[i].type));
     318                            rc = VERR_NOT_SUPPORTED;
     319                            break;
     320                    }
     321                }
     322
     323                if (RT_FAILURE(rc))
     324                {
     325                    LogFlowFunc(("Parameter %RU32 invalid (rc=%Rrc), refusing\n",
     326                                 i, rc));
     327                    break;
     328                }
     329            }
     330        }
     331
     332        return rc;
     333    }
     334
     335    int AssignToConnection(const ClientConnection *pConnection)
     336    {
     337        int rc;
     338
     339        LogFlowFunc(("mMsgType=%RU32, mParmCount=%RU32, mpParms=%p\n",
     340                     mMsgType, mParmCount, mpParms));
     341
     342        /* Does the current host command need more parameter space which
     343         * the client does not provide yet? */
     344        if (mParmCount > pConnection->mNumParms)
     345        {
     346            pConnection->mParms[0].setUInt32(mMsgType);   /* Message ID */
     347            pConnection->mParms[1].setUInt32(mParmCount); /* Required parameters for message */
     348
     349            /*
     350            * So this call apparently failed because the guest wanted to peek
     351            * how much parameters it has to supply in order to successfully retrieve
     352            * this command. Let's tell him so!
     353            */
     354            rc = VERR_TOO_MUCH_DATA;
     355        }
     356        else
     357        {
     358            rc = CopyTo(pConnection->mParms, pConnection->mNumParms);
     359
     360            /* Has there been enough parameter space but the wrong parameter types
     361             * were submitted -- maybe the client was just asking for the next upcoming
     362             * host message?
     363             *
     364             * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
     365             *       in every case. */
     366            if (RT_FAILURE(rc))
     367                rc = VERR_TOO_MUCH_DATA;
     368        }
     369
     370        LogFlowFunc(("Returned with rc=%Rrc\n", rc));
     371        return rc;
     372    }
     373
     374    /** Reference count for keeping track how many connected
     375     *  clients still need to process this command until it can
     376     *  be removed. */
     377    uint32_t mRefCount;
     378    /** The context ID this command belongs to. Will be extracted
     379     *  *always* from HGCM parameter [0]. */
     380    uint32_t mContextID;
     381    /** Dynamic structure for holding the HGCM parms */
     382    uint32_t mMsgType;
     383    uint32_t mParmCount;
     384    PVBOXHGCMSVCPARM mpParms;
     385} HostCommand;
     386
     387/**
     388 * Per-client structure used for book keeping/state tracking a
     389 * certain host command.
     390 */
     391typedef struct ClientContext
     392{
     393    /* Pointer to list node of this command. */
     394    HostCommand *mpHostCmd;
     395    /** The standard constructor. */
     396    ClientContext(void) : mpHostCmd(NULL) {}
     397    /** Internal constrcutor. */
     398    ClientContext(HostCommand *pHostCmd) : mpHostCmd(pHostCmd) {}
     399} ClientContext;
     400typedef std::map< uint32_t, ClientContext > ClientContextMap;
     401typedef std::map< uint32_t, ClientContext >::iterator ClientContextMapIter;
     402typedef std::map< uint32_t, ClientContext >::const_iterator ClientContextMapIterConst;
     403
    80404/**
    81405 * Structure for holding all clients with their
    82406 * generated host contexts. This is necessary for
    83  * maintaining the relationship between a client and its context IDs.
    84  */
    85 struct ClientContexts
    86 {
    87     /** This client ID. */
    88     uint32_t mClientID;
    89     /** The list of contexts a client is assigned to. */
    90     std::list< uint32_t > mContextList;
    91 
    92     /** The normal constructor. */
    93     ClientContexts(uint32_t aClientID)
    94                    : mClientID(aClientID) {}
    95 };
    96 /** The client list + iterator type */
    97 typedef std::list< ClientContexts > ClientContextsList;
    98 typedef std::list< ClientContexts >::iterator ClientContextsListIter;
    99 typedef std::list< ClientContexts >::const_iterator ClientContextsListIterConst;
    100 
    101 /**
    102  * Structure for holding an uncompleted guest call.
    103  */
    104 struct ClientWaiter
    105 {
    106     /** Client ID; a client can have multiple handles! */
    107     uint32_t mClientID;
    108     /** The call handle */
    109     VBOXHGCMCALLHANDLE mHandle;
    110     /** The call parameters */
    111     VBOXHGCMSVCPARM *mParms;
    112     /** Number of parameters */
    113     uint32_t mNumParms;
    114 
    115     /** The standard constructor. */
    116     ClientWaiter() : mClientID(0), mHandle(0), mParms(NULL), mNumParms(0) {}
    117     /** The normal constructor. */
    118     ClientWaiter(uint32_t aClientID, VBOXHGCMCALLHANDLE aHandle,
    119               VBOXHGCMSVCPARM aParms[], uint32_t cParms)
    120               : mClientID(aClientID), mHandle(aHandle), mParms(aParms), mNumParms(cParms) {}
    121 };
    122 /** The guest call list type */
    123 typedef std::list< ClientWaiter > ClientWaiterList;
    124 typedef std::list< ClientWaiter >::iterator CallListIter;
    125 typedef std::list< ClientWaiter >::const_iterator CallListIterConst;
    126 
    127 /**
    128  * Structure for holding a buffered host command.
    129  */
    130 typedef struct VBOXGUESTCTRLHOSTCMD
    131 {
    132     /** The context ID this command belongs to. Will be extracted
    133       * from the HGCM parameters. */
    134     uint32_t mContextID;
     407 * maintaining the relationship between a client and its context ID(s).
     408 */
     409typedef struct ClientState
     410{
     411    ClientState(PVBOXHGCMSVCHELPERS pSvcHelpers)
     412        : mSvcHelpers(pSvcHelpers), mpHostCmd(NULL),
     413          mFlags(0), mContextFilter(0), mIsPending(false),
     414          mHostCmdTries(0), mHostCmdRc(VINF_SUCCESS) {}
     415
     416    ClientState(void)
     417        : mSvcHelpers(NULL), mpHostCmd(NULL),
     418          mFlags(0), mContextFilter(0), mIsPending(false),
     419          mHostCmdTries(0), mHostCmdRc(VINF_SUCCESS) {}
     420
     421    bool WantsHostCommand(const HostCommand *pHostCmd) const
     422    {
     423        AssertPtrReturn(pHostCmd, false);
     424
     425        /*
     426         * If a sesseion filter is set, only obey those sessions we're interested in.
     427         */
     428        if (mFlags & CLIENTSTATE_FLAG_CONTEXTFILTER)
     429        {
     430            if (VBOX_GUESTCTRL_CONTEXTID_GET_SESSION(pHostCmd->mContextID) == mContextFilter)
     431                return true;
     432        }
     433        else /* Client is interested in all commands. */
     434            return true;
     435
     436        return false;
     437    }
     438
     439    int SetPending(const ClientConnection *pConnection)
     440    {
     441        AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
     442
     443        if (mpHostCmd == NULL)
     444        {
     445            AssertMsg(mIsPending == false,
     446                      ("Client %p already is pending but tried to receive a new host command\n", this));
     447
     448            mPending.mHandle   = pConnection->mHandle;
     449            mPending.mNumParms = pConnection->mNumParms;
     450            mPending.mParms    = pConnection->mParms;
     451
     452            mIsPending = true;
     453
     454            LogFlowFunc(("Client now is in pending mode\n"));
     455
     456            /*
     457             * Signal that we don't and can't return yet.
     458             */
     459            return VINF_HGCM_ASYNC_EXECUTE;
     460        }
     461
     462        /*
     463         * Signal that there already is a connection pending.
     464         * Shouldn't happen in daily usage.
     465         */
     466        AssertMsgFailed(("Client already has a connection pending\n"));
     467        return VERR_SIGNAL_PENDING;
     468    }
     469
     470    int Run(const ClientConnection *pConnection,
     471            const RTLISTANCHOR     *pHostCmdList)
     472    {
     473        int rc = VINF_SUCCESS;
     474
     475        LogFlowFunc(("Client pConnection=%p, pHostCmdList=%p\n",
     476                      pConnection, pHostCmdList));
     477        LogFlowFunc(("Client hostCmd=%p, mHostCmdRc=%Rrc, mHostCmdTries=%RU32\n",
     478                      mpHostCmd, mHostCmdRc, mHostCmdTries));
     479
     480        /* Do we have a current command? */
     481        if (mpHostCmd)
     482        {
     483            bool fRemove = false;
     484            if (RT_FAILURE(mHostCmdRc))
     485            {
     486                mHostCmdTries++;
     487
     488                /*
     489                 * If the client understood the message but supplied too little buffer space
     490                 * don't send this message again and drop it after 3 unsuccessful attempts.
     491                 * The host then should take care of next actions (maybe retry it with a smaller buffer).
     492                 */
     493                if (   mHostCmdRc    == VERR_TOO_MUCH_DATA
     494                    && mHostCmdTries >= 3)
     495                {
     496                    fRemove = true;
     497                }
     498                /* Client did not understand the message or something else weird happened. Try again one
     499                 * more time and drop it if it didn't get handled then. */
     500                else if (mHostCmdTries > 1)
     501                    fRemove = true;
     502            }
     503            else
     504                fRemove = true; /* Everything went fine, remove it. */
     505
     506            LogFlowFunc(("Client tried CID=%RU32 for %RU32 times, (last result=%Rrc, fRemove=%RTbool)\n",
     507                         mpHostCmd->mContextID, mHostCmdTries, mHostCmdRc, fRemove));
     508
     509            if (fRemove)
     510            {
     511                LogFlowFunc(("Client removes itself from command CID=%RU32\n",
     512                              mpHostCmd->mContextID));
     513
     514                /* Remove command from context map. */
     515                mContextMap.erase(mpHostCmd->mContextID);
     516
     517                /* Release reference for current command. */
     518                if (mpHostCmd->Release() == 0)
     519                {
     520                    LogFlowFunc(("Destroying and removing command CID=%RU32 from list\n",
     521                                 mpHostCmd->mContextID));
     522
     523                    /* Last reference removes the command from the list. */
     524                    RTListNodeRemove(&mpHostCmd->Node);
     525
     526                    RTMemFree(mpHostCmd);
     527                    mpHostCmd = NULL;
     528                }
     529
     530                /* Reset everything else. */
     531                mHostCmdRc    = VINF_SUCCESS;
     532                mHostCmdTries = 0;
     533            }
     534        }
     535
     536        /* ... or don't we have one (anymore?) Try getting a new one to process now. */
     537        if (mpHostCmd == NULL)
     538        {
     539            /* Get the next host command the clienet is interested in. */
     540            bool fFoundCmd = false;
     541            HostCommand *pCurCmd;
     542            RTListForEach(pHostCmdList, pCurCmd, HostCommand, Node)
     543            {
     544                fFoundCmd = WantsHostCommand(pCurCmd);
     545                if (fFoundCmd)
     546                {
     547                    mpHostCmd = pCurCmd;
     548                    AssertPtr(mpHostCmd);
     549                    mpHostCmd->AddRef();
     550
     551                    /* Create a command context to keep track of client-specific
     552                     * information about a certain command. */
     553                    Assert(mContextMap.find(mpHostCmd->mContextID) == mContextMap.end());
     554                    mContextMap[mpHostCmd->mContextID] = ClientContext(mpHostCmd);
     555                    /** @todo Exception handling! */
     556
     557                    LogFlowFunc(("Assigning next host comamnd CID=%RU32, cmdType=%RU32, cmdParms=%RU32, new refCount=%RU32\n",
     558                                 mpHostCmd->mContextID, mpHostCmd->mMsgType, mpHostCmd->mParmCount, mpHostCmd->mRefCount));
     559                    break;
     560                }
     561            }
     562
     563            LogFlowFunc(("Client %s new command\n",
     564                         fFoundCmd ? "found" : "did not find a"));
     565
     566            /* If no new command was found, set client into pending state. */
     567            if (!fFoundCmd)
     568                rc = SetPending(pConnection);
     569        }
     570
     571        if (mpHostCmd)
     572        {
     573            AssertPtr(mpHostCmd);
     574            mHostCmdRc = SendReply(pConnection, mpHostCmd);
     575            if (RT_FAILURE(mHostCmdRc))
     576            {
     577                LogFlowFunc(("Processing command CID=%RU32 ended with rc=%Rrc\n",
     578                             mpHostCmd->mContextID, mHostCmdRc));
     579            }
     580
     581            if (RT_SUCCESS(rc))
     582                rc = mHostCmdRc;
     583        }
     584
     585        LogFlowFunc(("Returned with rc=%Rrc\n", rc));
     586        return rc;
     587    }
     588
     589    int RunNow(const ClientConnection *pConnection,
     590               const PRTLISTANCHOR     pHostCmdList)
     591    {
     592        AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
     593        AssertPtrReturn(pHostCmdList, VERR_INVALID_POINTER);
     594
     595        AssertMsgReturn(!mIsPending, ("Can't use another connection when client still is in pending mode\n"),
     596                        VERR_INVALID_PARAMETER);
     597
     598        int rc = Run(pConnection, pHostCmdList);
     599
     600        LogFlowFunc(("Returned with rc=%Rrc\n"));
     601        return rc;
     602    }
     603
     604    int Wakeup(const PRTLISTANCHOR pHostCmdList)
     605    {
     606        AssertMsgReturn(mIsPending, ("Cannot wake up a client which is not in pending mode\n"),
     607                        VERR_INVALID_PARAMETER);
     608
     609        int rc = Run(&mPending, pHostCmdList);
     610
     611        /* Reset pending state. */
     612        mIsPending = false;
     613
     614        LogFlowFunc(("Returned with rc=%Rrc\n"));
     615        return rc;
     616    }
     617
     618    int CancelWaiting(int rcPending)
     619    {
     620        LogFlowFunc(("Cancelling waiting with %Rrc, isPending=%RTbool, pendingNumParms=%RU32, flags=%x\n",
     621                     rcPending, mIsPending, mPending.mNumParms, mFlags));
     622
     623        if (   mIsPending
     624            && mPending.mNumParms >= 2)
     625        {
     626            mPending.mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
     627            mPending.mParms[1].setUInt32(0);                         /* Required parameters for message. */
     628
     629            AssertPtr(mSvcHelpers);
     630            mSvcHelpers->pfnCallComplete(mPending.mHandle, rcPending);
     631
     632            mIsPending = false;
     633        }
     634
     635        return VINF_SUCCESS;
     636    }
     637
     638    int SendReply(const ClientConnection *pConnection,
     639                        HostCommand      *pHostCmd)
     640    {
     641        AssertPtrReturn(pConnection, VERR_INVALID_POINTER);
     642        AssertPtrReturn(pHostCmd, VERR_INVALID_POINTER);
     643
     644        /* Try assigning the host command to the client and store the
     645         * result code for later use. */
     646        int rc = pHostCmd->AssignToConnection(pConnection);
     647
     648        /* In any case the client did something, so wake up and remove from list. */
     649        AssertPtr(mSvcHelpers);
     650        mSvcHelpers->pfnCallComplete(pConnection->mHandle, rc);
     651
     652        LogFlowFunc(("pConnection=%p, pHostCmd=%p, rc=%Rrc\n",
     653                     pConnection, pHostCmd, rc));
     654        return rc;
     655    }
     656
     657    PVBOXHGCMSVCHELPERS mSvcHelpers;
     658    /** Client flags. @sa CLIENTSTATE_FLAG_ flags. */
     659    uint32_t mFlags;
     660    /** The context ID filter, based on the flags set. */
     661    uint32_t mContextFilter;
     662    /** Map containing all context IDs a client is assigned to. */
     663    //std::map< uint32_t, ClientContext > mContextMap;
     664    /** Pointer to current host command to process. */
     665    HostCommand *mpHostCmd;
     666    /** Last (most recent) rc after handling the
     667     *  host command. */
     668    int mHostCmdRc;
    135669    /** How many times the host service has tried to deliver this
    136      *  command to the guest. */
    137     uint32_t mTries;
    138     /** Dynamic structure for holding the HGCM parms */
    139     VBOXGUESTCTRPARAMBUFFER mParmBuf;
    140 
    141     /** The standard constructor. */
    142     VBOXGUESTCTRLHOSTCMD() : mContextID(0), mTries(0) {}
    143 };
    144 /** The host cmd list + iterator type */
    145 typedef std::list< VBOXGUESTCTRLHOSTCMD > HostCmdList;
    146 typedef std::list< VBOXGUESTCTRLHOSTCMD >::iterator HostCmdListIter;
    147 typedef std::list< VBOXGUESTCTRLHOSTCMD >::const_iterator HostCmdListIterConst;
     670     *  command to the according client. */
     671    uint32_t mHostCmdTries;
     672    /** Map containing all context IDs a client is assigned to. */
     673    ClientContextMap mContextMap;
     674    /** Flag indicating whether the client currently is pending. */
     675    bool mIsPending;
     676    /** The client's pending connection. */
     677    ClientConnection mPending;
     678} ClientState;
     679typedef std::map< uint32_t, ClientState > ClientStateMap;
     680typedef std::map< uint32_t, ClientState >::iterator ClientStateMapIter;
     681typedef std::map< uint32_t, ClientState >::const_iterator ClientStateMapIterConst;
    148682
    149683/**
     
    152686class Service : public RTCNonCopyable
    153687{
     688
    154689private:
     690
    155691    /** Type definition for use in callback functions. */
    156692    typedef Service SELF;
    157693    /** HGCM helper functions. */
    158694    PVBOXHGCMSVCHELPERS mpHelpers;
    159     /*
     695    /**
    160696     * Callback function supplied by the host for notification of updates
    161697     * to properties.
     
    164700    /** User data pointer to be supplied to the host callback function. */
    165701    void *mpvHostData;
    166     /** The deferred calls list. */
    167     ClientWaiterList mClientWaiterList;
    168     /** The host command list. */
    169     HostCmdList mHostCmds;
    170     /** Client contexts list. */
    171     ClientContextsList mClientContextsList;
    172     /** Number of connected clients. */
    173     uint32_t mNumClients;
     702    /** List containing all buffered host commands. */
     703    RTLISTANCHOR mHostCmdList;
     704    /** Map containing all connected clients. The primary key contains
     705     *  the HGCM client ID to identify the client. */
     706    ClientStateMap mClientStateMap;
    174707public:
    175708    explicit Service(PVBOXHGCMSVCHELPERS pHelpers)
     
    177710        , mpfnHostCallback(NULL)
    178711        , mpvHostData(NULL)
    179         , mNumClients(0)
    180     {
     712    {
     713        RTListInit(&mHostCmdList);
    181714    }
    182715
     
    207740        LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
    208741        SELF *pSelf = reinterpret_cast<SELF *>(pvService);
    209         int rc = pSelf->clientConnect(u32ClientID, pvClient);
    210         LogFlowFunc (("rc=%Rrc\n", rc));
    211         return rc;
     742        return pSelf->clientConnect(u32ClientID, pvClient);
    212743    }
    213744
     
    223754        LogFlowFunc (("pvService=%p, u32ClientID=%u, pvClient=%p\n", pvService, u32ClientID, pvClient));
    224755        SELF *pSelf = reinterpret_cast<SELF *>(pvService);
    225         int rc = pSelf->clientDisconnect(u32ClientID, pvClient);
    226         LogFlowFunc (("rc=%Rrc\n", rc));
    227         return rc;
     756        return pSelf->clientDisconnect(u32ClientID, pvClient);
    228757    }
    229758
     
    241770    {
    242771        AssertLogRelReturnVoid(VALID_PTR(pvService));
    243         LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
     772        LogFlowFunc (("pvService=%p, callHandle=%p, u32ClientID=%u, pvClient=%p, u32Function=%u, cParms=%u, paParms=%p\n",
     773                      pvService, callHandle, u32ClientID, pvClient, u32Function, cParms, paParms));
    244774        SELF *pSelf = reinterpret_cast<SELF *>(pvService);
    245775        pSelf->call(callHandle, u32ClientID, pvClient, u32Function, cParms, paParms);
    246         LogFlowFunc (("returning\n"));
    247776    }
    248777
     
    259788        LogFlowFunc (("pvService=%p, u32Function=%u, cParms=%u, paParms=%p\n", pvService, u32Function, cParms, paParms));
    260789        SELF *pSelf = reinterpret_cast<SELF *>(pvService);
    261         int rc = pSelf->hostCall(u32Function, cParms, paParms);
    262         LogFlowFunc (("rc=%Rrc\n", rc));
    263         return rc;
     790        return pSelf->hostCall(u32Function, cParms, paParms);
    264791    }
    265792
     
    278805        return VINF_SUCCESS;
    279806    }
     807
    280808private:
    281     int paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    282     void paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf);
    283     int paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, const VBOXGUESTCTRPARAMBUFFER *pSrcBuf);
     809
    284810    int prepareExecute(uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    285811    int clientConnect(uint32_t u32ClientID, void *pvClient);
    286812    int clientDisconnect(uint32_t u32ClientID, void *pvClient);
    287     int assignHostCmdToGuest(const VBOXGUESTCTRLHOSTCMD *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    288     int retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
     813    int clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
     814    int clientSetMsgFilter(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    289815    int cancelHostCmd(uint32_t u32ContextID);
    290     int cancelPendingWaits(uint32_t u32ClientID);
    291     int notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    292     int processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    293     void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID,
    294               void *pvClient, uint32_t eFunction, uint32_t cParms,
    295               VBOXHGCMSVCPARM paParms[]);
     816    int cancelPendingWaits(uint32_t u32ClientID, int rcPending);
     817    int hostCallback(VBOXHGCMCALLHANDLE callHandle, uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
     818    int hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
     819    void call(VBOXHGCMCALLHANDLE callHandle, uint32_t u32ClientID, void *pvClient, uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    296820    int hostCall(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[]);
    297     int uninit();
     821    int uninit(void);
    298822};
    299 
    300 
    301 /**
    302  * Stores a HGCM request in an internal buffer. Needs to be free'd using paramBufferFree().
    303  *
    304  * @return  IPRT status code.
    305  * @param   pBuf                    Buffer to store the HGCM request into.
    306  * @param   uMsg                    Message type.
    307  * @param   cParms                  Number of parameters of HGCM request.
    308  * @param   paParms                 Array of parameters of HGCM request.
    309  */
    310 int Service::paramBufferAllocate(PVBOXGUESTCTRPARAMBUFFER pBuf, uint32_t uMsg, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
    311 {
    312     AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
    313     if (cParms)
    314         AssertPtrReturn(paParms, VERR_INVALID_POINTER);
    315 
    316     /* Paranoia. */
    317     if (cParms > 256)
    318         cParms = 256;
    319 
    320     int rc = VINF_SUCCESS;
    321 
    322     /*
    323      * Don't verify anything here (yet), because this function only buffers
    324      * the HGCM data into an internal structure and reaches it back to the guest (client)
    325      * in an unmodified state.
    326      */
    327     pBuf->uMsg = uMsg;
    328     pBuf->uParmCount = cParms;
    329     if (pBuf->uParmCount)
    330     {
    331         pBuf->pParms = (VBOXHGCMSVCPARM*)RTMemAlloc(sizeof(VBOXHGCMSVCPARM) * pBuf->uParmCount);
    332         if (NULL == pBuf->pParms)
    333             rc = VERR_NO_MEMORY;
    334     }
    335 
    336     if (RT_SUCCESS(rc))
    337     {
    338         for (uint32_t i = 0; i < pBuf->uParmCount; i++)
    339         {
    340             pBuf->pParms[i].type = paParms[i].type;
    341             switch (paParms[i].type)
    342             {
    343                 case VBOX_HGCM_SVC_PARM_32BIT:
    344                     pBuf->pParms[i].u.uint32 = paParms[i].u.uint32;
    345                     break;
    346 
    347                 case VBOX_HGCM_SVC_PARM_64BIT:
    348                     /* Not supported yet. */
    349                     break;
    350 
    351                 case VBOX_HGCM_SVC_PARM_PTR:
    352                     pBuf->pParms[i].u.pointer.size = paParms[i].u.pointer.size;
    353                     if (pBuf->pParms[i].u.pointer.size > 0)
    354                     {
    355                         pBuf->pParms[i].u.pointer.addr = RTMemAlloc(pBuf->pParms[i].u.pointer.size);
    356                         if (NULL == pBuf->pParms[i].u.pointer.addr)
    357                         {
    358                             rc = VERR_NO_MEMORY;
    359                             break;
    360                         }
    361                         else
    362                             memcpy(pBuf->pParms[i].u.pointer.addr,
    363                                    paParms[i].u.pointer.addr,
    364                                    pBuf->pParms[i].u.pointer.size);
    365                     }
    366                     else
    367                     {
    368                         /* Size is 0 -- make sure we don't have any pointer. */
    369                         pBuf->pParms[i].u.pointer.addr = NULL;
    370                     }
    371                     break;
    372 
    373                 default:
    374                     break;
    375             }
    376             if (RT_FAILURE(rc))
    377                 break;
    378         }
    379     }
    380     return rc;
    381 }
    382 
    383 /**
    384  * Frees a buffered HGCM request.
    385  *
    386  * @return  IPRT status code.
    387  * @param   pBuf                    Parameter buffer to free.
    388  */
    389 void Service::paramBufferFree(PVBOXGUESTCTRPARAMBUFFER pBuf)
    390 {
    391     AssertPtr(pBuf);
    392     for (uint32_t i = 0; i < pBuf->uParmCount; i++)
    393     {
    394         switch (pBuf->pParms[i].type)
    395         {
    396             case VBOX_HGCM_SVC_PARM_PTR:
    397                 if (pBuf->pParms[i].u.pointer.size > 0)
    398                     RTMemFree(pBuf->pParms[i].u.pointer.addr);
    399                 break;
    400         }
    401     }
    402     if (pBuf->uParmCount)
    403     {
    404         RTMemFree(pBuf->pParms);
    405         pBuf->uParmCount = 0;
    406     }
    407 }
    408 
    409 /**
    410  * Copies data from a buffered HGCM request to the current HGCM request.
    411  *
    412  * @return  IPRT status code.
    413  * @param   paDstParms              Array of parameters of HGCM request to fill the data into.
    414  * @param   cPDstarms               Number of parameters the HGCM request can handle.
    415  * @param   pSrcBuf                 Parameter buffer to assign.
    416  */
    417 int Service::paramBufferAssign(VBOXHGCMSVCPARM paDstParms[], uint32_t cDstParms, const VBOXGUESTCTRPARAMBUFFER *pSrcBuf)
    418 {
    419     AssertPtr(pSrcBuf);
    420     int rc = VINF_SUCCESS;
    421     if (cDstParms != pSrcBuf->uParmCount)
    422     {
    423         LogFlowFunc(("Parameter count does not match (got %u, expected %u)\n",
    424                      cDstParms, pSrcBuf->uParmCount));
    425         rc = VERR_INVALID_PARAMETER;
    426     }
    427     else
    428     {
    429         for (uint32_t i = 0; i < pSrcBuf->uParmCount; i++)
    430         {
    431             if (paDstParms[i].type != pSrcBuf->pParms[i].type)
    432             {
    433                 LogFlowFunc(("Parameter %u type mismatch (got %u, expected %u)\n",
    434                              i, paDstParms[i].type, pSrcBuf->pParms[i].type));
    435                 rc = VERR_INVALID_PARAMETER;
    436             }
    437             else
    438             {
    439                 switch (pSrcBuf->pParms[i].type)
    440                 {
    441                     case VBOX_HGCM_SVC_PARM_32BIT:
    442                         paDstParms[i].u.uint32 = pSrcBuf->pParms[i].u.uint32;
    443                         break;
    444 
    445                     case VBOX_HGCM_SVC_PARM_PTR:
    446                     {
    447                         if (!pSrcBuf->pParms[i].u.pointer.size)
    448                             continue; /* Only copy buffer if there actually is something to copy. */
    449 
    450                         if (!paDstParms[i].u.pointer.addr)
    451                             rc = VERR_INVALID_PARAMETER;
    452 
    453                         if (paDstParms[i].u.pointer.size < pSrcBuf->pParms[i].u.pointer.size)
    454                             rc = VERR_BUFFER_OVERFLOW;
    455 
    456                         if (RT_SUCCESS(rc))
    457                         {
    458                             memcpy(paDstParms[i].u.pointer.addr,
    459                                    pSrcBuf->pParms[i].u.pointer.addr,
    460                                    pSrcBuf->pParms[i].u.pointer.size);
    461                         }
    462 
    463                         break;
    464                     }
    465 
    466                     case VBOX_HGCM_SVC_PARM_64BIT:
    467                         /* Fall through is intentional. */
    468                     default:
    469                         LogFlowFunc(("Parameter %u of type %u is not supported yet\n",
    470                                      i, pSrcBuf->pParms[i].type));
    471                         rc = VERR_NOT_SUPPORTED;
    472                         break;
    473                 }
    474             }
    475 
    476             if (RT_FAILURE(rc))
    477             {
    478                 LogFlowFunc(("Parameter %u invalid (rc=%Rrc), refusing\n",
    479                              i, rc));
    480                 break;
    481             }
    482         }
    483     }
    484     return rc;
    485 }
    486823
    487824/**
     
    494831int Service::clientConnect(uint32_t u32ClientID, void *pvClient)
    495832{
    496     LogFlowFunc(("New client (%ld) connected\n", u32ClientID));
    497     if (mNumClients < UINT32_MAX)
    498         mNumClients++;
    499     else
    500         AssertMsgFailed(("Max. number of clients reached\n"));
     833    LogFlowFunc(("New client with ID=%RU32 connected\n", u32ClientID));
     834#ifdef VBOX_STRICT
     835    ClientStateMapIterConst it = mClientStateMap.find(u32ClientID);
     836    if (it != mClientStateMap.end())
     837    {
     838        AssertMsgFailed(("Client with ID=%RU32 already connected when it should not\n",
     839                         u32ClientID));
     840        return VERR_ALREADY_EXISTS;
     841    }
     842#endif
     843    ClientState cs(mpHelpers);
     844    mClientStateMap[u32ClientID] = cs;
     845    /** @todo Exception handling! */
    501846    return VINF_SUCCESS;
    502847}
     
    513858int Service::clientDisconnect(uint32_t u32ClientID, void *pvClient)
    514859{
    515     LogFlowFunc(("Client (ID=%u, %u clients total) disconnected\n",
    516                  u32ClientID, mNumClients));
    517     Assert(mNumClients > 0);
    518     mNumClients--;
     860    LogFlowFunc(("Client with ID=%RU32 (%zu clients total) disconnected\n",
     861                 u32ClientID, mClientStateMap.size()));
    519862
    520863    /* If this was the last connected (guest) client we need to
    521864     * unblock all eventually queued up (waiting) host calls. */
    522     bool fAllClientsDisconnected = mNumClients == 0;
     865    bool fAllClientsDisconnected = mClientStateMap.size() == 0;
    523866    if (fAllClientsDisconnected)
    524         LogFlowFunc(("No connected clients left, notifying all queued up callbacks\n"));
     867        LogFlowFunc(("No connected clients left, notifying all queued up host callbacks\n"));
    525868
    526869    /*
     
    529872    int rc = VINF_SUCCESS;
    530873
    531     CallListIter itCall = mClientWaiterList.begin();
    532     while (itCall != mClientWaiterList.end())
    533     {
    534         if (itCall->mClientID == u32ClientID)
    535         {
    536             itCall = mClientWaiterList.erase(itCall);
    537         }
    538         else
    539             itCall++;
    540     }
    541 
    542     ClientContextsListIter itContextList = mClientContextsList.begin();
    543     while (   itContextList != mClientContextsList.end()
     874    ClientStateMapIter itClientState = mClientStateMap.begin();
     875    while (   itClientState != mClientStateMap.end()
    544876           && RT_SUCCESS(rc))
    545877    {
     
    549881         * anymore.
    550882         */
    551         if (   itContextList->mClientID == u32ClientID
     883        if (   itClientState->first == u32ClientID
    552884            || fAllClientsDisconnected)
    553885        {
    554             std::list< uint32_t >::iterator itContext = itContextList->mContextList.begin();
    555             while (itContext != itContextList->mContextList.end())
     886            LogFlowFunc(("Cancelling %RU32 context of client ID=%RU32\n",
     887                         itClientState->second.mContextMap.size(), u32ClientID));
     888
     889            ClientContextMapIter itContext = itClientState->second.mContextMap.begin();
     890            while (itContext != itClientState->second.mContextMap.end())
    556891            {
    557                 uint32_t uContextID = (*itContext);
     892                uint32_t uContextID = itContext->first;
    558893
    559894                /*
     
    561896                 * around and need to be cleaned up (canceling waits etc).
    562897                 */
    563                 LogFlowFunc(("Notifying CID=%u of disconnect ...\n", uContextID));
    564                 rc = cancelHostCmd(uContextID);
     898                LogFlowFunc(("Notifying CID=%RU32 of disconnect ...\n", uContextID));
     899                int rc2 = cancelHostCmd(uContextID);
    565900                if (RT_FAILURE(rc))
    566901                {
    567                     LogFlowFunc(("Cancelling of CID=%u failed with rc=%Rrc\n",
    568                                  uContextID, rc));
     902                    LogFlowFunc(("Cancelling assigned client CID=%RU32 failed with rc=%Rrc\n",
     903                                 uContextID, rc2));
    569904                    /* Keep going. */
    570905                }
    571906
     907                AssertPtr(itContext->second.mpHostCmd);
     908                itContext->second.mpHostCmd->Release();
     909
     910                LogFlowFunc(("Released host command CID=%RU32 returned with rc=%Rrc\n",
     911                             uContextID, rc2));
     912
    572913                itContext++;
    573914            }
    574             itContextList = mClientContextsList.erase(itContextList);
     915            itClientState = mClientStateMap.erase(itClientState);
    575916        }
    576917        else
    577             itContextList++;
     918            itClientState++;
    578919    }
    579920
     
    585926         * via a (multi stage) progress object.
    586927         */
    587         HostCmdListIter itHostCmd;
    588         for (itHostCmd = mHostCmds.begin(); itHostCmd != mHostCmds.end(); itHostCmd++)
    589         {
    590             rc = cancelHostCmd(itHostCmd->mContextID);
    591             if (RT_FAILURE(rc))
     928        HostCommand *pCurCmd = RTListGetFirst(&mHostCmdList, HostCommand, Node);
     929        while (pCurCmd)
     930        {
     931            HostCommand *pNext = RTListNodeGetNext(&pCurCmd->Node, HostCommand, Node);
     932            bool fLast = RTListNodeIsLast(&mHostCmdList, &pCurCmd->Node);
     933
     934            int rc2 = cancelHostCmd(pCurCmd->mContextID);
     935            if (RT_FAILURE(rc2))
    592936            {
    593                 LogFlowFunc(("Cancelling of buffered CID=%u failed with rc=%Rrc\n",
    594                              itHostCmd->mContextID, rc));
     937                LogFlowFunc(("Cancelling host command with CID=%u (refCount=%RU32) failed with rc=%Rrc\n",
     938                             pCurCmd->mContextID, pCurCmd->mRefCount, rc2));
    595939                /* Keep going. */
    596940            }
    597941
    598             paramBufferFree(&itHostCmd->mParmBuf);
    599         }
    600 
    601         mHostCmds.clear();
    602     }
    603 
    604     return rc;
    605 }
    606 
    607 /**
    608  * Assigns a specified host command to a client.
    609  *
    610  * @return  IPRT status code.
    611  * @param   pCmd                    Host command to send.
    612  * @param   callHandle              Call handle of the client to send the command to.
    613  * @param   cParms                  Number of parameters.
    614  * @param   paParms                 Array of parameters.
    615  */
    616 int Service::assignHostCmdToGuest(const VBOXGUESTCTRLHOSTCMD *pCmd, VBOXHGCMCALLHANDLE callHandle, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
    617 {
    618     AssertPtrReturn(pCmd, VERR_INVALID_POINTER);
    619     int rc;
    620 
    621     /* Does the current host command need more parameter space which
    622      * the client does not provide yet? */
    623     if (pCmd->mParmBuf.uParmCount > cParms)
    624     {
    625         paParms[0].setUInt32(pCmd->mParmBuf.uMsg);       /* Message ID */
    626         paParms[1].setUInt32(pCmd->mParmBuf.uParmCount); /* Required parameters for message */
    627 
    628         /*
    629         * So this call apparently failed because the guest wanted to peek
    630         * how much parameters it has to supply in order to successfully retrieve
    631         * this command. Let's tell him so!
    632         */
    633         rc = VERR_TOO_MUCH_DATA;
    634     }
    635     else
    636     {
    637         rc = paramBufferAssign(paParms, cParms, &pCmd->mParmBuf);
    638 
    639         /* Has there been enough parameter space but the wrong parameter types
    640          * were submitted -- maybe the client was just asking for the next upcoming
    641          * host message?
    642          *
    643          * Note: To keep this compatible to older clients we return VERR_TOO_MUCH_DATA
    644          *       in every case. */
    645         if (RT_FAILURE(rc))
    646             rc = VERR_TOO_MUCH_DATA;
    647     }
    648 
    649     LogFlowFunc(("Returned with rc=%Rrc\n", rc));
     942            pCurCmd->Free();
     943
     944            RTListNodeRemove(&pCurCmd->Node);
     945            RTMemFree(pCurCmd);
     946
     947            if (fLast)
     948                break;
     949
     950            pCurCmd = pNext;
     951        }
     952
     953        Assert(RTListIsEmpty(&mHostCmdList));
     954    }
     955
    650956    return rc;
    651957}
     
    661967 * @param   paParms                     Array of parameters.
    662968 */
    663 int Service::retrieveNextHostCmd(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
    664                                  uint32_t cParms, VBOXHGCMSVCPARM paParms[])
    665 {
    666     int rc = VINF_SUCCESS;
    667 
     969int Service::clientGetCommand(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
     970                              uint32_t cParms, VBOXHGCMSVCPARM paParms[])
     971{
    668972    /*
    669973     * Lookup client in our list so that we can assign the context ID of
    670974     * a command to that client.
    671975     */
    672     std::list< ClientContexts >::reverse_iterator it = mClientContextsList.rbegin();
    673     while (it != mClientContextsList.rend())
    674     {
    675         if (it->mClientID == u32ClientID)
    676             break;
    677         it++;
    678     }
    679 
    680     /* Not found? Add client to list. */
    681     if (it == mClientContextsList.rend())
    682     {
    683         mClientContextsList.push_back(ClientContexts(u32ClientID));
    684         it = mClientContextsList.rbegin();
    685     }
    686     Assert(it != mClientContextsList.rend());
     976    ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
     977    AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
     978                                                       u32ClientID));
     979    if (itClientState == mClientStateMap.end())
     980        return VERR_NOT_FOUND; /* Should never happen. */
     981
     982    ClientState &clientState = itClientState->second;
     983
     984    /* Use the current (inbound) connection. */
     985    ClientConnection thisCon;
     986    thisCon.mHandle   = callHandle;
     987    thisCon.mNumParms = cParms;
     988    thisCon.mParms    = paParms;
    687989
    688990    /*
    689991     * If host command list is empty (nothing to do right now) just
    690992     * defer the call until we got something to do (makes the client
    691      * wait, depending on the flags set).
    692      */
    693     if (mHostCmds.empty()) /* If command list is empty, defer ... */
    694     {
    695         mClientWaiterList.push_back(ClientWaiter(u32ClientID, callHandle, paParms, cParms));
    696         rc = VINF_HGCM_ASYNC_EXECUTE;
     993     * wait).
     994     */
     995    int rc;
     996    if (RTListIsEmpty(&mHostCmdList))
     997    {
     998        rc = clientState.SetPending(&thisCon);
    697999    }
    6981000    else
    6991001    {
    700         /*
    701          * Get the next unassigned host command in the list.
    702          */
    703          VBOXGUESTCTRLHOSTCMD &curCmd = mHostCmds.front();
    704          rc = assignHostCmdToGuest(&curCmd, callHandle, cParms, paParms);
    705          if (RT_SUCCESS(rc))
    706          {
    707              /* Remember which client processes which context (for
    708               * later reference & cleanup). */
    709              /// @todo r=bird: check if already in the list.
    710              /// @todo Use a map instead of a list?
    711              it->mContextList.push_back(curCmd.mContextID);
    712 
    713              /* Only if the guest really got and understood the message remove it from the list. */
    714              paramBufferFree(&curCmd.mParmBuf);
    715              mHostCmds.pop_front();
    716          }
    717          else
    718          {
    719              bool fRemoveCmd = false;
    720              uint32_t uTries = curCmd.mTries++;
    721 
    722              /* If the client understood the message but supplied too little buffer space
    723               * don't send this message again and drop it after 3 unsuccessful attempts.
    724               * The host then should take care of next actions (maybe retry it with a smaller buffer). */
    725              if (   rc     == VERR_BUFFER_OVERFLOW
    726                  && uTries >= 3)
    727              {
    728                  fRemoveCmd = true;
    729              }
    730              /* Client did not understand the message or something else weird happened. Try again one
    731               * more time and drop it if it didn't get handled then. */
    732              else if (uTries > 1)
    733                  fRemoveCmd = true;
    734 
    735              if (fRemoveCmd)
    736              {
    737                  paramBufferFree(&curCmd.mParmBuf);
    738                  mHostCmds.pop_front();
    739              }
    740          }
    741     }
     1002        rc = clientState.RunNow(&thisCon, &mHostCmdList);
     1003    }
     1004
     1005    LogFlowFunc(("Returned with rc=%Rrc\n", rc));
    7421006    return rc;
    7431007}
    7441008
    745 /**
    746  * Cancels a buffered host command to unblock waits on Main side
    747  * (via (multi stage) progress objects.
     1009int Service::clientSetMsgFilter(uint32_t u32ClientID, VBOXHGCMCALLHANDLE callHandle,
     1010                                uint32_t cParms, VBOXHGCMSVCPARM paParms[])
     1011{
     1012    /*
     1013     * Lookup client in our list so that we can assign the context ID of
     1014     * a command to that client.
     1015     */
     1016    ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
     1017    AssertMsg(itClientState != mClientStateMap.end(), ("Client with ID=%RU32 not found when it should be present\n",
     1018                                                       u32ClientID));
     1019    if (itClientState == mClientStateMap.end())
     1020        return VERR_NOT_FOUND; /* Should never happen. */
     1021
     1022    if (cParms != 2)
     1023        return VERR_INVALID_PARAMETER;
     1024
     1025    uint32_t uMaskAdd;
     1026    int rc = paParms[0].getUInt32(&uMaskAdd);
     1027    if (RT_SUCCESS(rc))
     1028    {
     1029        /* paParms[1] unused yet. */
     1030
     1031        ClientState &clientState = itClientState->second;
     1032
     1033        clientState.mFlags |= CLIENTSTATE_FLAG_CONTEXTFILTER;
     1034        clientState.mContextFilter = uMaskAdd;
     1035
     1036        LogFlowFunc(("Client ID=%RU32 now has filter=%x enabled (flags=%x)\n",
     1037                     u32ClientID, clientState.mContextFilter, clientState.mFlags));
     1038    }
     1039
     1040    LogFlowFunc(("Returned with rc=%Rrc\n", rc));
     1041    return rc;
     1042}
     1043
     1044/**
     1045 * Cancels a buffered host command to unblock waiting on Main side
     1046 * via callbacks.
    7481047 *
    7491048 * @return  IPRT status code.
     
    7561055    LogFlowFunc(("Cancelling CID=%u ...\n", u32ContextID));
    7571056
    758     CALLBACKDATACLIENTDISCONNECTED data;
    759     data.hdr.u32Magic = CALLBACKDATAMAGIC_CLIENT_DISCONNECTED;
    760     data.hdr.u32ContextID = u32ContextID;
     1057    CALLBACKDATA_CLIENT_DISCONNECTED data;
     1058    data.hdr.uContextID = u32ContextID;
    7611059
    7621060    AssertPtr(mpfnHostCallback);
     
    7721070 * @return  IPRT status code.
    7731071 * @param   u32ClientID                 The client's ID.
    774  */
    775 int Service::cancelPendingWaits(uint32_t u32ClientID)
    776 {
    777     int rc = VINF_SUCCESS;
    778     CallListIter it = mClientWaiterList.begin();
    779     while (it != mClientWaiterList.end())
    780     {
    781         if (it->mClientID == u32ClientID)
    782         {
    783             if (it->mNumParms >= 2)
    784             {
    785                 it->mParms[0].setUInt32(HOST_CANCEL_PENDING_WAITS); /* Message ID. */
    786                 it->mParms[1].setUInt32(0);                         /* Required parameters for message. */
    787             }
    788             if (mpHelpers)
    789                 mpHelpers->pfnCallComplete(it->mHandle, rc);
    790             it = mClientWaiterList.erase(it);
    791         }
    792         else
    793             it++;
    794     }
    795     return rc;
     1072 * @param   rcPending                   Result code for completing pending operation.
     1073 */
     1074int Service::cancelPendingWaits(uint32_t u32ClientID, int rcPending)
     1075{
     1076    ClientStateMapIter itClientState = mClientStateMap.find(u32ClientID);
     1077    if (itClientState != mClientStateMap.end())
     1078        return itClientState->second.CancelWaiting(rcPending);
     1079
     1080    return VINF_SUCCESS;
    7961081}
    7971082
     
    8011086 *
    8021087 * @return  IPRT status code.
     1088 * @param   callHandle              Call handle.
    8031089 * @param   eFunction               Function (event) that occured.
    8041090 * @param   cParms                  Number of parameters.
    8051091 * @param   paParms                 Array of parameters.
    8061092 */
    807 int Service::notifyHost(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
     1093int Service::hostCallback(VBOXHGCMCALLHANDLE callHandle,
     1094                          uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
    8081095{
    8091096    LogFlowFunc(("eFunction=%ld, cParms=%ld, paParms=%p\n",
    8101097                 eFunction, cParms, paParms));
    811     int rc = VINF_SUCCESS;
    812     if (   eFunction == GUEST_EXEC_SEND_STATUS
    813         && cParms    == 5)
    814     {
    815         CALLBACKDATAEXECSTATUS data;
    816         data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_STATUS;
    817         paParms[0].getUInt32(&data.hdr.u32ContextID);
    818 
    819         paParms[1].getUInt32(&data.u32PID);
    820         paParms[2].getUInt32(&data.u32Status);
    821         paParms[3].getUInt32(&data.u32Flags);
    822         paParms[4].getPointer(&data.pvData, &data.cbData);
    823 
    824         if (mpfnHostCallback)
    825             rc = mpfnHostCallback(mpvHostData, eFunction,
    826                                   (void *)(&data), sizeof(data));
    827     }
    828     else if (   eFunction == GUEST_EXEC_SEND_OUTPUT
    829              && cParms    == 5)
    830     {
    831         CALLBACKDATAEXECOUT data;
    832         data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_OUT;
    833         paParms[0].getUInt32(&data.hdr.u32ContextID);
    834 
    835         paParms[1].getUInt32(&data.u32PID);
    836         paParms[2].getUInt32(&data.u32HandleId);
    837         paParms[3].getUInt32(&data.u32Flags);
    838         paParms[4].getPointer(&data.pvData, &data.cbData);
    839 
    840         if (mpfnHostCallback)
    841             rc = mpfnHostCallback(mpvHostData, eFunction,
    842                                   (void *)(&data), sizeof(data));
    843     }
    844     else if (   eFunction == GUEST_EXEC_SEND_INPUT_STATUS
    845              && cParms    == 5)
    846     {
    847         CALLBACKDATAEXECINSTATUS data;
    848         data.hdr.u32Magic = CALLBACKDATAMAGIC_EXEC_IN_STATUS;
    849         paParms[0].getUInt32(&data.hdr.u32ContextID);
    850 
    851         paParms[1].getUInt32(&data.u32PID);
    852         paParms[2].getUInt32(&data.u32Status);
    853         paParms[3].getUInt32(&data.u32Flags);
    854         paParms[4].getUInt32(&data.cbProcessed);
    855 
    856         if (mpfnHostCallback)
    857             rc = mpfnHostCallback(mpvHostData, eFunction,
    858                                   (void *)(&data), sizeof(data));
     1098
     1099    int rc;
     1100    if (mpfnHostCallback)
     1101    {
     1102        VBOXGUESTCTRLHOSTCALLBACK data(cParms, paParms);
     1103        rc = mpfnHostCallback(mpvHostData, eFunction,
     1104                              (void *)(&data), sizeof(data));
    8591105    }
    8601106    else
    8611107        rc = VERR_NOT_SUPPORTED;
    862     LogFlowFunc(("returning %Rrc\n", rc));
     1108
     1109    LogFlowFunc(("Returning rc=%Rrc\n", rc));
    8631110    return rc;
    8641111}
     
    8731120 * @param   paParms                 Array of parameters.
    8741121 */
    875 int Service::processHostCmd(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
     1122int Service::hostProcessCommand(uint32_t eFunction, uint32_t cParms, VBOXHGCMSVCPARM paParms[])
    8761123{
    8771124    /*
     
    8811128     * the guest is not running/system is messed up somehow.
    8821129     */
    883     if (mNumClients == 0)
     1130    if (mClientStateMap.size() == 0)
    8841131        return VERR_NOT_FOUND;
    885     VBOXGUESTCTRLHOSTCMD newCmd;
    886     int rc = paramBufferAllocate(&newCmd.mParmBuf, eFunction, cParms, paParms);
    887     if (   RT_SUCCESS(rc)
    888         && cParms) /* Make sure we at least get one parameter (that is, the context ID). */
    889     {
     1132
     1133    int rc;
     1134    HostCommand *pHostCmd = (HostCommand*)RTMemAllocZ(sizeof(HostCommand));
     1135    if (pHostCmd)
     1136    {
     1137        rc = pHostCmd->Allocate(eFunction, cParms, paParms);
     1138        if (RT_SUCCESS(rc))
     1139            RTListAppend(&mHostCmdList, &pHostCmd->Node);
     1140    }
     1141    else
     1142        rc = VERR_NO_MEMORY;
     1143
     1144    if (RT_SUCCESS(rc))
     1145    {
     1146        LogFlowFunc(("Handling host command CID=%RU32, numClients=%zu\n",
     1147                     pHostCmd->mContextID, mClientStateMap.size()));
     1148
    8901149        /*
    891          * Assume that the context ID *always* is the first parameter,
    892          * assign the context ID to the command.
     1150         * Wake up all pending clients which are interested in this
     1151         * host command.
    8931152         */
    894         newCmd.mParmBuf.pParms[0].getUInt32(&newCmd.mContextID);
    895     }
    896     else if (!cParms)
    897         rc = VERR_INVALID_PARAMETER;
    898 
    899     if (RT_SUCCESS(rc))
    900     {
    901         LogFlowFunc(("Handling host command CID = %u\n",
    902                      newCmd.mContextID));
    903 
    904         bool fProcessed = false;
    905 
    906         /* Can we wake up a waiting client on guest? */
    907         if (!mClientWaiterList.empty())
    908         {
    909             ClientWaiter guest = mClientWaiterList.front();
    910             rc = assignHostCmdToGuest(&newCmd,
    911                                       guest.mHandle, guest.mNumParms, guest.mParms);
    912 
    913             /* In any case the client did something, so wake up and remove from list. */
    914             AssertPtr(mpHelpers);
    915             mpHelpers->pfnCallComplete(guest.mHandle, rc);
    916             mClientWaiterList.pop_front();
    917 
    918             /*
    919              * If we got back an error (like VERR_TOO_MUCH_DATA or VERR_BUFFER_OVERFLOW)
    920              * we buffer the host command in the next block and return success to the host.
    921              */
    922             if (RT_FAILURE(rc))
     1153#ifdef DEBUG
     1154        uint32_t uClientsWokenUp = 0;
     1155#endif
     1156
     1157        ClientStateMapIter itClientState = mClientStateMap.begin();
     1158        AssertMsg(itClientState != mClientStateMap.end(), ("Client state map is empty when it should not\n"));
     1159        while (itClientState != mClientStateMap.end())
     1160        {
     1161            if (itClientState->second.mIsPending) /* Only wake up pending clients. */
    9231162            {
    924                 rc = VINF_SUCCESS;
    925             }
    926             else /* If command was understood by the client, free and remove from host commands list. */
    927             {
    928                 LogFlowFunc(("Host command CID = %u processed with rc=%Rrc\n",
    929                              newCmd.mContextID, rc));
    930 
    931                 paramBufferFree(&newCmd.mParmBuf);
    932             }
    933         }
    934 
    935         if (!fProcessed)
    936         {
    937             LogFlowFunc(("Buffering host command CID = %u (rc=%Rrc)\n",
    938                          newCmd.mContextID, rc));
    939 
    940             mHostCmds.push_back(newCmd);
    941         }
     1163                LogFlowFunc(("Waking up client ID=%RU32 (isPending=%RTbool) ...\n",
     1164                             itClientState->first, itClientState->second.mIsPending));
     1165
     1166                ClientState &clientState = itClientState->second;
     1167                int rc2 = clientState.Wakeup(&mHostCmdList);
     1168                LogFlowFunc(("Client ID=%RU32 wakeup ended with rc=%Rrc\n",
     1169                             itClientState->first, rc2));
     1170#ifdef DEBUG
     1171                uClientsWokenUp++;
     1172#endif
     1173            }
     1174            else
     1175                LogFlowFunc(("Client ID=%RU32 is not in pending state\n",
     1176                             itClientState->first));
     1177
     1178            itClientState++;
     1179        }
     1180
     1181#ifdef DEBUG
     1182        LogFlowFunc(("%RU32 clients have been succcessfully woken up\n",
     1183                      uClientsWokenUp));
     1184#endif
    9421185    }
    9431186
     
    9601203{
    9611204    int rc = VINF_SUCCESS;
    962     LogFlowFunc(("u32ClientID = %u, fn = %u, cParms = %u, paParms = 0x%p\n",
     1205    LogFlowFunc(("u32ClientID=%RU32, fn=%RU32, cParms=%RU32, paParms=0x%p\n",
    9631206                 u32ClientID, eFunction, cParms, paParms));
    9641207    try
    9651208    {
    966         switch (eFunction)
    967         {
    968             /*
    969              * The guest asks the host for the next message to process.
    970              */
    971             case GUEST_GET_HOST_MSG:
    972                 LogFlowFunc(("GUEST_GET_HOST_MSG\n"));
    973                 rc = retrieveNextHostCmd(u32ClientID, callHandle, cParms, paParms);
    974                 break;
    975 
    976             /*
    977              * The guest wants to shut down and asks us (this service) to cancel
    978              * all blocking pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
    979              * guest can gracefully shut down.
    980              */
    981             case GUEST_CANCEL_PENDING_WAITS:
    982                 LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
    983                 rc = cancelPendingWaits(u32ClientID);
    984                 break;
    985 
    986             /*
    987              * For all other regular commands we call our notifyHost
    988              * function. If the current command does not support notifications
    989              * notifyHost will return VERR_NOT_SUPPORTED.
    990              */
    991             default:
    992                 rc = notifyHost(eFunction, cParms, paParms);
    993                 break;
    994         }
    995         if (rc != VINF_HGCM_ASYNC_EXECUTE)
    996         {
    997             /* Tell the client that the call is complete (unblocks waiting). */
    998             AssertPtr(mpHelpers);
    999             mpHelpers->pfnCallComplete(callHandle, rc);
     1209        /*
     1210         * The guest asks the host for the next message to process.
     1211         */
     1212        if (eFunction == GUEST_MSG_WAIT)
     1213        {
     1214            LogFlowFunc(("GUEST_MSG_GET\n"));
     1215            rc = clientGetCommand(u32ClientID, callHandle, cParms, paParms);
     1216        }
     1217        else
     1218        {
     1219            switch (eFunction)
     1220            {
     1221                /*
     1222                 * A client wants to shut down and asks us (this service) to cancel
     1223                 * all blocking/pending waits (VINF_HGCM_ASYNC_EXECUTE) so that the
     1224                 * client can gracefully shut down.
     1225                 */
     1226                case GUEST_CANCEL_PENDING_WAITS:
     1227                    LogFlowFunc(("GUEST_CANCEL_PENDING_WAITS\n"));
     1228                    rc = cancelPendingWaits(u32ClientID, VINF_SUCCESS /* Pending result */);
     1229                    break;
     1230
     1231                /*
     1232                 * The guest only wants certain messages set by the filter mask(s).
     1233                 * Since VBox 4.3+.
     1234                 */
     1235                case GUEST_MSG_FILTER:
     1236                    LogFlowFunc(("GUEST_MSG_FILTER\n"));
     1237                    rc = clientSetMsgFilter(u32ClientID, callHandle, cParms, paParms);
     1238                    break;
     1239
     1240                /*
     1241                 * For all other regular commands we call our hostCallback
     1242                 * function. If the current command does not support notifications,
     1243                 * notifyHost will return VERR_NOT_SUPPORTED.
     1244                 */
     1245                default:
     1246                    rc = hostCallback(callHandle, eFunction, cParms, paParms);
     1247                    break;
     1248            }
     1249
     1250            if (rc != VINF_HGCM_ASYNC_EXECUTE)
     1251            {
     1252                /* Tell the client that the call is complete (unblocks waiting). */
     1253                AssertPtr(mpHelpers);
     1254                mpHelpers->pfnCallComplete(callHandle, rc);
     1255            }
    10001256        }
    10011257    }
     
    10041260        rc = VERR_NO_MEMORY;
    10051261    }
    1006     LogFlowFunc(("rc = %Rrc\n", rc));
    10071262}
    10081263
     
    10151270{
    10161271    int rc = VERR_NOT_SUPPORTED;
    1017     LogFlowFunc(("fn = %u, cParms = %u, paParms = 0x%p\n",
     1272    LogFlowFunc(("fn=%RU32, cParms=%RU32, paParms=0x%p\n",
    10181273                 eFunction, cParms, paParms));
    10191274    try
    10201275    {
    1021         rc = processHostCmd(eFunction, cParms, paParms);
     1276        switch (eFunction)
     1277        {
     1278            /**
     1279             * Host
     1280             */
     1281            case HOST_CANCEL_PENDING_WAITS:
     1282            {
     1283                LogFlowFunc(("HOST_CANCEL_PENDING_WAITS\n"));
     1284                ClientStateMapIter itClientState = mClientStateMap.begin();
     1285                while (itClientState != mClientStateMap.end())
     1286                {
     1287                    int rc2 = itClientState->second.CancelWaiting(VINF_SUCCESS /* Pending rc. */);
     1288                    if (RT_FAILURE(rc2))
     1289                        LogFlowFunc(("Cancelling waiting for client ID=%RU32 failed with rc=%Rrc",
     1290                                     itClientState->first, rc2));
     1291                    itClientState++;
     1292                }
     1293                rc = VINF_SUCCESS;
     1294                break;
     1295            }
     1296
     1297            default:
     1298                rc = hostProcessCommand(eFunction, cParms, paParms);
     1299                break;
     1300        }
    10221301    }
    10231302    catch (std::bad_alloc)
     
    10261305    }
    10271306
    1028     LogFlowFunc(("rc = %Rrc\n", rc));
    10291307    return rc;
    10301308}
     
    10321310int Service::uninit()
    10331311{
    1034     Assert(mHostCmds.empty());
     1312    Assert(RTListIsEmpty(&mHostCmdList));
    10351313
    10361314    return VINF_SUCCESS;
  • trunk/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp

    r36873 r44863  
    66
    77/*
    8  * Copyright (C) 2011 Oracle Corporation
     8 * Copyright (C) 2011-2013 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    157157    static CMDHOST s_aCmdHostAll[] =
    158158    {
    159         /** No client connected, invalid command. */
    160         { 1024 /* Not existing command */, 0, 0, false, VERR_NOT_SUPPORTED },
    161         { -1 /* Invalid command */, 0, 0, false, VERR_NOT_SUPPORTED },
     159        #if 0
     160        /** No client connected. */
     161        { 1024 /* Not existing command */, 0, 0, false, VERR_NOT_FOUND },
     162        { -1 /* Invalid command */, 0, 0, false, VERR_NOT_FOUND },
    162163        { HOST_CANCEL_PENDING_WAITS, 1024, 0, false, VERR_NOT_FOUND },
    163164        { HOST_CANCEL_PENDING_WAITS, 0, &s_aParms[0], false, VERR_NOT_FOUND },
     
    166167        { HOST_CANCEL_PENDING_WAITS, 0, 0, false, VERR_NOT_FOUND },
    167168
    168         /** Client connected. */
    169         { 1024 /* Not existing command */, 0, 0, true, VERR_NOT_SUPPORTED },
    170         { -1 /* Invalid command */, 0, 0, true, VERR_NOT_SUPPORTED },
     169        /** Client connected, no parameters given. */
     170        { HOST_EXEC_SET_INPUT, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER },
     171        { 1024 /* Not existing command */, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER },
     172        { -1 /* Invalid command */, 0 /* No parameters given */, 0, true, VERR_INVALID_PARAMETER },
    171173
    172174        /** Client connected, valid parameters given. */
     
    174176        { HOST_CANCEL_PENDING_WAITS, 1024, &s_aParms[0], true, VINF_SUCCESS },
    175177        { HOST_CANCEL_PENDING_WAITS, 0, &s_aParms[0], true, VINF_SUCCESS},
     178        #endif
    176179
    177180        /** Client connected, invalid parameters given. */
    178         { HOST_CANCEL_PENDING_WAITS, 1024, 0, true, VERR_INVALID_POINTER },
    179         { HOST_CANCEL_PENDING_WAITS, 1, 0, true, VERR_INVALID_POINTER },
    180         { HOST_CANCEL_PENDING_WAITS, -1, 0, true, VERR_INVALID_POINTER },
     181        { HOST_EXEC_CMD, 1024, 0, true, VERR_INVALID_POINTER },
     182        { HOST_EXEC_CMD, 1, 0, true, VERR_INVALID_POINTER },
     183        { HOST_EXEC_CMD, -1, 0, true, VERR_INVALID_POINTER },
    181184
    182185        /** Client connected, parameters given. */
     
    184187        { HOST_EXEC_CMD, 1, &s_aParms[0], true, VINF_SUCCESS },
    185188        { HOST_EXEC_SET_INPUT, 1, &s_aParms[0], true, VINF_SUCCESS },
    186         { HOST_EXEC_GET_OUTPUT, 1, &s_aParms[0], true, VINF_SUCCESS }
     189        { HOST_EXEC_GET_OUTPUT, 1, &s_aParms[0], true, VINF_SUCCESS },
     190
     191        /** Client connected, unknown command + valid parameters given. */
     192        { -1, 1, &s_aParms[0], true, VINF_SUCCESS }
    187193    };
    188194
     
    203209        /* No commands from host yet. */
    204210        static VBOXHGCMSVCPARM s_aParmsGuest[8];
     211        s_aParmsGuest[0].setUInt32(0 /* Msg type */);
     212        s_aParmsGuest[1].setUInt32(0 /* Parameters */);
    205213        pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    206                         GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]);
     214                        GUEST_MSG_WAIT, 2, &s_aParmsGuest[0]);
    207215        RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc);
    208216
     
    211219        s_aParmsHost[0].setUInt32(1000 /* Context ID */);
    212220        s_aParmsHost[1].setString("foo.bar");
    213 
    214         rc = pTable->pfnHostCall(pTable->pvService, HOST_EXEC_CMD, 2, &s_aParmsHost[0]);
     221        s_aParmsHost[2].setString("baz");
     222
     223        rc = pTable->pfnHostCall(pTable->pvService, HOST_EXEC_CMD, 3, &s_aParmsHost[0]);
    215224        RTTEST_CHECK_RC_RET(g_hTest, rc, VINF_SUCCESS, rc);
    216 
    217         /* Client: Get host command with a invalid parameter count specified. */
    218         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    219                         GUEST_GET_HOST_MSG, 1024, &s_aParmsGuest[0]);
    220         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc);
    221         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    222                         GUEST_GET_HOST_MSG, -1, &s_aParmsGuest[0]);
    223         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc);
    224         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    225                         GUEST_GET_HOST_MSG, -1, NULL);
    226         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc);
    227         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    228                         GUEST_GET_HOST_MSG, 16, NULL);
    229         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_INVALID_PARAMETER, callHandle.rc);
    230 
    231         /* Client: Get host command with a too small HGCM array. */
    232         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    233                         GUEST_GET_HOST_MSG, 0, &s_aParmsGuest[0]);
    234         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_TOO_MUCH_DATA, callHandle.rc);
    235         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    236                         GUEST_GET_HOST_MSG, 1, &s_aParmsGuest[0]);
    237         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_TOO_MUCH_DATA, callHandle.rc);
    238 
    239         /* Client: Get host command without an allocated buffer. */
    240         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    241                         GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]);
    242         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VERR_BUFFER_OVERFLOW, callHandle.rc);
    243 
    244         /* Client: Get host command, this time with a valid buffer. */
    245         char szBuf[16];
    246         s_aParmsGuest[1].setPointer(szBuf, sizeof(szBuf));
    247         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    248                         GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]);
    249         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc);
    250 
    251         /* Client: Now make sure there's no host message left anymore. */
    252         pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
    253                         GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]);
    254         RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc);
    255225
    256226        /* Client: Disconnect again. */
Note: See TracChangeset for help on using the changeset viewer.

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