VirtualBox

Ignore:
Timestamp:
Apr 28, 2011 8:55:09 AM (14 years ago)
Author:
vboxsync
Message:

HostServices/GuestCtrl: Rewritten testcase, now builds by default.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/HostServices/GuestControl/testcase/tstGuestControlSvc.cpp

    r33806 r36873  
    66
    77/*
    8  * Copyright (C) 2010 Oracle Corporation
     8 * Copyright (C) 2011 Oracle Corporation
    99 *
    1010 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    2121*******************************************************************************/
    2222#include <VBox/HostServices/GuestControlSvc.h>
    23 #include <iprt/alloca.h>
    2423#include <iprt/initterm.h>
    25 #include <iprt/crc.h>
    26 #include <iprt/ctype.h>
    27 #include <iprt/env.h>
    28 #include <iprt/file.h>
    29 #include <iprt/getopt.h>
    30 #include <iprt/handle.h>
    31 #include <iprt/mem.h>
    32 #include <iprt/message.h>
    33 #include <iprt/param.h>
    34 #include <iprt/path.h>
    35 #include <iprt/pipe.h>
    36 #include <iprt/poll.h>
    37 #include <iprt/process.h>
    3824#include <iprt/stream.h>
    39 #include <iprt/thread.h>
     25#include <iprt/test.h>
    4026
    4127#include "../gctrl.h"
    4228
     29/*******************************************************************************
     30*   Global Variables                                                           *
     31*******************************************************************************/
     32static RTTEST g_hTest = NIL_RTTEST;
     33
    4334using namespace guestControl;
    4435
    45 extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad (VBOXHGCMSVCFNTABLE *pTable);
    46 char g_szImageName[RTPATH_MAX];
    47 
    48 /** Prototypes. */
    49 int guestExecSendStdOut(VBOXHGCMSVCFNTABLE *pTable,
    50                         const char *pszStdOut, uint32_t cbStdOut);
     36extern "C" DECLCALLBACK(DECLEXPORT(int)) VBoxHGCMSvcLoad(VBOXHGCMSVCFNTABLE *pTable);
    5137
    5238/** Simple call handle structure for the guest call completion callback */
    5339struct VBOXHGCMCALLHANDLE_TYPEDEF
    5440{
    55     /** Where to store the result code */
     41    /** Where to store the result code. */
    5642    int32_t rc;
    5743};
    5844
    59 /**
    60  * For buffering process input supplied by the client.
    61  */
    62 typedef struct TXSEXECSTDINBUF
    63 {
    64     /** The mount of buffered data. */
    65     size_t  cb;
    66     /** The current data offset. */
    67     size_t  off;
    68     /** The data buffer. */
    69     char   *pch;
    70     /** The amount of allocated buffer space. */
    71     size_t  cbAllocated;
    72     /** Send further input into the bit bucket (stdin is dead). */
    73     bool    fBitBucket;
    74     /** The CRC-32 for standard input (received part). */
    75     uint32_t uCrc32;
    76 } TXSEXECSTDINBUF;
    77 /** Pointer to a standard input buffer. */
    78 typedef TXSEXECSTDINBUF *PTXSEXECSTDINBUF;
    79 
    8045/** Call completion callback for guest calls. */
    8146static void callComplete(VBOXHGCMCALLHANDLE callHandle, int32_t rc)
     
    8651/**
    8752 * Initialise the HGCM service table as much as we need to start the
    88  * service
     53 * service.
     54 *
     55 * @return IPRT status.
    8956 * @param  pTable the table to initialise
    9057 */
    91 void initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)
     58int initTable(VBOXHGCMSVCFNTABLE *pTable, VBOXHGCMSVCHELPERS *pHelpers)
    9259{
    9360    pTable->cbSize = sizeof (VBOXHGCMSVCFNTABLE);
     
    9562    pHelpers->pfnCallComplete = callComplete;
    9663    pTable->pHelpers = pHelpers;
    97 }
     64
     65    return VINF_SUCCESS;
     66}
     67
     68typedef struct CMDHOST
     69{
     70    /** The HGCM command to execute. */
     71    int cmd;
     72    /** Number of parameters. */
     73    int num_parms;
     74    /** The actual parameters. */
     75    const PVBOXHGCMSVCPARM parms;
     76    /** Flag indicating whether we need a connected client for this command. */
     77    bool fNeedsClient;
     78    /** The desired return value from the host. */
     79    int rc;
     80} CMDHOST, *PCMDHOST;
     81
     82typedef struct CMDCLIENT
     83{
     84    /** The client's ID. */
     85    int client_id;
     86    /** The HGCM command to execute. */
     87    int cmd;
     88    /** Number of parameters. */
     89    int num_parms;
     90    /** The actual parameters. */
     91    const PVBOXHGCMSVCPARM parms;
     92    /** The desired return value from the host. */
     93    int rc;
     94} CMDCLIENT, *PCMDCLIENT;
    9895
    9996/**
    100  * Sets up the redirection / pipe / nothing for one of the standard handles.
    101  *
    102  * @returns IPRT status code.  No client replies made.
    103  * @param   pszHowTo            How to set up this standard handle.
    104  * @param   fd                  Which standard handle it is (0 == stdin, 1 ==
    105  *                              stdout, 2 == stderr).
    106  * @param   ph                  The generic handle that @a pph may be set
    107  *                              pointing to.  Always set.
    108  * @param   pph                 Pointer to the RTProcCreateExec argument.
    109  *                              Always set.
    110  * @param   phPipe              Where to return the end of the pipe that we
    111  *                              should service.  Always set.
    112  */
    113 static int guestExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
    114 {
    115     AssertPtr(ph);
    116     AssertPtr(pph);
    117     AssertPtr(phPipe);
    118 
    119     ph->enmType = RTHANDLETYPE_PIPE;
    120     ph->u.hPipe = NIL_RTPIPE;
    121     *pph        = NULL;
    122     *phPipe     = NIL_RTPIPE;
    123 
    124     int rc;
    125 
    126     /*
    127      * Setup a pipe for forwarding to/from the client.
    128      * The ph union struct will be filled with a pipe read/write handle
    129      * to represent the "other" end to phPipe.
    130      */
    131     if (fd == 0) /* stdin? */
    132     {
    133         /* Connect a wrtie pipe specified by phPipe to stdin. */
    134         rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
     97 * Tests the HOST_EXEC_CMD function.
     98 * @returns iprt status value to indicate whether the test went as expected.
     99 * @note    prints its own diagnostic information to stdout.
     100 */
     101static int testHostCmd(const VBOXHGCMSVCFNTABLE *pTable, const PCMDHOST pCmd, uint32_t uNumTests)
     102{
     103    int rc = VINF_SUCCESS;
     104    if (!VALID_PTR(pTable->pfnHostCall))
     105    {
     106        RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Invalid pfnHostCall() pointer\n");
     107        rc = VERR_INVALID_POINTER;
    135108    }
    136     else /* stdout or stderr? */
    137     {
    138         /* Connect a read pipe specified by phPipe to stdout or stderr. */
    139         rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
    140     }
    141     if (RT_FAILURE(rc))
    142         return rc;
    143     ph->enmType = RTHANDLETYPE_PIPE;
    144     *pph = ph;
    145 
    146     return rc;
    147 }
    148 
    149 /**
    150  * Handle an error event on standard input.
    151  *
    152  * @param   hPollSet            The polling set.
    153  * @param   fPollEvt            The event mask returned by RTPollNoResume.
    154  * @param   phStdInW            The standard input pipe handle.
    155  * @param   pStdInBuf           The standard input buffer.
    156  */
    157 static void guestExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
    158                                                PTXSEXECSTDINBUF pStdInBuf)
    159 {
    160     int rc2;
    161     if (pStdInBuf->off < pStdInBuf->cb)
    162     {
    163         rc2 = RTPollSetRemove(hPollSet, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
    164         AssertRC(rc2);
    165     }
    166 
    167     rc2 = RTPollSetRemove(hPollSet, 0 /*TXSEXECHNDID_STDIN*/);
    168     AssertRC(rc2);
    169 
    170     rc2 = RTPipeClose(*phStdInW);
    171     AssertRC(rc2);
    172     *phStdInW = NIL_RTPIPE;
    173 
    174     RTMemFree(pStdInBuf->pch);
    175     pStdInBuf->pch          = NULL;
    176     pStdInBuf->off          = 0;
    177     pStdInBuf->cb           = 0;
    178     pStdInBuf->cbAllocated  = 0;
    179     pStdInBuf->fBitBucket   = true;
    180 }
    181 
    182 /**
    183  * Try write some more data to the standard input of the child.
    184  *
    185  * @returns IPRT status code.
    186  * @param   pStdInBuf           The standard input buffer.
    187  * @param   hStdInW             The standard input pipe.
    188  */
    189 static int guestExecProcWriteStdIn(PTXSEXECSTDINBUF pStdInBuf, RTPIPE hStdInW)
    190 {
    191     size_t  cbToWrite = pStdInBuf->cb - pStdInBuf->off;
    192     size_t  cbWritten;
    193     int     rc = RTPipeWrite(hStdInW, &pStdInBuf->pch[pStdInBuf->off], cbToWrite, &cbWritten);
    194109    if (RT_SUCCESS(rc))
    195110    {
    196         Assert(cbWritten == cbToWrite);
    197         pStdInBuf->off += cbWritten;
    198     }
    199     return rc;
    200 }
    201 
    202 /**
    203  * Handle an event indicating we can write to the standard input pipe of the
    204  * child process.
    205  *
    206  * @param   hPollSet            The polling set.
    207  * @param   fPollEvt            The event mask returned by RTPollNoResume.
    208  * @param   phStdInW            The standard input pipe.
    209  * @param   pStdInBuf           The standard input buffer.
    210  */
    211 static void guestExecProcHandleStdInWritableEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW,
    212                                                   PTXSEXECSTDINBUF pStdInBuf)
    213 {
    214     int rc;
    215     if (!(fPollEvt & RTPOLL_EVT_ERROR))
    216     {
    217         rc = guestExecProcWriteStdIn(pStdInBuf, *phStdInW);
    218         if (RT_FAILURE(rc) && rc != VERR_BAD_PIPE)
     111        for (unsigned i = 0; (i < uNumTests) && RT_SUCCESS(rc); i++)
    219112        {
    220             /** @todo do we need to do something about this error condition? */
    221             AssertRC(rc);
    222         }
    223 
    224         if (pStdInBuf->off < pStdInBuf->cb)
    225         {
    226             rc = RTPollSetRemove(hPollSet, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
    227             AssertRC(rc);
    228         }
    229     }
    230     else
    231         guestExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW, pStdInBuf);
    232 }
    233 
    234 /**
    235  * Handle pending output data or error on standard out, standard error or the
    236  * test pipe.
    237  *
    238  * @returns IPRT status code from client send.
    239  * @param   hPollSet            The polling set.
    240  * @param   fPollEvt            The event mask returned by RTPollNoResume.
    241  * @param   phPipeR             The pipe handle.
    242  * @param   pu32Crc             The current CRC-32 of the stream. (In/Out)
    243  * @param   uHandleId           The handle ID.
    244  * @param   pszOpcode           The opcode for the data upload.
    245  *
    246  * @todo    Put the last 4 parameters into a struct!
    247  */
    248 static int guestExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phPipeR,
    249                                           uint32_t *puCrc32 , uint32_t uHandleId)
    250 {
    251     Log(("guestExecProcHandleOutputEvent: fPollEvt=%#x\n",  fPollEvt));
    252 
    253     /*
    254      * Try drain the pipe before acting on any errors.
    255      */
    256     int rc = VINF_SUCCESS;
    257 
    258     char    abBuf[_64K];
    259     size_t  cbRead;
    260     int     rc2 = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
    261     if (RT_SUCCESS(rc2) && cbRead)
    262     {
    263         Log(("Crc32=%#x ", *puCrc32));
    264 
    265 #if 1
    266             abBuf[cbRead] = '\0';
    267             RTPrintf("%s: %s\n", uHandleId == 1 ? "StdOut: " : "StdErr: ", abBuf);
    268 #endif
    269 
    270         /**puCrc32 = RTCrc32Process(*puCrc32, abBuf, cbRead);
    271         Log(("cbRead=%#x Crc32=%#x \n", cbRead, *puCrc32));
    272         Pkt.uCrc32 = RTCrc32Finish(*puCrc32);*/
    273        /* if (g_fDisplayOutput)
    274         {
    275             if (enmHndId == TXSEXECHNDID_STDOUT)
    276                 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
    277             else if (enmHndId == TXSEXECHNDID_STDERR)
    278                 RTStrmPrintf(g_pStdErr, "%.*s", cbRead, Pkt.abBuf);
    279         }
    280 
    281         rc = txsReplyInternal(&Pkt.Hdr, pszOpcode, cbRead + sizeof(uint32_t));*/
    282 
    283         /* Make sure we go another poll round in case there was too much data
    284            for the buffer to hold. */
    285         fPollEvt &= RTPOLL_EVT_ERROR;
    286     }
    287     else if (RT_FAILURE(rc2))
    288     {
    289         fPollEvt |= RTPOLL_EVT_ERROR;
    290         AssertMsg(rc2 == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
    291     }
    292 
    293     /*
    294      * If an error was raised signalled,
    295      */
    296     if (fPollEvt & RTPOLL_EVT_ERROR)
    297     {
    298         rc2 = RTPollSetRemove(hPollSet, uHandleId);
    299         AssertRC(rc2);
    300 
    301         rc2 = RTPipeClose(*phPipeR);
    302         AssertRC(rc2);
    303         *phPipeR = NIL_RTPIPE;
    304     }
    305     return rc;
    306 }
    307 
    308 /**
    309  * Handle a transport event or successful pfnPollIn() call.
    310  *
    311  * @returns IPRT status code from client send.
    312  * @retval  VINF_EOF indicates ABORT command.
    313  *
    314  * @param   hPollSet            The polling set.
    315  * @param   fPollEvt            The event mask returned by RTPollNoResume.
    316  * @param   idPollHnd           The handle ID.
    317  * @param   hStdInW             The standard input pipe.
    318  * @param   pStdInBuf           The standard input buffer.
    319  */
    320 static int guestExecProcHandleTransportEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, uint32_t idPollHnd,
    321                                              PRTPIPE phStdInW, PTXSEXECSTDINBUF pStdInBuf)
    322 {
    323 
    324     int rc = RTPollSetAddPipe(hPollSet, *phStdInW, RTPOLL_EVT_WRITE, 4 /*TXSEXECHNDID_STDIN_WRITABLE*/);
    325 
    326     return rc;
    327 }
    328 
    329 static int guestExecProcLoop(VBOXHGCMSVCFNTABLE *pTable,
    330                              RTPROCESS hProcess, RTMSINTERVAL cMillies, RTPOLLSET hPollSet,
    331                              RTPIPE hStdInW, RTPIPE hStdOutR, RTPIPE hStdErrR)
    332 {
    333     int                 rc;
    334     int                 rc2;
    335     TXSEXECSTDINBUF     StdInBuf            = { 0, 0, NULL, 0, hStdInW == NIL_RTPIPE, RTCrc32Start() };
    336     uint32_t            uStdOutCrc32        = RTCrc32Start();
    337     uint32_t            uStdErrCrc32        = uStdOutCrc32;
    338     uint64_t const      MsStart             = RTTimeMilliTS();
    339     RTPROCSTATUS        ProcessStatus       = { 254, RTPROCEXITREASON_ABEND };
    340     bool                fProcessAlive       = true;
    341     bool                fProcessTimedOut    = false;
    342     uint64_t            MsProcessKilled     = UINT64_MAX;
    343     bool const          fHavePipes          = hStdInW    != NIL_RTPIPE
    344                                            || hStdOutR   != NIL_RTPIPE
    345                                            || hStdErrR   != NIL_RTPIPE;
    346     RTMSINTERVAL const  cMsPollBase         = hStdInW != NIL_RTPIPE
    347                                               ? 100   /* need to poll for input */
    348                                               : 1000; /* need only poll for process exit and aborts */
    349     RTMSINTERVAL        cMsPollCur          = 0;
    350 
    351     /*
    352      * Before entering the loop, tell the host that we've started the guest
    353      * and that it's now OK to send input to the process.
    354      */
    355     rc = VINF_SUCCESS;
    356 
    357     /*
    358      * Process input, output, the test pipe and client requests.
    359      */
    360     while (RT_SUCCESS(rc))
    361     {
    362         /*
    363          * Wait/Process all pending events.
    364          */
    365         uint32_t idPollHnd;
    366         uint32_t fPollEvt;
    367         rc2 = RTPollNoResume(hPollSet, cMsPollCur, &fPollEvt, &idPollHnd);
    368 
    369         cMsPollCur = 0;                 /* no rest until we've checked everything. */
    370 
    371         if (RT_SUCCESS(rc2))
    372         {
    373             switch (idPollHnd)
     113            RTTestPrintf(g_hTest, RTTESTLVL_INFO, "Testing #%u (cmd: %d, num_parms: %d, parms: 0x%p\n",
     114                         i, pCmd[i].cmd, pCmd[i].num_parms, pCmd[i].parms);
     115
     116            if (pCmd[i].fNeedsClient)
    374117            {
    375                 case 0 /* TXSEXECHNDID_STDIN */:
    376                     guestExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, &hStdInW, &StdInBuf);
    377                     break;
    378 
    379                 case 1 /* TXSEXECHNDID_STDOUT */:
    380                     rc = guestExecProcHandleOutputEvent(hPollSet, fPollEvt, &hStdOutR, &uStdOutCrc32, 1 /* TXSEXECHNDID_STDOUT */);
    381                     break;
    382 
    383                 case 2 /*TXSEXECHNDID_STDERR */:
    384                     rc = guestExecProcHandleOutputEvent(hPollSet, fPollEvt, &hStdErrR, &uStdErrCrc32, 2 /*TXSEXECHNDID_STDERR */);
    385                     break;
    386 
    387                 case 4 /* TXSEXECHNDID_STDIN_WRITABLE */:
    388                     guestExecProcHandleStdInWritableEvent(hPollSet, fPollEvt, &hStdInW, &StdInBuf);
    389                     break;
    390 
    391                 default:
    392                     rc = guestExecProcHandleTransportEvent(hPollSet, fPollEvt, idPollHnd, &hStdInW, &StdInBuf);
    393                     break;
     118                int client_rc = pTable->pfnConnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */);
     119                if (RT_FAILURE(client_rc))
     120                    rc = client_rc;
    394121            }
    395             if (RT_FAILURE(rc) || rc == VINF_EOF)
    396                 break; /* abort command, or client dead or something */
    397             continue;
    398         }
    399 
    400         /*
    401          * Check for incoming data.
    402          */
    403 
    404         /*
    405          * Check for process death.
    406          */
    407         if (fProcessAlive)
    408         {
    409             rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
    410             if (RT_SUCCESS_NP(rc2))
    411             {
    412                 fProcessAlive = false;
    413                 continue;
    414             }
    415             if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
    416                 continue;
    417             if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
    418             {
    419                 fProcessAlive = false;
    420                 ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
    421                 ProcessStatus.iStatus   = 255;
    422                 AssertFailed();
    423             }
    424             else
    425                 AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
    426         }
    427 
    428         /*
    429          * If the process has terminated, we're should head out.
    430          */
    431         if (!fProcessAlive)
    432             break;
    433 
    434         /*
    435          * Check for timed out, killing the process.
    436          */
    437         uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
    438         if (cMillies != RT_INDEFINITE_WAIT)
    439         {
    440             uint64_t u64Now = RTTimeMilliTS();
    441             uint64_t cMsElapsed = u64Now - MsStart;
    442             if (cMsElapsed >= cMillies)
    443             {
    444                 fProcessTimedOut = true;
    445                 if (    MsProcessKilled == UINT64_MAX
    446                     ||  u64Now - MsProcessKilled > 1000)
    447                 {
    448                     if (u64Now - MsProcessKilled > 20*60*1000)
    449                         break; /* give up after 20 mins */
    450                     RTProcTerminate(hProcess);
    451                     MsProcessKilled = u64Now;
    452                     continue;
    453                 }
    454                 cMilliesLeft = 10000;
    455             }
    456             else
    457                 cMilliesLeft = cMillies - (uint32_t)cMsElapsed;
    458         }
    459 
    460         /* Reset the polling interval since we've done all pending work. */
    461         cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
    462     }
    463 
    464     /*
    465      * Try kill the process if it's still alive at this point.
    466      */
    467     if (fProcessAlive)
    468     {
    469         if (MsProcessKilled == UINT64_MAX)
    470         {
    471             MsProcessKilled = RTTimeMilliTS();
    472             RTProcTerminate(hProcess);
    473             RTThreadSleep(500);
    474         }
    475 
    476         for (size_t i = 0; i < 10; i++)
    477         {
    478             rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
    479             if (RT_SUCCESS(rc2))
    480             {
    481                 fProcessAlive = false;
    482                 break;
    483             }
    484             if (i >= 5)
    485                 RTProcTerminate(hProcess);
    486             RTThreadSleep(i >= 5 ? 2000 : 500);
    487         }
    488     }
    489 
    490     /*
    491      * If we don't have a client problem (RT_FAILURE(rc) we'll reply to the
    492      * clients exec packet now.
    493      */
    494     if (RT_SUCCESS(rc))
    495     {
    496         if (     fProcessTimedOut  && !fProcessAlive && MsProcessKilled != UINT64_MAX)
    497         {
    498 
    499         }
    500         else if (fProcessTimedOut  &&  fProcessAlive && MsProcessKilled != UINT64_MAX)
    501         {
    502 
    503         }
    504         /*else if (g_fTerminate && (fProcessAlive || MsProcessKilled != UINT64_MAX))
    505         {
    506 
    507         }*/
    508         else if (fProcessAlive)
    509         {
    510 
    511         }
    512         else if (MsProcessKilled != UINT64_MAX)
    513         {
    514 
    515         }
    516         else if (   ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL
    517                  && ProcessStatus.iStatus   == 0)
    518         {
    519 
    520         }
    521         else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
    522         {
    523 
    524         }
    525         else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
    526         {
    527 
    528         }
    529         else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
    530         {
    531 
    532         }
    533         else
    534         {
    535 
    536         }
    537     }
    538 
    539     RTMemFree(StdInBuf.pch);
    540     return rc;
    541 }
    542 
    543 int guestExecProcess(VBOXHGCMSVCFNTABLE *pTable,
    544                      uint32_t fFlags, const char *pszExecName,
    545                      uint32_t cArgs,    const char * const *papszArgs,
    546                      uint32_t cEnvVars, const char * const *papszEnv,
    547                      const char *pszStdIn, const char *pszStdOut, const char *pszStdErr,
    548                      const char *pszUsername, const char *pszPassword, RTMSINTERVAL cMillies)
    549 {
    550 #if 0
    551     /* Print some info */
    552     RTPrintf("Flags: %u\n"
    553              "# Args: %u\n"
    554              "# Env: %u\n"
    555              "StdIn: %s, StdOut: %s, StdErr: %s\n"
    556              "User: %s, Timelimit: %u\n",
    557                 fFlags, cArgs, cEnvVars,
    558                 pszStdIn, pszStdOut, pszStdErr,
    559                 pszUsername ? pszUsername : "<None>", cMillies);
    560     for (uint32_t i=0; i<cArgs; i++)
    561         RTPrintf("Arg %u: %s\n", i, papszArgs[i]);
    562     for (uint32_t i=0; i<cEnvVars; i++)
    563         RTPrintf("Env %u: %s\n", i, papszEnv[i]);
    564 #endif
    565 
    566     /*
    567      * Create the environment.
    568      */
    569     RTENV hEnv;
    570     int rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
    571     if (RT_SUCCESS(rc))
    572     {
    573         size_t i;
    574         for (i = 0; i < cEnvVars; i++)
    575         {
    576             rc = RTEnvPutEx(hEnv, papszEnv[i]);
    577             if (RT_FAILURE(rc))
    578                 break;
    579         }
    580         if (RT_SUCCESS(rc))
    581         {
    582             /*
    583              * Setup the redirection of the standard stuff.
    584              */
    585             /** @todo consider supporting: gcc stuff.c >file 2>&1.  */
    586             RTHANDLE    hStdIn;
    587             PRTHANDLE   phStdIn;
    588             RTPIPE      hStdInW;
    589             rc = guestExecSetupPipe(0 /* stdin */, &hStdIn, &phStdIn, &hStdInW);
     122
    590123            if (RT_SUCCESS(rc))
    591124            {
    592                 RTHANDLE    hStdOut;
    593                 PRTHANDLE   phStdOut;
    594                 RTPIPE      hStdOutR;
    595                 rc = guestExecSetupPipe(1 /* stdout */, &hStdOut, &phStdOut, &hStdOutR);
    596                 if (RT_SUCCESS(rc))
     125                int host_rc = pTable->pfnHostCall(pTable->pvService,
     126                                                  pCmd[i].cmd,
     127                                                  pCmd[i].num_parms,
     128                                                  pCmd[i].parms);
     129                if (host_rc != pCmd[i].rc)
    597130                {
    598                     RTHANDLE    hStdErr;
    599                     PRTHANDLE   phStdErr;
    600                     RTPIPE      hStdErrR;
    601                     rc = guestExecSetupPipe(2 /* stderr */, &hStdErr, &phStdErr, &hStdErrR);
     131                    RTTestPrintf(g_hTest, RTTESTLVL_FAILURE, "Host call test #%u returned with rc=%Rrc instead of rc=%Rrc\n",
     132                                 i, host_rc, pCmd[i].rc);
     133                    rc = host_rc;
    602134                    if (RT_SUCCESS(rc))
    603                     {
    604                         /*
    605                          * Create a poll set for the pipes and let the
    606                          * transport layer add stuff to it as well.
    607                          */
    608                         RTPOLLSET hPollSet;
    609                         rc = RTPollSetCreate(&hPollSet);
    610                         if (RT_SUCCESS(rc))
    611                         {
    612                             rc = RTPollSetAddPipe(hPollSet, hStdInW, RTPOLL_EVT_ERROR, 0 /* TXSEXECHNDID_STDIN */);
    613                             if (RT_SUCCESS(rc))
    614                                 rc = RTPollSetAddPipe(hPollSet, hStdOutR,   RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 1 /* TXSEXECHNDID_STDOUT */);
    615                             if (RT_SUCCESS(rc))
    616                                 rc = RTPollSetAddPipe(hPollSet, hStdErrR,   RTPOLL_EVT_READ | RTPOLL_EVT_ERROR, 2 /* TXSEXECHNDID_TESTPIPE */);
    617                             if (RT_SUCCESS(rc))
    618                             {
    619                                 RTPROCESS hProcess;
    620                                 rc = RTProcCreateEx(pszExecName, papszArgs, hEnv, 0 /*fFlags*/,
    621                                                     phStdIn, phStdOut, phStdErr,
    622                                                     /*pszUsername, pszPassword,*/ NULL, NULL,
    623                                                     &hProcess);
    624                                 if (RT_SUCCESS(rc))
    625                                 {
    626                                     /*
    627                                      * Close the child ends of any pipes and redirected files.
    628                                      */
    629                                     int rc2 = RTHandleClose(phStdIn);   AssertRC(rc2);
    630                                     phStdIn    = NULL;
    631                                     rc2 = RTHandleClose(phStdOut);  AssertRC(rc2);
    632                                     phStdOut   = NULL;
    633                                     rc2 = RTHandleClose(phStdErr);  AssertRC(rc2);
    634                                     phStdErr   = NULL;
    635 
    636                                     rc = guestExecProcLoop(pTable,
    637                                                            hProcess, cMillies, hPollSet,
    638                                                            hStdInW, hStdOutR, hStdErrR);
    639                                     /*
    640                                      * The handles that are no longer in the set have
    641                                      * been closed by the above call in order to prevent
    642                                      * the guest from getting stuck accessing them.
    643                                      * So, NIL the handles to avoid closing them again.
    644                                      */
    645                                     if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 0 /* stdin */, NULL)))
    646                                         hStdInW = NIL_RTPIPE;
    647                                     if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 1 /* stdout */, NULL)))
    648                                         hStdOutR = NIL_RTPIPE;
    649                                     if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, 2 /* stderr */, NULL)))
    650                                         hStdErrR = NIL_RTPIPE;
    651                                 }
    652                             }
    653                         }
    654                         RTPipeClose(hStdErrR);
    655                         RTHandleClose(phStdErr);
    656                     }
    657                     RTPipeClose(hStdOutR);
    658                     RTHandleClose(phStdOut);
     135                        rc = VERR_INVALID_PARAMETER;
    659136                }
    660                 RTPipeClose(hStdInW);
    661                 RTHandleClose(phStdIn);
    662             }
    663         }
    664         RTEnvDestroy(hEnv);
    665     }
    666 
    667     return rc;
    668 }
    669 
    670 int guestExecHandleCmdExecute(VBOXHGCMSVCFNTABLE *pTable, PVBOXHGCMSVCPARM paParms, uint32_t cParms)
    671 {
    672     char *pcBuf;
    673     uint32_t cbLen;
    674 
    675     if(cParms < 13)
    676         return VERR_INVALID_PARAMETER;
    677 
    678     /* flags */
    679     uint32_t uFlags;
    680     int rc = paParms[1].getUInt32(&uFlags);
    681     if (RT_SUCCESS(rc))
    682     {
    683         /* cmd */
    684         rc = paParms[2].getString(&pcBuf, &cbLen);
    685         char *pszExecName = RTStrDup(pcBuf);
    686 
    687         /* arguments */
    688         if (   pszExecName
    689             && RT_SUCCESS(rc))
    690         {
    691             /* argc */
    692             uint32_t uArgc;
    693             paParms[3].getUInt32(&uArgc);
    694 
    695             /* argv */
    696             char *pcData;
    697             paParms[4].getBuffer((void**)&pcData, &cbLen);
    698             AssertPtr(pcData);
    699 
    700             char **ppaArg;
    701             int iArgs;
    702             rc = RTGetOptArgvFromString(&ppaArg, &iArgs, pcData, NULL);
    703             Assert(uArgc == iArgs);
    704 
    705             /* environment */
    706             if (RT_SUCCESS(rc))
    707             {
    708                 /* envc */
    709                 uint32_t uEnvc;
    710                 paParms[5].getUInt32(&uEnvc);
    711 
    712                 /* envv */
    713                 paParms[6].getBuffer((void**)&pcData, &cbLen);
    714                 AssertPtr(pcData);
    715 
    716                 char **ppaEnv = NULL;
    717                 if (uEnvc)
     137
     138                if (pCmd[i].fNeedsClient)
    718139                {
    719                     ppaEnv = (char**)RTMemAlloc(uEnvc * sizeof(char*));
    720                     AssertPtr(ppaEnv);
    721 
    722                     char *pcCur = pcData;
    723                     uint32_t i = 0;
    724                     while (pcCur < pcData + cbLen)
    725                     {
    726                         if (RTStrAPrintf(&ppaEnv[i++], "%s", pcCur) < 0)
    727                         {
    728                             rc = VERR_NO_MEMORY;
    729                             break;
    730                         }
    731                         pcCur += strlen(pcCur) + 1; /* Skip terminating zero. */
    732                     }
     140                    int client_rc = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */);
     141                    if (RT_SUCCESS(rc))
     142                        rc = client_rc;
    733143                }
    734                 if (RT_SUCCESS(rc))
    735                 {
    736                     /* stdin */
    737                     rc = paParms[7].getString(&pcBuf, &cbLen);
    738                     char *pszStdIn = RTStrDup(pcBuf);
    739                     if (   pszStdIn
    740                         && RT_SUCCESS(rc))
    741                     {
    742                         /* stdout */
    743                         rc = paParms[8].getString(&pcBuf, &cbLen);
    744                         char *pszStdOut = RTStrDup(pcBuf);
    745                         if (   pszStdOut
    746                             && RT_SUCCESS(rc))
    747                         {
    748                             /* stderr */
    749                             rc = paParms[9].getString(&pcBuf, &cbLen);
    750                             char *pszStdErr = RTStrDup(pcBuf);
    751                             if (   pszStdErr
    752                                 && RT_SUCCESS(rc))
    753                             {
    754                                 /* user */
    755                                 rc = paParms[10].getString(&pcBuf, &cbLen);
    756                                 char *pszUser = RTStrDup(pcBuf);
    757                                 if (   pszUser
    758                                     && RT_SUCCESS(rc))
    759                                 {
    760                                     /* password */
    761                                     rc = paParms[11].getString(&pcBuf, &cbLen);
    762                                     char *pszPassword = RTStrDup(pcBuf);
    763                                     if (RT_SUCCESS(rc))
    764                                     {
    765                                         /* time to wait (0 = max, no time limit) */
    766                                         RTMSINTERVAL msTimeLimit;
    767                                         rc = paParms[12].getUInt32(&msTimeLimit);
    768                                         if (RT_SUCCESS(rc))
    769                                         {
    770                                             rc = guestExecProcess(pTable,
    771                                                                   uFlags, pszExecName,
    772                                                                   uArgc, ppaArg,
    773                                                                   uEnvc, ppaEnv,
    774                                                                   pszStdIn, pszStdOut, pszStdIn,
    775                                                                   pszUser, pszPassword,
    776                                                                   msTimeLimit == UINT32_MAX ? RT_INDEFINITE_WAIT : msTimeLimit);
    777                                         }
    778                                     }
    779                                     RTStrFree(pszUser);
    780                                 }
    781                                 RTStrFree(pszStdErr);
    782                             }
    783                             RTStrFree(pszStdOut);
    784                         }
    785                         RTStrFree(pszStdIn);
    786                     }
    787                     for (uint32_t i=0; i<uEnvc; i++)
    788                         RTStrFree(ppaEnv[i]);
    789                     RTMemFree(ppaEnv);
    790                 }
    791                 RTGetOptArgvFree(ppaArg);
    792             }
    793             RTStrFree(pszExecName);
    794         }
    795     }
    796     return rc;
    797 }
    798 
    799 /** Sends the current stdout (stderr later?) data to the host. */
    800 int guestExecSendStdOut(VBOXHGCMSVCFNTABLE *pTable,
    801                         const char *pszStdOut, uint32_t cbStdOut)
    802 {
    803     VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
    804     int command = GUEST_EXEC_SEND_STDOUT;
    805     VBOXHGCMSVCPARM paParms[3];
    806 
    807     if (   pszStdOut == NULL
    808         || cbStdOut == 0)
    809     {
    810         return VERR_INVALID_PARAMETER;
    811     }
    812 
    813     paParms[0].setUInt32(123 /* @todo PID. */);
    814     paParms[1].setUInt32(1 /* @todo Pipe ID. */);
    815     paParms[2].setPointer((void*)pszStdOut, cbStdOut + 1);
    816 
    817     pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command,
    818                     3, paParms);
    819     int rc = VINF_SUCCESS;
    820     if (RT_FAILURE(callHandle.rc))
    821     {
    822         RTPrintf("guestSendStdOut failed with callHandle.rc=%Rrc\n", callHandle.rc);
    823         rc = callHandle.rc;
    824     }
    825     return rc;
    826 }
    827 
    828 /** Gets a new message (command) from the host and does the appropriate action. */
    829 int guestGetHostMsg(VBOXHGCMSVCFNTABLE *pTable)
    830 {
    831     VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
    832     int command = GUEST_GET_HOST_MSG;
    833     VBOXHGCMSVCPARM paParms[13];
    834     /* Work around silly constant issues - we ought to allow passing
    835      * constant strings in the hgcm parameters. */
    836     pTable->pfnCall(pTable->pvService, &callHandle, 0, NULL, command,
    837                     13, paParms);
    838     int rc;
    839     if (RT_FAILURE(callHandle.rc))
    840     {
    841         RTPrintf("guestGetHostMsg failed with callHandle.rc=%Rrc\n", callHandle.rc);
    842         rc = callHandle.rc;
    843     }
    844     else
    845     {
    846         uint32_t uCmd;
    847 
    848         /*
    849          * Parameter 0 *always* specifies the actual command the host wants
    850          * to execute.
    851          */
    852         rc = paParms[0].getUInt32(&uCmd);
    853         if (RT_SUCCESS(rc))
    854         {
    855             switch(uCmd)
    856             {
    857                 case GETHOSTMSG_EXEC_CMD:
    858                     rc = guestExecHandleCmdExecute(pTable, paParms, 13);
    859                     break;
    860 
    861                 case GETHOSTMSG_EXEC_STDIN:
    862                     //rc = guestExecHandleCmdStdIn(pTable, paParms, 12);
    863                     break;
    864 
    865                 default:
    866                     RTPrintf("guestGetHostMsg: Invalid host command: %u\n", uCmd);
    867                     rc = VERR_INVALID_PARAMETER;
    868                     break;
    869144            }
    870145        }
     
    873148}
    874149
    875 /** This represents the execution service thread in VBoxService. */
    876 static DECLCALLBACK(int) guestThread(RTTHREAD Thread, void *pvUser)
    877 {
    878     VBOXHGCMSVCFNTABLE *pTable = (VBOXHGCMSVCFNTABLE*)pvUser;
    879     AssertPtr(pTable);
    880 
    881     int rc = guestGetHostMsg(pTable);
    882     if(RT_FAILURE(rc))
    883         return rc;
    884 
    885     /* We don't remove the current request from the host queue yet,
    886      * so don't try to get a new host message more than once atm. */
    887 
    888     for(;;) /* Run forever atm. */
    889     {
    890         RTThreadSleep(1);
    891         /** @tdo Add graceful shutdown here. */
     150static int testHost(const VBOXHGCMSVCFNTABLE *pTable)
     151{
     152    RTTestSub(g_hTest, "Testing host commands ...");
     153
     154    static VBOXHGCMSVCPARM s_aParms[1];
     155    s_aParms[0].setUInt32(1000 /* Context ID */);
     156
     157    static CMDHOST s_aCmdHostAll[] =
     158    {
     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 },
     162        { HOST_CANCEL_PENDING_WAITS, 1024, 0, false, VERR_NOT_FOUND },
     163        { HOST_CANCEL_PENDING_WAITS, 0, &s_aParms[0], false, VERR_NOT_FOUND },
     164
     165        /** No client connected, valid command. */
     166        { HOST_CANCEL_PENDING_WAITS, 0, 0, false, VERR_NOT_FOUND },
     167
     168        /** Client connected. */
     169        { 1024 /* Not existing command */, 0, 0, true, VERR_NOT_SUPPORTED },
     170        { -1 /* Invalid command */, 0, 0, true, VERR_NOT_SUPPORTED },
     171
     172        /** Client connected, valid parameters given. */
     173        { HOST_CANCEL_PENDING_WAITS, 0, 0, true, VINF_SUCCESS },
     174        { HOST_CANCEL_PENDING_WAITS, 1024, &s_aParms[0], true, VINF_SUCCESS },
     175        { HOST_CANCEL_PENDING_WAITS, 0, &s_aParms[0], true, VINF_SUCCESS},
     176
     177        /** 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
     182        /** Client connected, parameters given. */
     183        { HOST_CANCEL_PENDING_WAITS, 1, &s_aParms[0], true, VINF_SUCCESS },
     184        { HOST_EXEC_CMD, 1, &s_aParms[0], true, VINF_SUCCESS },
     185        { HOST_EXEC_SET_INPUT, 1, &s_aParms[0], true, VINF_SUCCESS },
     186        { HOST_EXEC_GET_OUTPUT, 1, &s_aParms[0], true, VINF_SUCCESS }
     187    };
     188
     189    int rc = testHostCmd(pTable, &s_aCmdHostAll[0], RT_ELEMENTS(s_aCmdHostAll));
     190    RTTestSubDone(g_hTest);
     191    return rc;
     192}
     193
     194static int testClient(const VBOXHGCMSVCFNTABLE *pTable)
     195{
     196    RTTestSub(g_hTest, "Testing client commands ...");
     197
     198    int rc = pTable->pfnConnect(pTable->pvService, 1 /* Client ID */, NULL /* pvClient */);
     199    if (RT_SUCCESS(rc))
     200    {
     201        VBOXHGCMCALLHANDLE_TYPEDEF callHandle = { VINF_SUCCESS };
     202
     203        /* No commands from host yet. */
     204        static VBOXHGCMSVCPARM s_aParmsGuest[8];
     205        pTable->pfnCall(pTable->pvService, &callHandle, 1 /* Client ID */, NULL /* pvClient */,
     206                        GUEST_GET_HOST_MSG, 2, &s_aParmsGuest[0]);
     207        RTTEST_CHECK_RC_RET(g_hTest, callHandle.rc, VINF_SUCCESS, callHandle.rc);
     208
     209        /* Host: Add a dummy command. */
     210        static VBOXHGCMSVCPARM s_aParmsHost[8];
     211        s_aParmsHost[0].setUInt32(1000 /* Context ID */);
     212        s_aParmsHost[1].setString("foo.bar");
     213
     214        rc = pTable->pfnHostCall(pTable->pvService, HOST_EXEC_CMD, 2, &s_aParmsHost[0]);
     215        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);
     255
     256        /* Client: Disconnect again. */
     257        int rc2 = pTable->pfnDisconnect(pTable->pvService, 1000 /* Client ID */, NULL /* pvClient */);
     258        if (RT_SUCCESS(rc))
     259            rc = rc2;
    892260    }
     261
     262    RTTestSubDone(g_hTest);
    893263    return rc;
    894264}
    895265
    896 int hostExecCmd(VBOXHGCMSVCFNTABLE *pTable,
    897                 uint32_t u32Flags,
    898                 const char *pszCmd,
    899                 uint32_t u32Argc,
    900                 const void *pvArgs,
    901                 uint32_t cbArgs,
    902                 uint32_t u32Envc,
    903                 const void *pvEnv,
    904                 uint32_t cbEnv,
    905                 const char *pszStdin,
    906                 const char *pszStdOut,
    907                 const char *pszStdErr,
    908                 const char *pszUser,
    909                 const char *pszPassword,
    910                 uint32_t u32TimeLimit)
    911 {
    912     int command = HOST_EXEC_CMD;
    913     VBOXHGCMSVCPARM paParms[13];
    914 
    915     /** @todo Put some assert macros here! */
    916     paParms[0].setUInt32(HOST_EXEC_CMD);
    917     paParms[1].setUInt32(u32Flags);
    918     paParms[2].setPointer((void*)pszCmd, (uint32_t)strlen(pszCmd) + 1);
    919     paParms[3].setUInt32(u32Argc);
    920     paParms[4].setPointer((void*)pvArgs, cbArgs);
    921     paParms[5].setUInt32(u32Envc);
    922     paParms[6].setPointer((void*)pvEnv, cbEnv);
    923     paParms[7].setPointer((void*)pszStdin, (uint32_t)strlen(pszStdin) + 1);
    924     paParms[8].setPointer((void*)pszStdOut, (uint32_t)strlen(pszStdOut) + 1);
    925     paParms[9].setPointer((void*)pszStdErr, (uint32_t)strlen(pszStdErr) + 1);
    926     paParms[10].setPointer((void*)pszUser, (uint32_t)strlen(pszUser) + 1);
    927     paParms[11].setPointer((void*)pszPassword, (uint32_t)strlen(pszPassword) + 1);
    928     paParms[12].setUInt32(u32TimeLimit);
    929 
    930     int rc = pTable->pfnHostCall(pTable->pvService, command,
    931                                  13, paParms);
    932     if (RT_FAILURE(rc))
    933     {
    934         RTPrintf("hostDoExecute() call failed with rc=%Rrc\n", rc);
    935     }
    936     return rc;
    937 }
    938 
    939 int hostExecGetStatus(VBOXHGCMSVCFNTABLE *pTable)
    940 {
    941     int command = HOST_EXEC_GET_STATUS;
    942     VBOXHGCMSVCPARM paParms[13];
    943 
    944     /** @todo Put some assert macros here! */
    945     paParms[0].setUInt32(0 /* @todo 0=Execute */);
    946 
    947     return 0;
    948 }
    949 
    950 /**
    951  * Test the EXECUTE function.
    952  * @returns iprt status value to indicate whether the test went as expected.
    953  * @note    prints its own diagnostic information to stdout.
    954  */
    955 int testExecute(VBOXHGCMSVCFNTABLE *pTable)
    956 {
    957     RTPrintf("Testing the EXECUTE call.\n");
    958     if (!VALID_PTR(pTable->pfnHostCall))
    959     {
    960         RTPrintf("Invalid pfnHostCall() pointer\n");
    961         return VERR_INVALID_POINTER;
    962     }
    963 
    964     int rc = VINF_SUCCESS;
    965     /*
    966      * The host code (= later Main?) will run in this thread,
    967      * while the client (guest) code will run in another one (= VBoxService in guest).
    968      */
    969     RTTHREAD threadGuest;
    970     rc = RTThreadCreate(&threadGuest, guestThread, pTable /* Save call table. */,
    971                         0, RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, "GUEST");
    972 
    973     /* This is the host code. */
    974     if(RT_SUCCESS(rc))
    975     {
    976         void *pvArgs;
    977         uint32_t cArgs;
    978         uint32_t cbArgs;
    979 
    980         void *pvEnv = NULL;
    981         uint32_t cEnv;
    982         uint32_t cbEnv;
    983 
    984         char szCmdLine[_1K];
    985 
    986         /* Test stdout*/
    987         RTStrPrintf(szCmdLine, sizeof(szCmdLine),
    988                     "%s --test-stdout", g_szImageName); /* Include image name as argv[0]. */
    989         rc = gctrlPrepareExecArgv(szCmdLine, &pvArgs, &cbArgs, &cArgs);
    990         if (RT_SUCCESS(rc))
    991         {
    992             rc = gctrlAddToExecEnvv("VBOX_LOG=all", &pvEnv, &cbEnv, &cEnv);
    993             if (RT_SUCCESS(rc))
    994                 rc = gctrlAddToExecEnvv("VBOX_LOG_DEST=file=c:\\baz\\barfoo.txt", &pvEnv, &cbEnv, &cEnv);
    995             if (RT_SUCCESS(rc))
    996                 rc = gctrlAddToExecEnvv("HOME=iN-WoNdeRlAnd", &pvEnv, &cbEnv, &cEnv);
    997             if (RT_SUCCESS(rc))
    998             {
    999                 rc = hostExecCmd(pTable,
    1000                                  456123,
    1001                                  g_szImageName,
    1002                                  cArgs,
    1003                                  pvArgs,
    1004                                  cbArgs,
    1005                                  cEnv,
    1006                                  pvEnv,
    1007                                  cbEnv,
    1008                                  "|",
    1009                                  "|",
    1010                                  "|",
    1011                                  "vbox",
    1012                                  "password",
    1013                                  UINT32_MAX);
    1014                 RTMemFree(pvEnv);
    1015             }
    1016             RTMemFree(pvArgs);
    1017         }
    1018         if (RT_SUCCESS(rc))
    1019         {
    1020             for(;;)
    1021             {
    1022                 RTThreadSleep(1);
    1023             }
    1024         }
    1025 
    1026         /* Wait for guest thread to finish. */
    1027         int rc2;
    1028         rc = RTThreadWait(threadGuest, RT_INDEFINITE_WAIT, &rc2);
    1029         if (RT_FAILURE(rc))
    1030             RTPrintf("RTThreadWait failed, rc=%Rrc\n", rc);
    1031         else if (RT_FAILURE(rc2))
    1032             RTPrintf("Thread failed, rc2=%Rrc\n", rc2);
    1033     }
    1034     return rc;
    1035 }
    1036 
    1037 int selfTestExecStdOut(int h, int argc, char **argv)
    1038 {
    1039     RTStrmPrintf(g_pStdOut, "This is the first text to StdOut!\n");
    1040     RTStrmPrintf(g_pStdErr, "Hum, this really should be ignored because it's stderr! :-/\n");
    1041     /*for (int i=0; i<10; i++)
    1042     {
    1043         RTStrmPrintf(g_pStdOut, "This is the i=%d to StdOut! Waiting ...\n", i+1);
    1044         RTStrmPrintf(g_pStdErr, "This is the i=%d to StdErr!\n", i+1);
    1045         RTThreadSleep(1000);
    1046 
    1047     }*/
    1048     RTThreadSleep(5000);
    1049     return VINF_SUCCESS;
    1050 }
    1051 
    1052 RTEXITCODE selfTestExecStd(int h, int argc, char **argv)
    1053 {
    1054     int rc = VINF_SUCCESS;
    1055 
    1056 #if 0
    1057     /* Dump arguments and environment. */
    1058     RTENV env;
    1059     rc = RTEnvCreate(&env);
    1060     if (RT_SUCCESS(rc))
    1061     {
    1062         const char *const *pcEnv = RTEnvGetExecEnvP(env);
    1063         RTPrintf("Environment: %s\n" , pcEnv);
    1064         rc = RTEnvDestroy(env);
    1065     }
    1066 #endif
    1067 
    1068     /* Do the test(s) based on the handle number(s). */
    1069     switch (h)
    1070     {
    1071         case 0: /* stdin */
    1072             break;
    1073 
    1074         case 1: /* stdout */
    1075             rc = selfTestExecStdOut(h, argc, argv);
    1076             break;
    1077 
    1078         case 2: /* stderr */
    1079             break;
    1080 
    1081         case 3: /* all std* */
    1082             break;
    1083     }
    1084 
    1085     return RTEXITCODE_SUCCESS;
    1086 }
    1087 
    1088 /**
    1089  * Parses the arguments.
    1090  *
    1091  * @returns Exit code. Exit if this is non-zero or @a *pfExit is set.
    1092  * @param   argc                The number of arguments.
    1093  * @param   argv                The argument vector.
    1094  * @param   pfExit              For indicating exit when the exit code is zero.
    1095  */
    1096 static RTEXITCODE parseArgv(int argc, char **argv, bool *pfExit)
    1097 {
    1098 #if 1
    1099     RTPrintf("ArgC: %d\n", argc);
    1100     for (int i=0; i<argc; i++)
    1101         RTPrintf("ArgV: %s\n", argv[i]);
    1102 #endif
    1103 
    1104     if (argc == 2)
    1105     {
    1106         *pfExit = true;
    1107         if (!strcmp(argv[1], "--test-stdin"))
    1108             return selfTestExecStd(0 /* stdin */, argc, argv);
    1109         else if (!strcmp(argv[1], "--test-stdout"))
    1110             return selfTestExecStd(1 /* stdout */, argc, argv);
    1111         else if (!strcmp(argv[1], "--test-stderr"))
    1112             return selfTestExecStd(2 /* stderr */, argc, argv);
    1113         else if (!strcmp(argv[1], "--test-all"))
    1114             return selfTestExecStd(3 /* all */, argc, argv);
    1115 
    1116         RTPrintf("Unknown test! Exit.\n");
    1117         return RTEXITCODE_FAILURE;
    1118     }
    1119     return RTEXITCODE_SUCCESS;
    1120 }
    1121 
     266/*
     267 * Set environment variable "IPRT_TEST_MAX_LEVEL=all" to get more debug output!
     268 */
    1122269int main(int argc, char **argv)
    1123270{
    1124     int rc = RTR3Init();
    1125     if (RT_FAILURE(rc))
    1126         return RTMsgInitFailure(rc);
    1127 
    1128     /* Save image name for later use. */
    1129     if (!RTProcGetExecutablePath(g_szImageName, sizeof(g_szImageName)))
    1130         return RTMsgErrorExit(RTEXITCODE_FAILURE, "RTProcGetExecutablePath failed\n");
    1131 
     271    RTEXITCODE rcExit  = RTTestInitAndCreate("tstGuestControlSvc", &g_hTest);
     272    if (rcExit != RTEXITCODE_SUCCESS)
     273        return rcExit;
     274    RTTestBanner(g_hTest);
     275
     276    /* Some host info. */
     277    RTTestIPrintf(RTTESTLVL_ALWAYS, "sizeof(void*)=%d\n", sizeof(void*));
     278
     279    /* Do the tests. */
    1132280    VBOXHGCMSVCFNTABLE svcTable;
    1133281    VBOXHGCMSVCHELPERS svcHelpers;
    1134 
    1135     initTable(&svcTable, &svcHelpers);
    1136 
    1137     RTPrintf("PID: %u\n", RTProcSelf ());
    1138 
    1139     bool fExit = false;
    1140     rc = parseArgv(argc, argv, &fExit);
    1141     if (rc || fExit)
    1142         return rc;
    1143 
    1144     /* The function is inside the service, not HGCM. */
    1145     if (RT_FAILURE(VBoxHGCMSvcLoad(&svcTable)))
    1146     {
    1147         RTPrintf("Failed to start the HGCM service.\n");
    1148         return 1;
    1149     }
    1150     if (RT_FAILURE(testExecute(&svcTable)))
    1151         return 1;
    1152     RTPrintf("tstGuestControlSvc: SUCCEEDED.\n");
    1153     return 0;
    1154 }
     282    RTTEST_CHECK_RC_RET(g_hTest, initTable(&svcTable, &svcHelpers), VINF_SUCCESS, 1);
     283
     284    do
     285    {
     286        RTTESTI_CHECK_RC_BREAK(VBoxHGCMSvcLoad(&svcTable), VINF_SUCCESS);
     287
     288        RTTESTI_CHECK_RC_BREAK(testHost(&svcTable), VINF_SUCCESS);
     289
     290        RTTESTI_CHECK_RC_BREAK(svcTable.pfnUnload(svcTable.pvService), VINF_SUCCESS);
     291
     292        RTTESTI_CHECK_RC_BREAK(VBoxHGCMSvcLoad(&svcTable), VINF_SUCCESS);
     293
     294        RTTESTI_CHECK_RC_BREAK(testClient(&svcTable), VINF_SUCCESS);
     295
     296        RTTESTI_CHECK_RC_BREAK(svcTable.pfnUnload(svcTable.pvService), VINF_SUCCESS);
     297
     298    } while (0);
     299
     300    return RTTestSummaryAndDestroy(g_hTest);
     301}
     302
Note: See TracChangeset for help on using the changeset viewer.

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