VirtualBox

Changeset 39279 in vbox


Ignore:
Timestamp:
Nov 11, 2011 5:50:19 PM (13 years ago)
Author:
vboxsync
Message:

VBoxService/GuestCtrl: Overhauled internals, removed pipe double buffering, now using an IPC request/response system for communication (work in progress).

Location:
trunk/src/VBox/Additions/common/VBoxService
Files:
3 deleted
6 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Additions/common/VBoxService/Makefile.kmk

    r38133 r39279  
    6969 VBoxService_SOURCES    += \
    7070        VBoxServiceControl.cpp \
    71         VBoxServiceControlExec.cpp \
    72         VBoxServiceControlExecThread.cpp \
    73         VBoxServicePipeBuf.cpp
     71        VBoxServiceControlExecThread.cpp
    7472endif
    7573
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp

    r38587 r39279  
    3939/** The control interval (milliseconds). */
    4040uint32_t g_ControlInterval = 0;
    41 /** The semaphore we're blocking on. */
    42 static RTSEMEVENTMULTI      g_hControlEvent = NIL_RTSEMEVENTMULTI;
     41/** The semaphore we're blocking our main control thread on. */
     42RTSEMEVENTMULTI             g_hControlEvent = NIL_RTSEMEVENTMULTI;
    4343/** The guest control service client ID. */
    4444static uint32_t             g_GuestControlSvcClientID = 0;
    4545/** How many started guest processes are kept into memory for supplying
    46  *  information to the host. Default is 5 processes. If 0 is specified,
     46 *  information to the host. Default is 25 processes. If 0 is specified,
    4747 *  the maximum number of processes is unlimited. */
    48 uint32_t                    g_GuestControlProcsMaxKept = 5;
     48uint32_t                    g_GuestControlProcsMaxKept = 25;
    4949/** List of guest control threads. */
    5050RTLISTNODE                  g_GuestControlThreads;
    5151/** Critical section protecting g_GuestControlExecThreads. */
    5252RTCRITSECT                  g_GuestControlThreadsCritSect;
     53
     54static int VBoxServiceControlStartAllowed(bool *pbAllowed);
    5355
    5456/** @copydoc VBOXSERVICE::pfnPreInit */
     
    191193
    192194                case HOST_EXEC_CMD:
    193                     rc = VBoxServiceControlExecHandleCmdStartProcess(g_GuestControlSvcClientID, uNumParms);
     195                    rc = VBoxServiceControlHandleCmdStartProc(g_GuestControlSvcClientID, uNumParms);
    194196                    break;
    195197
    196198                case HOST_EXEC_SET_INPUT:
    197199                    /** @todo Make buffer size configurable via guest properties/argv! */
    198                     rc = VBoxServiceControlExecHandleCmdSetInput(g_GuestControlSvcClientID, uNumParms, _1M /* Buffer size */);
     200                    rc = VBoxServiceControlHandleCmdSetInput(g_GuestControlSvcClientID, uNumParms, _1M /* Buffer size */);
    199201                    break;
    200202
    201203                case HOST_EXEC_GET_OUTPUT:
    202                     rc = VBoxServiceControlExecHandleCmdGetOutput(g_GuestControlSvcClientID, uNumParms);
     204                    rc = VBoxServiceControlHandleCmdGetOutput(g_GuestControlSvcClientID, uNumParms);
    203205                    break;
    204206
     
    231233
    232234
     235/**
     236 * Handles starting processes on the guest.
     237 *
     238 * @returns IPRT status code.
     239 * @param   u32ClientId                 The HGCM client session ID.
     240 * @param   uNumParms                   The number of parameters the host is offering.
     241 */
     242int VBoxServiceControlHandleCmdStartProc(uint32_t uClientID, uint32_t uNumParms)
     243{
     244    uint32_t uContextID;
     245    char szCmd[_1K];
     246    uint32_t uFlags;
     247    char szArgs[_1K];
     248    uint32_t uNumArgs;
     249    char szEnv[_64K];
     250    uint32_t cbEnv = sizeof(szEnv);
     251    uint32_t uNumEnvVars;
     252    char szUser[128];
     253    char szPassword[128];
     254    uint32_t uTimeLimitMS;
     255
     256#if 0 /* for valgrind */
     257    RT_ZERO(szCmd);
     258    RT_ZERO(szArgs);
     259    RT_ZERO(szEnv);
     260    RT_ZERO(szUser);
     261    RT_ZERO(szPassword);
     262#endif
     263
     264    if (uNumParms != 11)
     265        return VERR_INVALID_PARAMETER;
     266
     267    int rc = VbglR3GuestCtrlExecGetHostCmd(uClientID,
     268                                           uNumParms,
     269                                           &uContextID,
     270                                           /* Command */
     271                                           szCmd,      sizeof(szCmd),
     272                                           /* Flags */
     273                                           &uFlags,
     274                                           /* Arguments */
     275                                           szArgs,     sizeof(szArgs), &uNumArgs,
     276                                           /* Environment */
     277                                           szEnv, &cbEnv, &uNumEnvVars,
     278                                           /* Credentials */
     279                                           szUser,     sizeof(szUser),
     280                                           szPassword, sizeof(szPassword),
     281                                           /* Timelimit */
     282                                           &uTimeLimitMS);
     283    if (RT_SUCCESS(rc))
     284    {
     285#ifdef DEBUG
     286        VBoxServiceVerbose(3, "ControlExec: Start process szCmd=%s, uFlags=%u, szArgs=%s, szEnv=%s, szUser=%s, szPW=%s, uTimeout=%u\n",
     287                           szCmd, uFlags, uNumArgs ? szArgs : "<None>", uNumEnvVars ? szEnv : "<None>", szUser, szPassword, uTimeLimitMS);
     288#endif
     289        bool fAllowed = false;
     290        int rc = VBoxServiceControlStartAllowed(&fAllowed);
     291        if (RT_FAILURE(rc))
     292            VBoxServiceError("ControlExec: Error determining whether process can be started or not, rc=%Rrc\n", rc);
     293
     294        if (   RT_SUCCESS(rc)
     295            && fAllowed)
     296        {
     297            int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
     298            if (RT_SUCCESS(rc))
     299            {
     300                /** @todo Put the following params into a struct! */
     301                RTLISTNODE *pThreadNode;
     302                rc = VBoxServiceControlThreadStart(uClientID, uContextID,
     303                                                   szCmd, uFlags, szArgs, uNumArgs,
     304                                                   szEnv, cbEnv, uNumEnvVars,
     305                                                   szUser, szPassword, uTimeLimitMS,
     306                                                   &pThreadNode);
     307                if (RT_SUCCESS(rc))
     308                {
     309                    /* Insert thread node into thread list. */
     310                    /*rc =*/ RTListAppend(&g_GuestControlThreads, pThreadNode);
     311                }
     312
     313                int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
     314                if (RT_SUCCESS(rc))
     315                    rc = rc2;
     316            }
     317        }
     318        else /* Process start is not allowed due to policy settings. */
     319        {
     320            /* Tell the host. */
     321            rc = VbglR3GuestCtrlExecReportStatus(uClientID, uContextID, 0 /* PID, invalid. */,
     322                                                 PROC_STS_ERROR, VERR_MAX_PROCS_REACHED,
     323                                                 NULL /* pvData */, 0 /* cbData */);
     324        }
     325    }
     326    else
     327        VBoxServiceError("ControlExec: Failed to retrieve exec start command! Error: %Rrc\n", rc);
     328    return rc;
     329}
     330
     331
     332/**
     333 * Gets output from stdout/stderr of a specified guest process.
     334 *
     335 * @return  IPRT status code.
     336 * @param   uPID                    PID of process to retrieve the output from.
     337 * @param   uHandleId               Stream ID (stdout = 0, stderr = 2) to get the output from.
     338 * @param   uTimeout                Timeout (in ms) to wait for output becoming available.
     339 * @param   pvBuf                   Pointer to a pre-allocated buffer to store the output.
     340 * @param   cbBuf                   Size (in bytes) of the pre-allocated buffer.
     341 * @param   pcbRead                 Pointer to number of bytes read.  Optional.
     342 */
     343int VBoxServiceControlExecGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout,
     344                                    void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
     345{
     346    AssertPtrReturn(pvBuf, VERR_INVALID_POINTER);
     347    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     348    /* pcbRead is optional. */
     349
     350    int rc = VINF_SUCCESS;
     351
     352    VBOXSERVICECTRLREQUEST ctrlRequest;
     353    ctrlRequest.cbData = cbBuf;
     354    ctrlRequest.pvData = (uint8_t*)pvBuf;
     355
     356    switch (uHandleId)
     357    {
     358        case OUTPUT_HANDLE_ID_STDERR:
     359            ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_STDERR_READ;
     360            break;
     361
     362        case OUTPUT_HANDLE_ID_STDOUT:
     363        case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED:
     364            ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_STDOUT_READ;
     365            break;
     366
     367        default:
     368            rc = VERR_INVALID_PARAMETER;
     369            break;
     370    }
     371
     372    if (RT_SUCCESS(rc))
     373        rc = VBoxServiceControlExecThreadPerform(uPID, &ctrlRequest);
     374
     375    if (RT_SUCCESS(rc))
     376    {
     377        if (pcbRead)
     378            *pcbRead = ctrlRequest.cbData;
     379    }
     380
     381    return rc;
     382}
     383
     384
     385/**
     386 * Injects input to a specified running process.
     387 *
     388 * @return  IPRT status code.
     389 * @param   uPID                    PID of process to set the input for.
     390 * @param   fPendingClose           Flag indicating whether this is the last input block sent to the process.
     391 * @param   pvBuf                   Pointer to a buffer containing the actual input data.
     392 * @param   cbBuf                   Size (in bytes) of the input buffer data.
     393 * @param   pcbWritten              Pointer to number of bytes written to the process.  Optional.
     394 */
     395int VBoxServiceControlSetInput(uint32_t uPID, bool fPendingClose,
     396                               void *pvBuf, uint32_t cbBuf,
     397                               uint32_t *pcbWritten)
     398{
     399    AssertPtrReturn(pvBuf, VERR_INVALID_PARAMETER);
     400    AssertReturn(cbBuf, VERR_INVALID_PARAMETER);
     401    /* pcbWritten is optional. */
     402
     403    int rc = VINF_SUCCESS;
     404
     405    VBOXSERVICECTRLREQUEST ctrlRequest;
     406    ctrlRequest.cbData  = cbBuf;
     407    ctrlRequest.pvData  = pvBuf;
     408    ctrlRequest.enmType = fPendingClose
     409                        ? VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF : VBOXSERVICECTRLREQUEST_STDIN_WRITE;
     410    if (RT_SUCCESS(rc))
     411        rc = VBoxServiceControlExecThreadPerform(uPID, &ctrlRequest);
     412
     413    if (RT_SUCCESS(rc))
     414    {
     415        if (pcbWritten)
     416            *pcbWritten = ctrlRequest.cbData;
     417    }
     418
     419    return rc;
     420}
     421
     422
     423/**
     424 * Handles input for a started process by copying the received data into its
     425 * stdin pipe.
     426 *
     427 * @returns IPRT status code.
     428 * @param   u32ClientId                 The HGCM client session ID.
     429 * @param   uNumParms                   The number of parameters the host is offering.
     430 * @param   cMaxBufSize                 The maximum buffer size for retrieving the input data.
     431 */
     432int VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize)
     433{
     434    uint32_t uContextID;
     435    uint32_t uPID;
     436    uint32_t uFlags;
     437    uint32_t cbSize;
     438
     439    AssertReturn(RT_IS_POWER_OF_TWO(cbMaxBufSize), VERR_INVALID_PARAMETER);
     440    uint8_t *pabBuffer = (uint8_t*)RTMemAlloc(cbMaxBufSize);
     441    AssertPtrReturn(pabBuffer, VERR_NO_MEMORY);
     442
     443    uint32_t uStatus = INPUT_STS_UNDEFINED; /* Status sent back to the host. */
     444    uint32_t cbWritten = 0; /* Number of bytes written to the guest. */
     445
     446    /*
     447     * Ask the host for the input data.
     448     */
     449    int rc = VbglR3GuestCtrlExecGetHostCmdInput(u32ClientId, uNumParms,
     450                                                &uContextID, &uPID, &uFlags,
     451                                                pabBuffer, cbMaxBufSize, &cbSize);
     452    if (RT_FAILURE(rc))
     453    {
     454        VBoxServiceError("ControlExec: [PID %u]: Failed to retrieve exec input command! Error: %Rrc\n",
     455                         uPID, rc);
     456    }
     457    else if (cbSize >  cbMaxBufSize)
     458    {
     459        VBoxServiceError("ControlExec: [PID %u]: Too much input received! cbSize=%u, cbMaxBufSize=%u\n",
     460                         uPID, cbSize, cbMaxBufSize);
     461        rc = VERR_INVALID_PARAMETER;
     462    }
     463    else
     464    {
     465        /*
     466         * Is this the last input block we need to deliver? Then let the pipe know ...
     467         */
     468        bool fPendingClose = false;
     469        if (uFlags & INPUT_FLAG_EOF)
     470        {
     471            fPendingClose = true;
     472            VBoxServiceVerbose(4, "ControlExec: [PID %u]: Got last input block of size %u ...\n",
     473                               uPID, cbSize);
     474        }
     475
     476        rc = VBoxServiceControlSetInput(uPID, fPendingClose, pabBuffer,
     477                                                  cbSize, &cbWritten);
     478        VBoxServiceVerbose(4, "ControlExec: [PID %u]: Written input, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
     479                           uPID, rc, uFlags, fPendingClose, cbSize, cbWritten);
     480        if (RT_SUCCESS(rc))
     481        {
     482            if (cbWritten || !cbSize) /* Did we write something or was there anything to write at all? */
     483            {
     484                uStatus = INPUT_STS_WRITTEN;
     485                uFlags = 0;
     486            }
     487        }
     488        else
     489        {
     490            if (rc == VERR_BAD_PIPE)
     491                uStatus = INPUT_STS_TERMINATED;
     492            else if (rc == VERR_BUFFER_OVERFLOW)
     493                uStatus = INPUT_STS_OVERFLOW;
     494        }
     495    }
     496    RTMemFree(pabBuffer);
     497
     498    /*
     499     * If there was an error and we did not set the host status
     500     * yet, then do it now.
     501     */
     502    if (   RT_FAILURE(rc)
     503        && uStatus == INPUT_STS_UNDEFINED)
     504    {
     505        uStatus = INPUT_STS_ERROR;
     506        uFlags = rc;
     507    }
     508    Assert(uStatus > INPUT_STS_UNDEFINED);
     509
     510    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Input processed, CID=%u, uStatus=%u, uFlags=0x%x, cbWritten=%u\n",
     511                       uPID, uContextID, uStatus, uFlags, cbWritten);
     512
     513    /* Note: Since the context ID is unique the request *has* to be completed here,
     514     *       regardless whether we got data or not! Otherwise the progress object
     515     *       on the host never will get completed! */
     516    rc = VbglR3GuestCtrlExecReportStatusIn(u32ClientId, uContextID, uPID,
     517                                           uStatus, uFlags, (uint32_t)cbWritten);
     518
     519    if (RT_FAILURE(rc))
     520        VBoxServiceError("ControlExec: [PID %u]: Failed to report input status! Error: %Rrc\n",
     521                         uPID, rc);
     522    return rc;
     523}
     524
     525
     526/**
     527 * Handles the guest control output command.
     528 *
     529 * @return  IPRT status code.
     530 * @param   u32ClientId     idClient    The HGCM client session ID.
     531 * @param   uNumParms       cParms      The number of parameters the host is
     532 *                                      offering.
     533 */
     534int VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms)
     535{
     536    uint32_t uContextID;
     537    uint32_t uPID;
     538    uint32_t uHandleID;
     539    uint32_t uFlags;
     540
     541    int rc = VbglR3GuestCtrlExecGetHostCmdOutput(u32ClientId, uNumParms,
     542                                                 &uContextID, &uPID, &uHandleID, &uFlags);
     543    if (RT_SUCCESS(rc))
     544    {
     545        uint32_t cbRead = 0;
     546        uint8_t *pBuf = (uint8_t*)RTMemAlloc(_64K);
     547        if (pBuf)
     548        {
     549            rc = VBoxServiceControlExecGetOutput(uPID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
     550                                                 pBuf, _64K /* cbSize */, &cbRead);
     551            if (RT_SUCCESS(rc))
     552                VBoxServiceVerbose(3, "ControlExec: [PID %u]: Got output, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
     553                                   uPID, uContextID, cbRead, uHandleID, uFlags);
     554            else
     555                VBoxServiceError("ControlExec: [PID %u]: Failed to retrieve output, CID=%u, uHandle=%u, rc=%Rrc\n",
     556                                 uPID, uContextID, uHandleID, rc);
     557            /* Note: Since the context ID is unique the request *has* to be completed here,
     558             *       regardless whether we got data or not! Otherwise the progress object
     559             *       on the host never will get completed! */
     560            /* cbRead now contains actual size. */
     561            int rc2 = VbglR3GuestCtrlExecSendOut(u32ClientId, uContextID, uPID, uHandleID, uFlags,
     562                                                 pBuf, cbRead);
     563            if (RT_SUCCESS(rc))
     564                rc = rc2;
     565            RTMemFree(pBuf);
     566        }
     567        else
     568            rc = VERR_NO_MEMORY;
     569    }
     570
     571    if (RT_FAILURE(rc))
     572        VBoxServiceError("ControlExec: [PID %u]: Error handling output command! Error: %Rrc\n",
     573                         uPID, rc);
     574    return rc;
     575}
     576
     577
    233578/** @copydoc VBOXSERVICE::pfnStop */
    234579static DECLCALLBACK(void) VBoxServiceControlStop(void)
     
    236581    VBoxServiceVerbose(3, "Control: Stopping ...\n");
    237582
    238     /** @todo Later, figure what to do if we're in RTProcWait(). it's a very
     583    /** @todo Later, figure what to do if we're in RTProcWait(). It's a very
    239584     *        annoying call since doesn't support timeouts in the posix world. */
    240585    RTSemEventMultiSignal(g_hControlEvent);
     
    253598
    254599
    255 void VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread)
    256 {
    257     AssertPtrReturnVoid(pThread);
    258     ASMAtomicXchgBool(&pThread->fShutdown, true);
    259 }
    260 
    261 
    262 int VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread)
    263 {
    264     AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    265     int rc = VINF_SUCCESS;
    266     if (   pThread->Thread != NIL_RTTHREAD
    267         && !pThread->fShutdown) /* Only shutdown threads which aren't yet. */
    268     {
    269         /* Wait a bit ... */
    270         rc = RTThreadWait(pThread->Thread, 30 * 1000 /* Wait 30 seconds max. */, NULL);
    271     }
    272     return rc;
    273 }
    274 
    275 
    276600static void VBoxServiceControlDestroyThreads(void)
    277601{
     
    282606    {
    283607        /* Signal all threads that we want to shutdown. */
    284         PVBOXSERVICECTRLTHREAD pNode;
    285         RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
    286             VBoxServiceControlThreadSignalShutdown(pNode);
     608        PVBOXSERVICECTRLTHREAD pThread;
     609        RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
     610            VBoxServiceControlThreadSignalShutdown(pThread);
    287611
    288612        /* Wait for threads to shutdown. */
    289         RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
    290         {
    291             int rc2 = VBoxServiceControlThreadWaitForShutdown(pNode);
     613        RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
     614        {
     615            int rc2 = VBoxServiceControlExecThreadShutdown(pThread);
    292616            if (RT_FAILURE(rc2))
    293                 VBoxServiceError("Control: Thread failed to stop; rc2=%Rrc\n", rc2);
    294 
    295             /* Destroy thread specific data. */
    296             switch (pNode->enmType)
    297             {
    298                 case kVBoxServiceCtrlThreadDataExec:
    299                     VBoxServiceControlExecThreadDataDestroy((PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData);
    300                     break;
    301 
    302                 default:
    303                     break;
    304             }
     617                VBoxServiceError("Control: Guest process thread failed to stop; rc2=%Rrc\n", rc2);
    305618        }
    306619
    307620        /* Finally destroy thread list. */
    308         pNode = RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node);
    309         while (pNode)
    310         {
    311             PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pNode->Node, VBOXSERVICECTRLTHREAD, Node);
    312             bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &pNode->Node);
    313 
    314             RTListNodeRemove(&pNode->Node);
    315             RTMemFree(pNode);
     621        pThread = RTListGetFirst(&g_GuestControlThreads, VBOXSERVICECTRLTHREAD, Node);
     622        while (pThread)
     623        {
     624            PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pThread->Node, VBOXSERVICECTRLTHREAD, Node);
     625            bool fLast = RTListNodeIsLast(&g_GuestControlThreads, &pThread->Node);
     626
     627            VBoxServiceControlRemoveThread(pThread);
     628            VBoxServiceControlExecThreadDestroy(pThread);
    316629
    317630            if (fLast)
    318631                break;
    319632
    320             pNode = pNext;
     633            pThread = pNext;
    321634        }
    322635
     
    325638            rc = rc2;
    326639    }
     640
    327641    RTCritSectDelete(&g_GuestControlThreadsCritSect);
    328642}
     
    343657        RTSemEventMultiDestroy(g_hControlEvent);
    344658        g_hControlEvent = NIL_RTSEMEVENTMULTI;
     659    }
     660}
     661
     662
     663static int VBoxServiceControlStartAllowed(bool *pbAllowed)
     664{
     665    AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
     666
     667    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
     668    if (RT_SUCCESS(rc))
     669    {
     670        /*
     671         * Check if we're respecting our memory policy by checking
     672         * how many guest processes are started and served already.
     673         */
     674        bool fLimitReached = false;
     675        if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
     676        {
     677            uint32_t uProcsRunning = 0;
     678            uint32_t uProcsStopped = 0;
     679            PVBOXSERVICECTRLTHREAD pThread;
     680            RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
     681            {
     682    // THREAD LOCKING!!
     683                Assert(pThread->fStarted != pThread->fStopped);
     684                if (pThread->fStarted)
     685                    uProcsRunning++;
     686                else if (pThread->fStopped)
     687                    uProcsStopped++;
     688                else
     689                    AssertMsgFailed(("ControlExec: Guest process neither started nor stopped!?\n"));
     690            }
     691
     692            VBoxServiceVerbose(2, "ControlExec: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
     693                               g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped);
     694
     695            int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1);
     696            if (iProcsLeft < 0)
     697            {
     698                VBoxServiceVerbose(3, "ControlExec: Maximum running guest processes reached (%u)\n",
     699                                   g_GuestControlProcsMaxKept);
     700                fLimitReached = true;
     701            }
     702        }
     703
     704        *pbAllowed = !fLimitReached;
     705
     706        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
     707        if (RT_SUCCESS(rc))
     708            rc = rc2;
     709    }
     710
     711    return rc;
     712}
     713
     714
     715/**
     716 * Finds a (formerly) started process given by its PID.
     717 *
     718 * @return  PVBOXSERVICECTRLTHREAD      Process structure if found, otherwise NULL.
     719 * @param   uPID                        PID to search for.
     720 */
     721const PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadByPID(uint32_t uPID)
     722{
     723    PVBOXSERVICECTRLTHREAD pNode = NULL;
     724    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
     725    if (RT_SUCCESS(rc))
     726    {
     727        PVBOXSERVICECTRLTHREAD pNodeCur;
     728        RTListForEach(&g_GuestControlThreads, pNodeCur, VBOXSERVICECTRLTHREAD, Node)
     729        {
     730            if (pNodeCur->uPID == uPID)
     731            {
     732                pNode = pNodeCur;
     733                break;
     734            }
     735        }
     736
     737        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
     738        if (RT_SUCCESS(rc))
     739            rc = rc2;
     740    }
     741
     742    return pNode;
     743}
     744
     745
     746void VBoxServiceControlRemoveThread(PVBOXSERVICECTRLTHREAD pThread)
     747{
     748    if (!pThread)
     749        return;
     750
     751    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
     752    if (RT_SUCCESS(rc))
     753    {
     754        RTListNodeRemove(&pThread->Node);
     755
     756        int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
     757        if (RT_SUCCESS(rc))
     758            rc = rc2;
    345759    }
    346760}
     
    357771    "Host-driven Guest Control",
    358772    /* pszUsage. */
    359     "              [--control-interval <ms>] [--control-procs-max-kept <x>]"
     773    "              [--control-interval <ms>] [--control-procs-max-kept <x>]\n"
     774    "              [--control-procs-mem-std[in|out|err] <KB>]"
    360775    ,
    361776    /* pszOptions. */
     
    364779    "    --control-procs-max-kept\n"
    365780    "                            Specifies how many started guest processes are\n"
    366     "                            kept into memory to work with.\n"
     781    "                            kept into memory to work with. Default is 25.\n"
    367782    ,
    368783    /* methods */
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.cpp

    r38870 r39279  
    11/* $Id$ */
    22/** @file
    3  * VBoxServiceControlExecThread - Thread for an executed guest process.
     3 * VBoxServiceControlExecThread - Thread for every started guest process.
    44 */
    55
     
    2222#include <iprt/asm.h>
    2323#include <iprt/assert.h>
     24#include <iprt/env.h>
    2425#include <iprt/getopt.h>
     26#include <iprt/handle.h>
    2527#include <iprt/mem.h>
     28#include <iprt/path.h>
    2629#include <iprt/pipe.h>
     30#include <iprt/poll.h>
     31#include <iprt/process.h>
    2732#include <iprt/semaphore.h>
    2833#include <iprt/string.h>
    29 
     34#include <iprt/thread.h>
     35
     36#include <VBox/VBoxGuestLib.h>
    3037#include <VBox/HostServices/GuestControlSvc.h>
    3138
    32 #include "VBoxServicePipeBuf.h"
     39#include "VBoxServiceInternal.h"
    3340#include "VBoxServiceControlExecThread.h"
    3441
    35 extern uint32_t g_GuestControlProcsMaxKept;
    36 extern RTLISTNODE g_GuestControlThreads;
    37 extern RTCRITSECT g_GuestControlThreadsCritSect;
    38 
    39 PVBOXSERVICECTRLTHREAD vboxServiceControlExecThreadGetByPID(uint32_t uPID);
    40 int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread);
     42using namespace guestControl;
     43
     44/* Internal functions. */
     45int vboxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREAD pData, uint32_t uPID);
     46void vboxServiceControlExecThreadFree(PVBOXSERVICECTRLTHREAD pData);
     47int vboxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread);
    4148
    4249/**
     
    7380
    7481    pThread->fShutdown = false;
    75     pThread->fStarted = false;
    76     pThread->fStopped = false;
     82    pThread->fStarted  = false;
     83    pThread->fStopped  = false;
    7784
    7885    pThread->uContextID = u32ContextID;
    7986    /* ClientID will be assigned when thread is started! */
    8087
    81     /* Specific stuff. */
    82     PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREADDATAEXEC));
    83     if (pData == NULL)
    84         return VERR_NO_MEMORY;
    85 
    86     pData->uPID = 0; /* Don't have a PID yet. */
    87     pData->pszCmd = RTStrDup(pszCmd);
    88     pData->uFlags = uFlags;
    89     pData->uNumEnvVars = 0;
    90     pData->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
     88    int rc = RTCritSectInit(&pThread->CritSect);
     89    if (RT_FAILURE(rc))
     90        return rc;
     91
     92    rc = RTSemEventMultiCreate(&pThread->RequestEvent);
     93    AssertRCReturn(rc, rc);
     94
     95    pThread->uPID = 0; /* Don't have a PID yet. */
     96    pThread->pszCmd = RTStrDup(pszCmd);
     97    pThread->uFlags = uFlags;
     98    pThread->uTimeLimitMS = (   uTimeLimitMS == UINT32_MAX
     99                             || uTimeLimitMS == 0)
     100                          ? RT_INDEFINITE_WAIT : uTimeLimitMS;
    91101
    92102    /* Prepare argument list. */
    93     int rc = RTGetOptArgvFromString(&pData->papszArgs, (int*)&pData->uNumArgs,
    94                                     (uNumArgs > 0) ? pszArgs : "", NULL);
     103    pThread->uNumArgs = 0; /* Initialize in case of RTGetOptArgvFromString() is failing ... */
     104    rc = RTGetOptArgvFromString(&pThread->papszArgs, (int*)&pThread->uNumArgs,
     105                                (uNumArgs > 0) ? pszArgs : "", NULL);
    95106    /* Did we get the same result? */
    96     Assert(uNumArgs == pData->uNumArgs);
     107    Assert(uNumArgs == pThread->uNumArgs);
    97108
    98109    if (RT_SUCCESS(rc))
    99110    {
    100111        /* Prepare environment list. */
     112        pThread->uNumEnvVars = 0;
    101113        if (uNumEnvVars)
    102114        {
    103             pData->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*));
    104             AssertPtr(pData->papszEnv);
    105             pData->uNumEnvVars = uNumEnvVars;
     115            pThread->papszEnv = (char **)RTMemAlloc(uNumEnvVars * sizeof(char*));
     116            AssertPtr(pThread->papszEnv);
     117            pThread->uNumEnvVars = uNumEnvVars;
    106118
    107119            const char *pszCur = pszEnv;
     
    116128                    break;
    117129                }
    118                 int cbStr = RTStrAPrintf(&pData->papszEnv[i++], "%s", pszCur);
     130                int cbStr = RTStrAPrintf(&pThread->papszEnv[i++], "%s", pszCur);
    119131                if (cbStr < 0)
    120132                {
     
    127139        }
    128140
    129         pData->pszUser = RTStrDup(pszUser);
    130         pData->pszPassword = RTStrDup(pszPassword);
    131         pData->uTimeLimitMS = uTimeLimitMS;
    132 
    133         /* Adjust time limit value. */
    134         pData->uTimeLimitMS = (   uTimeLimitMS == UINT32_MAX
    135                                || uTimeLimitMS == 0)
    136                             ? RT_INDEFINITE_WAIT : uTimeLimitMS;
    137 
    138         /* Init buffers. */
    139         rc = VBoxServicePipeBufInit(&pData->stdOut, VBOXSERVICECTRLPIPEID_STDOUT,
    140                                     false /*fNeedNotificationPipe*/);
     141        /* User management. */
     142        pThread->pszUser     = RTStrDup(pszUser);
     143        pThread->pszPassword = RTStrDup(pszPassword);
     144    }
     145
     146    if (RT_FAILURE(rc)) /* Clean up on failure. */
     147        vboxServiceControlExecThreadFree(pThread);
     148    return rc;
     149}
     150
     151
     152/**
     153 * TODO
     154 *
     155 * @param   pThread
     156 */
     157void VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread)
     158{
     159    AssertPtrReturnVoid(pThread);
     160
     161    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Signalling shutdown ...\n",
     162                       pThread->uPID);
     163
     164    /* First, set the shutdown flag. */
     165    ASMAtomicXchgBool(&pThread->fShutdown, true);
     166
     167    VBOXSERVICECTRLREQUEST ctrlRequest;
     168    RT_ZERO(ctrlRequest);
     169    ctrlRequest.enmType = VBOXSERVICECTRLREQUEST_QUIT;
     170
     171    int rc = VBoxServiceControlExecThreadPerform(pThread->uPID, &ctrlRequest);
     172    if (RT_FAILURE(rc))
     173        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Sending quit request failed with rc=%Rrc\n",
     174                           pThread->uPID, rc);
     175}
     176
     177
     178static int vboxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread)
     179{
     180    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
     181    int rc = VINF_SUCCESS;
     182    if (   pThread->Thread != NIL_RTTHREAD
     183        && !pThread->fShutdown) /* Only shutdown threads which aren't yet. */
     184    {
     185        /* Wait a bit ... */
     186        rc = RTThreadWait(pThread->Thread, 30 * 1000 /* Wait 30 seconds max. */, NULL);
     187    }
     188    return rc;
     189}
     190
     191
     192/**
     193 * Frees an allocated thread data structure along with all its allocated parameters.
     194 *
     195 * @param   pThread        Pointer to thread data to free.
     196 */
     197static void vboxServiceControlExecThreadFree(const PVBOXSERVICECTRLTHREAD pThread)
     198{
     199    if (pThread)
     200    {
     201        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Freeing thread data ...\n",
     202                           pThread->uPID);
     203
     204        RTStrFree(pThread->pszCmd);
     205        if (pThread->uNumEnvVars)
     206        {
     207            for (uint32_t i = 0; i < pThread->uNumEnvVars; i++)
     208                RTStrFree(pThread->papszEnv[i]);
     209            RTMemFree(pThread->papszEnv);
     210        }
     211        RTGetOptArgvFree(pThread->papszArgs);
     212        RTStrFree(pThread->pszUser);
     213        RTStrFree(pThread->pszPassword);
     214    }
     215}
     216
     217
     218/**
     219 * Closes the stdin pipe of a guest process.
     220 *
     221 * @return  IPRT status code.
     222 * @param   hPollSet            The polling set.
     223 * @param   phStdInW            The standard input pipe handle.
     224 */
     225static int VBoxServiceControlExecProcCloseStdIn(RTPOLLSET hPollSet, PRTPIPE phStdInW)
     226{
     227    int rc = RTPollSetRemove(hPollSet, VBOXSERVICECTRLPIPEID_STDIN);
     228    if (rc != VERR_POLL_HANDLE_ID_NOT_FOUND)
     229        AssertRC(rc);
     230
     231    rc = RTPipeClose(*phStdInW);
     232    AssertRC(rc);
     233    *phStdInW = NIL_RTPIPE;
     234
     235    return rc;
     236}
     237
     238
     239/**
     240 * Handle an error event on standard input.
     241 *
     242 * @return  IPRT status code.
     243 * @param   hPollSet            The polling set.
     244 * @param   fPollEvt            The event mask returned by RTPollNoResume.
     245 * @param   phStdInW            The standard input pipe handle.
     246 */
     247static int VBoxServiceControlExecProcHandleStdInErrorEvent(RTPOLLSET hPollSet, uint32_t fPollEvt, PRTPIPE phStdInW)
     248{
     249    NOREF(fPollEvt);
     250
     251    return VBoxServiceControlExecProcCloseStdIn(hPollSet, phStdInW);
     252}
     253
     254
     255/**
     256 * Handle pending output data or error on standard out or standard error.
     257 *
     258 * @returns IPRT status code from client send.
     259 * @param   hPollSet            The polling set.
     260 * @param   fPollEvt            The event mask returned by RTPollNoResume.
     261 * @param   phPipeR             The pipe handle.
     262 * @param   idPollHnd           The pipe ID to handle.
     263 *
     264 */
     265static int VBoxServiceControlExecProcHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt,
     266                                                       PRTPIPE phPipeR, uint32_t idPollHnd)
     267{
     268    int rc = VINF_SUCCESS;
     269
     270#if 0
     271    if (fPollEvt & RTPOLL_EVT_READ)
     272    {
     273        /** @todo Later: Notify the host about the read operation! */
     274
     275        /* Make sure we go another poll round in case there was too much data
     276           for the buffer to hold. */
     277        fPollEvt &= RTPOLL_EVT_ERROR;
     278
     279        /* Remove read event from poll set and just poll for errors from now on.
     280         * This is necessary for doing a RTPipeRead. */
     281        rc = RTPollSetEventsChange(hPollSet, idPollHnd, RTPOLL_EVT_ERROR);
     282        AssertRC(rc);
     283    }
     284#endif
     285
     286#ifdef DEBUG_andy
     287    VBoxServiceVerbose(4, "ControlExec: HandleOutputEvent fPollEvt=0x%x, idPollHnd=%u\n",
     288                       fPollEvt, idPollHnd);
     289#endif
     290
     291    /*
     292     * If an error was raised signalled
     293     */
     294    if (fPollEvt & RTPOLL_EVT_ERROR)
     295    {
     296        rc = RTPollSetRemove(hPollSet, idPollHnd);
     297        AssertRC(rc);
     298
     299        rc = RTPipeClose(*phPipeR);
     300        AssertRC(rc);
     301        *phPipeR = NIL_RTPIPE;
     302    }
     303
     304    return rc;
     305}
     306
     307
     308int VBoxServiceControlExecProcHandleIPCNotify(RTPOLLSET hPollSet, uint32_t fPollEvt,
     309                                              PRTPIPE phNotificationPipeR)
     310{
     311#ifdef DEBUG_andy
     312    VBoxServiceVerbose(4, "ControlExec: HandleIPCNotify\n");
     313#endif
     314    /* Drain the notification pipe. */
     315    uint8_t abBuf[8];
     316    size_t cbIgnore;
     317    int rc = RTPipeRead(*phNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
     318    if (RT_FAILURE(rc))
     319        VBoxServiceError("ControlExec: Draining IPC notification pipe failed with rc=%Rrc\n", rc);
     320    return rc;
     321}
     322
     323
     324static int VBoxServiceControlExecHandleIPCRequest(RTPOLLSET hPollSet, uint32_t fPollEvt,
     325                                                  PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR,
     326                                                  PVBOXSERVICECTRLREQUEST pRequest)
     327{
     328#ifdef DEBUG_andy
     329    VBoxServiceVerbose(4, "ControlExec: HandleIPCRequest\n");
     330#endif
     331
     332    AssertPtrReturn(phStdInW, VERR_INVALID_POINTER);
     333    AssertPtrReturn(phStdOutR, VERR_INVALID_POINTER);
     334    AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER);
     335    AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
     336
     337    int rc = VINF_SUCCESS;
     338    int rcReq = VINF_SUCCESS; /* Actual request result. */
     339
     340    switch (pRequest->enmType)
     341    {
     342        case VBOXSERVICECTRLREQUEST_QUIT: /* Main control asked us to quit. */
     343            /** @todo Check for some conditions to check to
     344             *        veto quitting. */
     345            pRequest->rc = VINF_SUCCESS;
     346            break;
     347
     348        case VBOXSERVICECTRLREQUEST_STDIN_WRITE:
     349            /* Fall through is intentional. */
     350        case VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF:
     351        {
     352            AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
     353            AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER);
     354
     355            size_t cbWritten = 0;
     356            if (*phStdInW != NIL_RTPIPE)
     357            {
     358                rcReq = RTPipeWrite(*phStdInW,
     359                                    pRequest->pvData, pRequest->cbData, &cbWritten);
     360            }
     361            else
     362                rcReq = VINF_EOF;
     363
     364            /* If this is the last write we need to close the stdin pipe on our
     365             * end and remove it from the poll set. */
     366            if (VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF == pRequest->enmType)
     367                rc = VBoxServiceControlExecProcCloseStdIn(hPollSet, phStdInW);
     368
     369            /* Reqport back actual data written (if any). */
     370            pRequest->cbData = cbWritten;
     371            break;
     372        }
     373
     374        case VBOXSERVICECTRLREQUEST_STDOUT_READ:
     375            /* Fall through is intentional. */
     376        case VBOXSERVICECTRLREQUEST_STDERR_READ:
     377        {
     378            AssertPtrReturn(pRequest->pvData, VERR_INVALID_POINTER);
     379            AssertReturn(pRequest->cbData, VERR_INVALID_PARAMETER);
     380
     381            PRTPIPE pPipeR = pRequest->enmType == VBOXSERVICECTRLREQUEST_STDERR_READ
     382                           ? phStdErrR : phStdOutR;
     383            AssertPtr(pPipeR);
     384
     385            size_t cbRead = 0;
     386            if (*pPipeR != NIL_RTPIPE)
     387            {
     388                rcReq = RTPipeRead(*pPipeR,
     389                                   pRequest->pvData, pRequest->cbData, &cbRead);
     390                if (rcReq == VERR_BROKEN_PIPE)
     391                    rcReq = VINF_EOF;
     392            }
     393            else
     394                rcReq = VINF_EOF;
     395
     396            /* Report back actual data read (if any). */
     397            pRequest->cbData = cbRead;
     398            break;
     399        }
     400
     401        default:
     402            rcReq = VERR_INVALID_PARAMETER;
     403            break;
     404    }
     405
     406    pRequest->rc = RT_SUCCESS(rc)
     407                 ? rcReq : rc;
     408
     409    VBoxServiceVerbose(4, "ControlExec: Handled IPC request with rcReq=%Rrc, enmType=%u, cbData=%u\n",
     410                       rcReq, pRequest->enmType, pRequest->cbData);
     411    return rc;
     412}
     413
     414
     415/**
     416 * Execution loop which runs in a dedicated per-started-process thread and
     417 * handles all pipe input/output and signalling stuff.
     418 *
     419 * @return  IPRT status code.
     420 * @param   pThread                     The process' thread handle.
     421 * @param   hProcess                    The actual process handle.
     422 * @param   cMsTimeout                  Time limit (in ms) of the process' life time.
     423 * @param   hPollSet                    The poll set to use.
     424 * @param   hStdInW                     Handle to the process' stdin write end.
     425 * @param   hStdOutR                    Handle to the process' stdout read end.
     426 * @param   hStdErrR                    Handle to the process' stderr read end.
     427 */
     428static int VBoxServiceControlExecProcLoop(PVBOXSERVICECTRLTHREAD pThread,
     429                                          RTPROCESS hProcess, RTMSINTERVAL cMsTimeout, RTPOLLSET hPollSet,
     430                                          PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR)
     431{
     432    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
     433    AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
     434    AssertPtrReturn(phStdOutR, VERR_INVALID_PARAMETER);
     435    AssertPtrReturn(phStdErrR, VERR_INVALID_PARAMETER);
     436
     437    int                         rc;
     438    int                         rc2;
     439    uint64_t const              MsStart             = RTTimeMilliTS();
     440    RTPROCSTATUS                ProcessStatus       = { 254, RTPROCEXITREASON_ABEND };
     441    bool                        fProcessAlive       = true;
     442    bool                        fProcessTimedOut    = false;
     443    uint64_t                    MsProcessKilled     = UINT64_MAX;
     444    RTMSINTERVAL const          cMsPollBase         = *phStdInW != NIL_RTPIPE
     445                                                      ? 100   /* Need to poll for input. */
     446                                                      : 1000; /* Need only poll for process exit and aborts. */
     447    RTMSINTERVAL                cMsPollCur          = 0;
     448
     449    /*
     450     * Assign PID to thread data.
     451     * Also check if there already was a thread with the same PID and shut it down -- otherwise
     452     * the first (stale) entry will be found and we get really weird results!
     453     */
     454    rc = vboxServiceControlExecThreadAssignPID(pThread, hProcess);
     455    if (RT_FAILURE(rc))
     456    {
     457        VBoxServiceError("ControlExec: Unable to assign PID to new thread, rc=%Rrc\n", rc);
     458        return rc;
     459    }
     460
     461    /*
     462     * Before entering the loop, tell the host that we've started the guest
     463     * and that it's now OK to send input to the process.
     464     */
     465    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Process started, CID=%u, User=%s\n",
     466                       pThread->uPID, pThread->uContextID, pThread->pszUser);
     467    rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
     468                                         pThread->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
     469                                         NULL /* pvData */, 0 /* cbData */);
     470
     471    /*
     472     * Process input, output, the test pipe and client requests.
     473     */
     474    while (   RT_SUCCESS(rc)
     475           && RT_UNLIKELY(!pThread->fShutdown))
     476    {
     477        /*
     478         * Wait/Process all pending events.
     479         */
     480        uint32_t idPollHnd;
     481        uint32_t fPollEvt;
     482        rc2 = RTPollNoResume(hPollSet, /*cMsPollCur*/ RT_INDEFINITE_WAIT, &fPollEvt, &idPollHnd);
     483        if (pThread->fShutdown)
     484            continue;
     485
     486        cMsPollCur = 0; /* No rest until we've checked everything. */
     487
     488        if (RT_SUCCESS(rc2))
     489        {
     490            /*VBoxServiceVerbose(4, "ControlExec: [PID %u}: RTPollNoResume idPollHnd=%u\n",
     491                                 pThread->uPID, idPollHnd);*/
     492            switch (idPollHnd)
     493            {
     494                case VBOXSERVICECTRLPIPEID_STDIN:
     495                    rc = VBoxServiceControlExecProcHandleStdInErrorEvent(hPollSet, fPollEvt, phStdInW);
     496                    break;
     497
     498                case VBOXSERVICECTRLPIPEID_STDOUT:
     499                    rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt,
     500                                                                     phStdOutR, idPollHnd);
     501                    break;
     502
     503                case VBOXSERVICECTRLPIPEID_STDERR:
     504                    rc = VBoxServiceControlExecProcHandleOutputEvent(hPollSet, fPollEvt,
     505                                                                     phStdErrR, idPollHnd);
     506                    break;
     507
     508                case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
     509                    rc = VBoxServiceControlExecProcHandleIPCNotify(hPollSet, fPollEvt,
     510                                                                   &pThread->hNotificationPipeR);
     511                    /* Fall through is intentional. */
     512                default:
     513                    /* Handle IPC requests. */
     514                    if (RT_SUCCESS(rc))
     515                    {
     516                        rc = VBoxServiceControlExecHandleIPCRequest(hPollSet, fPollEvt,
     517                                                                    phStdInW, phStdOutR, phStdErrR,
     518                                                                    pThread->pRequest);
     519                    }
     520
     521                    /* If we were asked to terminate do so ... */
     522                    if (pThread->pRequest->enmType == VBOXSERVICECTRLREQUEST_QUIT)
     523                        rc = VINF_EOF;
     524
     525                    /* In any case, regardless of the result, we notify
     526                     * the main guest control to unblock it. */
     527                    int rc2 = RTSemEventMultiSignal(pThread->RequestEvent);
     528                    AssertRC(rc2);
     529                    /* No access to pRequest here anymore -- could be out of scope
     530                     * or modified already! */
     531                    break;
     532            }
     533            if (RT_FAILURE(rc) || rc == VINF_EOF)
     534                break; /* Abort command, or client dead or something. */
     535
     536            /* Only notification pipe left? Then there's nothing to poll for anymore really.
     537             * Bail out .. */
     538            if (RTPollSetGetCount(hPollSet) > 1)
     539                continue;
     540        }
     541
     542        /*
     543         * Check for process death.
     544         */
     545        if (fProcessAlive)
     546        {
     547            rc2 = RTProcWaitNoResume(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
     548            if (RT_SUCCESS_NP(rc2))
     549            {
     550                fProcessAlive = false;
     551                continue;
     552            }
     553            if (RT_UNLIKELY(rc2 == VERR_INTERRUPTED))
     554                continue;
     555            if (RT_UNLIKELY(rc2 == VERR_PROCESS_NOT_FOUND))
     556            {
     557                fProcessAlive = false;
     558                ProcessStatus.enmReason = RTPROCEXITREASON_ABEND;
     559                ProcessStatus.iStatus   = 255;
     560                AssertFailed();
     561            }
     562            else
     563                AssertMsg(rc2 == VERR_PROCESS_RUNNING, ("%Rrc\n", rc2));
     564        }
     565
     566        /*
     567         * If the process has terminated, we're should head out.
     568         */
     569        if (!fProcessAlive)
     570            break;
     571
     572        /*
     573         * Check for timed out, killing the process.
     574         */
     575        uint32_t cMilliesLeft = RT_INDEFINITE_WAIT;
     576        if (cMsTimeout != RT_INDEFINITE_WAIT)
     577        {
     578            uint64_t u64Now = RTTimeMilliTS();
     579            uint64_t cMsElapsed = u64Now - MsStart;
     580            if (cMsElapsed >= cMsTimeout)
     581            {
     582                VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...",
     583                                   pThread->uPID, cMsElapsed, cMsTimeout);
     584
     585                fProcessTimedOut = true;
     586                if (    MsProcessKilled == UINT64_MAX
     587                    ||  u64Now - MsProcessKilled > 1000)
     588                {
     589                    if (u64Now - MsProcessKilled > 20*60*1000)
     590                        break; /* Give up after 20 mins. */
     591                    RTProcTerminate(hProcess);
     592                    MsProcessKilled = u64Now;
     593                    continue;
     594                }
     595                cMilliesLeft = 10000;
     596            }
     597            else
     598                cMilliesLeft = cMsTimeout - (uint32_t)cMsElapsed;
     599        }
     600
     601        /* Reset the polling interval since we've done all pending work. */
     602        cMsPollCur = cMilliesLeft >= cMsPollBase ? cMsPollBase : cMilliesLeft;
     603
     604        /*
     605         * Need to exit?
     606         */
     607        if (pThread->fShutdown)
     608            break;
     609    }
     610
     611    /*
     612     * Try kill the process if it's still alive at this point.
     613     */
     614    if (fProcessAlive)
     615    {
     616        if (MsProcessKilled == UINT64_MAX)
     617        {
     618            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Is still alive and not killed yet\n",
     619                               pThread->uPID);
     620
     621            MsProcessKilled = RTTimeMilliTS();
     622            RTProcTerminate(hProcess);
     623            RTThreadSleep(500);
     624        }
     625
     626        for (size_t i = 0; i < 10; i++)
     627        {
     628            VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Waiting to exit ...\n",
     629                               pThread->uPID, i + 1);
     630            rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
     631            if (RT_SUCCESS(rc2))
     632            {
     633                VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Exited\n",
     634                                   pThread->uPID, i + 1);
     635                fProcessAlive = false;
     636                break;
     637            }
     638            if (i >= 5)
     639            {
     640                VBoxServiceVerbose(4, "ControlExec: [PID %u]: Kill attempt %d/10: Trying to terminate ...\n",
     641                                   pThread->uPID, i + 1);
     642                RTProcTerminate(hProcess);
     643            }
     644            RTThreadSleep(i >= 5 ? 2000 : 500);
     645        }
     646
     647        if (fProcessAlive)
     648            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Could not be killed\n", pThread->uPID);
     649    }
     650
     651    /*
     652     * If we don't have a client problem (RT_FAILURE(rc)) we'll reply to the
     653     * clients exec packet now.
     654     */
     655    if (RT_SUCCESS(rc))
     656    {
     657        /* Mark this thread as stopped and do some action required for stopping ... */
     658        //VBoxServiceControlExecThreadCleanup(pThread);
     659
     660        uint32_t uStatus = PROC_STS_UNDEFINED;
     661        uint32_t uFlags = 0;
     662
     663        if (     fProcessTimedOut  && !fProcessAlive && MsProcessKilled != UINT64_MAX)
     664        {
     665            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out and got killed\n",
     666                               pThread->uPID);
     667            uStatus = PROC_STS_TOK;
     668        }
     669        else if (fProcessTimedOut  &&  fProcessAlive && MsProcessKilled != UINT64_MAX)
     670        {
     671            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Timed out and did *not* get killed\n",
     672                               pThread->uPID);
     673            uStatus = PROC_STS_TOA;
     674        }
     675        else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
     676        {
     677            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Got terminated because system/service is about to shutdown\n",
     678                               pThread->uPID);
     679            uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
     680            uFlags = pThread->uFlags; /* Return handed-in execution flags back to the host. */
     681        }
     682        else if (fProcessAlive)
     683        {
     684            VBoxServiceError("ControlExec: [PID %u]: Is alive when it should not!\n",
     685                             pThread->uPID);
     686        }
     687        else if (MsProcessKilled != UINT64_MAX)
     688        {
     689            VBoxServiceError("ControlExec: [PID %u]: Has been killed when it should not!\n",
     690                             pThread->uPID);
     691        }
     692        else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
     693        {
     694            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_NORMAL (%u)\n",
     695                               pThread->uPID, ProcessStatus.iStatus);
     696
     697            uStatus = PROC_STS_TEN;
     698            uFlags = ProcessStatus.iStatus;
     699        }
     700        else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
     701        {
     702            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_SIGNAL (%u)\n",
     703                               pThread->uPID, ProcessStatus.iStatus);
     704
     705            uStatus = PROC_STS_TES;
     706            uFlags = ProcessStatus.iStatus;
     707        }
     708        else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
     709        {
     710            VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended with RTPROCEXITREASON_ABEND (%u)\n",
     711                               pThread->uPID, ProcessStatus.iStatus);
     712
     713            uStatus = PROC_STS_TEA;
     714            uFlags = ProcessStatus.iStatus;
     715        }
     716        else
     717            VBoxServiceVerbose(1, "ControlExec: [PID %u]: Handling process status %u not implemented\n",
     718                               pThread->uPID, ProcessStatus.enmReason);
     719
     720        rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
     721                                             pThread->uPID, uStatus, uFlags,
     722                                             NULL /* pvData */, 0 /* cbData */);
     723        if (RT_FAILURE(rc))
     724            VBoxServiceError("ControlExec: [PID %u]: Error reporting final status to host; rc=%Rrc\n",
     725                             pThread->uPID, rc);
     726
     727        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Ended, CID=%u, Status=%u, Flags=%u\n",
     728                           pThread->uPID, pThread->uContextID, uStatus, uFlags);
     729
     730        VBoxServiceVerbose(3, "ControlExec: [PID %u]: Process loop ended with rc=%Rrc\n",
     731                           pThread->uPID, rc);
     732    }
     733    else
     734        VBoxServiceError("ControlExec: [PID %u]: Loop failed with rc=%Rrc\n",
     735                         pThread->uPID, rc);
     736    return rc;
     737}
     738
     739
     740/**
     741 * Sets up the redirection / pipe / nothing for one of the standard handles.
     742 *
     743 * @returns IPRT status code.  No client replies made.
     744 * @param   fd                  Which standard handle it is (0 == stdin, 1 ==
     745 *                              stdout, 2 == stderr).
     746 * @param   ph                  The generic handle that @a pph may be set
     747 *                              pointing to.  Always set.
     748 * @param   pph                 Pointer to the RTProcCreateExec argument.
     749 *                              Always set.
     750 * @param   phPipe              Where to return the end of the pipe that we
     751 *                              should service.  Always set.
     752 */
     753static int VBoxServiceControlExecSetupPipe(int fd, PRTHANDLE ph, PRTHANDLE *pph, PRTPIPE phPipe)
     754{
     755    AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
     756    AssertPtrReturn(pph, VERR_INVALID_PARAMETER);
     757    AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
     758
     759    ph->enmType = RTHANDLETYPE_PIPE;
     760    ph->u.hPipe = NIL_RTPIPE;
     761    *pph        = NULL;
     762    *phPipe     = NIL_RTPIPE;
     763
     764    int rc;
     765
     766    /*
     767     * Setup a pipe for forwarding to/from the client.
     768     * The ph union struct will be filled with a pipe read/write handle
     769     * to represent the "other" end to phPipe.
     770     */
     771    if (fd == 0) /* stdin? */
     772    {
     773        /* Connect a wrtie pipe specified by phPipe to stdin. */
     774        rc = RTPipeCreate(&ph->u.hPipe, phPipe, RTPIPE_C_INHERIT_READ);
     775    }
     776    else /* stdout or stderr? */
     777    {
     778        /* Connect a read pipe specified by phPipe to stdout or stderr. */
     779        rc = RTPipeCreate(phPipe, &ph->u.hPipe, RTPIPE_C_INHERIT_WRITE);
     780    }
     781    if (RT_FAILURE(rc))
     782        return rc;
     783    ph->enmType = RTHANDLETYPE_PIPE;
     784    *pph = ph;
     785
     786    return rc;
     787}
     788
     789
     790/**
     791 * Expands a file name / path to its real content. This only works on Windows
     792 * for now (e.g. translating "%TEMP%\foo.exe" to "C:\Windows\Temp" when starting
     793 * with system / administrative rights).
     794 *
     795 * @return  IPRT status code.
     796 * @param   pszPath                     Path to resolve.
     797 * @param   pszExpanded                 Pointer to string to store the resolved path in.
     798 * @param   cbExpanded                  Size (in bytes) of string to store the resolved path.
     799 */
     800static int VBoxServiceControlExecMakeFullPath(const char *pszPath, char *pszExpanded, size_t cbExpanded)
     801{
     802    int rc = VINF_SUCCESS;
     803#ifdef RT_OS_WINDOWS
     804    if (!ExpandEnvironmentStrings(pszPath, pszExpanded, cbExpanded))
     805        rc = RTErrConvertFromWin32(GetLastError());
     806#else
     807    /* No expansion for non-Windows yet. */
     808    rc = RTStrCopy(pszExpanded, cbExpanded, pszPath);
     809#endif
     810#ifdef DEBUG
     811    VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecMakeFullPath: %s -> %s\n",
     812                       pszPath, pszExpanded);
     813#endif
     814    return rc;
     815}
     816
     817
     818/**
     819 * Resolves the full path of a specified executable name. This function also
     820 * resolves internal VBoxService tools to its appropriate executable path + name.
     821 *
     822 * @return  IPRT status code.
     823 * @param   pszFileName                 File name to resovle.
     824 * @param   pszResolved                 Pointer to a string where the resolved file name will be stored.
     825 * @param   cbResolved                  Size (in bytes) of resolved file name string.
     826 */
     827static int VBoxServiceControlExecResolveExecutable(const char *pszFileName, char *pszResolved, size_t cbResolved)
     828{
     829    int rc = VINF_SUCCESS;
     830
     831    /* Search the path of our executable. */
     832    char szVBoxService[RTPATH_MAX];
     833    if (RTProcGetExecutablePath(szVBoxService, sizeof(szVBoxService)))
     834    {
     835        char *pszExecResolved = NULL;
     836        if (   (g_pszProgName && RTStrICmp(pszFileName, g_pszProgName) == 0)
     837            || !RTStrICmp(pszFileName, VBOXSERVICE_NAME))
     838        {
     839            /* We just want to execute VBoxService (no toolbox). */
     840            pszExecResolved = RTStrDup(szVBoxService);
     841        }
     842        else /* Nothing to resolve, copy original. */
     843            pszExecResolved = RTStrDup(pszFileName);
     844        AssertPtr(pszExecResolved);
     845
     846        rc = VBoxServiceControlExecMakeFullPath(pszExecResolved, pszResolved, cbResolved);
     847#ifdef DEBUG
     848        VBoxServiceVerbose(3, "ControlExec: VBoxServiceControlExecResolveExecutable: %s -> %s\n",
     849                           pszFileName, pszResolved);
     850#endif
     851        RTStrFree(pszExecResolved);
     852    }
     853    return rc;
     854}
     855
     856
     857/**
     858 * Constructs the argv command line by resolving environment variables
     859 * and relative paths.
     860 *
     861 * @return IPRT status code.
     862 * @param  pszArgv0         First argument (argv0), either original or modified version.
     863 * @param  papszArgs        Original argv command line from the host, starting at argv[1].
     864 * @param  ppapszArgv       Pointer to a pointer with the new argv command line.
     865 *                          Needs to be freed with RTGetOptArgvFree.
     866 */
     867static int VBoxServiceControlExecPrepareArgv(const char *pszArgv0,
     868                                             const char * const *papszArgs, char ***ppapszArgv)
     869{
     870/** @todo RTGetOptArgvToString converts to MSC quoted string, while
     871 *        RTGetOptArgvFromString takes bourne shell according to the docs...
     872 * Actually, converting to and from here is a very roundabout way of prepending
     873 * an entry (pszFilename) to an array (*ppapszArgv). */
     874    int rc = VINF_SUCCESS;
     875    char *pszNewArgs = NULL;
     876    if (pszArgv0)
     877        rc = RTStrAAppend(&pszNewArgs, pszArgv0);
     878    if (   RT_SUCCESS(rc)
     879        && papszArgs)
     880
     881    {
     882        char *pszArgs;
     883        rc = RTGetOptArgvToString(&pszArgs, papszArgs,
     884                                  RTGETOPTARGV_CNV_QUOTE_MS_CRT); /* RTGETOPTARGV_CNV_QUOTE_BOURNE_SH */
    141885        if (RT_SUCCESS(rc))
    142886        {
    143             rc = VBoxServicePipeBufInit(&pData->stdErr, VBOXSERVICECTRLPIPEID_STDERR,
    144                                         false /*fNeedNotificationPipe*/);
     887            rc = RTStrAAppend(&pszNewArgs, " ");
    145888            if (RT_SUCCESS(rc))
    146                 rc = VBoxServicePipeBufInit(&pData->stdIn, VBOXSERVICECTRLPIPEID_STDIN,
    147                                             true /*fNeedNotificationPipe*/);
     889                rc = RTStrAAppend(&pszNewArgs, pszArgs);
     890        }
     891    }
     892
     893    if (RT_SUCCESS(rc))
     894    {
     895        int iNumArgsIgnored;
     896        rc = RTGetOptArgvFromString(ppapszArgv, &iNumArgsIgnored,
     897                                    pszNewArgs ? pszNewArgs : "", NULL /* Use standard separators. */);
     898    }
     899
     900    if (pszNewArgs)
     901        RTStrFree(pszNewArgs);
     902    return rc;
     903}
     904
     905
     906/**
     907 * Helper function to create/start a process on the guest.
     908 *
     909 * @return  IPRT status code.
     910 * @param   pszExec                     Full qualified path of process to start (without arguments).
     911 * @param   papszArgs                   Pointer to array of command line arguments.
     912 * @param   hEnv                        Handle to environment block to use.
     913 * @param   fFlags                      Process execution flags.
     914 * @param   phStdIn                     Handle for the process' stdin pipe.
     915 * @param   phStdOut                    Handle for the process' stdout pipe.
     916 * @param   phStdErr                    Handle for the process' stderr pipe.
     917 * @param   pszAsUser                   User name (account) to start the process under.
     918 * @param   pszPassword                 Password of the specified user.
     919 * @param   phProcess                   Pointer which will receive the process handle after
     920 *                                      successful process start.
     921 */
     922static int VBoxServiceControlExecCreateProcess(const char *pszExec, const char * const *papszArgs, RTENV hEnv, uint32_t fFlags,
     923                                               PCRTHANDLE phStdIn, PCRTHANDLE phStdOut, PCRTHANDLE phStdErr, const char *pszAsUser,
     924                                               const char *pszPassword, PRTPROCESS phProcess)
     925{
     926    AssertPtrReturn(pszExec, VERR_INVALID_PARAMETER);
     927    AssertPtrReturn(papszArgs, VERR_INVALID_PARAMETER);
     928    AssertPtrReturn(phProcess, VERR_INVALID_PARAMETER);
     929
     930    int  rc = VINF_SUCCESS;
     931    char szExecExp[RTPATH_MAX];
     932#ifdef RT_OS_WINDOWS
     933    /*
     934     * If sysprep should be executed do this in the context of VBoxService, which
     935     * (usually, if started by SCM) has administrator rights. Because of that a UI
     936     * won't be shown (doesn't have a desktop).
     937     */
     938    if (RTStrICmp(pszExec, "sysprep") == 0)
     939    {
     940        /* Use a predefined sysprep path as default. */
     941        char szSysprepCmd[RTPATH_MAX] = "C:\\sysprep\\sysprep.exe";
     942
     943        /*
     944         * On Windows Vista (and up) sysprep is located in "system32\\sysprep\\sysprep.exe",
     945         * so detect the OS and use a different path.
     946         */
     947        OSVERSIONINFOEX OSInfoEx;
     948        RT_ZERO(OSInfoEx);
     949        OSInfoEx.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
     950        if (    GetVersionEx((LPOSVERSIONINFO) &OSInfoEx)
     951            &&  OSInfoEx.dwPlatformId == VER_PLATFORM_WIN32_NT
     952            &&  OSInfoEx.dwMajorVersion >= 6 /* Vista or later */)
     953        {
     954            rc = RTEnvGetEx(RTENV_DEFAULT, "windir", szSysprepCmd, sizeof(szSysprepCmd), NULL);
     955            if (RT_SUCCESS(rc))
     956                rc = RTPathAppend(szSysprepCmd, sizeof(szSysprepCmd), "system32\\sysprep\\sysprep.exe");
    148957        }
    149958
    150959        if (RT_SUCCESS(rc))
    151960        {
    152             pThread->enmType = kVBoxServiceCtrlThreadDataExec;
    153             pThread->pvData = pData;
    154         }
    155     }
    156 
     961            char **papszArgsExp;
     962            rc = VBoxServiceControlExecPrepareArgv(szSysprepCmd /* argv0 */, papszArgs, &papszArgsExp);
     963            if (RT_SUCCESS(rc))
     964            {
     965                rc = RTProcCreateEx(szSysprepCmd, papszArgsExp, hEnv, 0 /* fFlags */,
     966                                    phStdIn, phStdOut, phStdErr, NULL /* pszAsUser */,
     967                                    NULL /* pszPassword */, phProcess);
     968            }
     969            RTGetOptArgvFree(papszArgsExp);
     970        }
     971        return rc;
     972    }
     973#endif /* RT_OS_WINDOWS */
     974
     975#ifdef VBOXSERVICE_TOOLBOX
     976    if (RTStrStr(pszExec, "vbox_") == pszExec)
     977    {
     978        /* We want to use the internal toolbox (all internal
     979         * tools are starting with "vbox_" (e.g. "vbox_cat"). */
     980        rc = VBoxServiceControlExecResolveExecutable(VBOXSERVICE_NAME, szExecExp, sizeof(szExecExp));
     981    }
     982    else
     983    {
     984#endif
     985        /*
     986         * Do the environment variables expansion on executable and arguments.
     987         */
     988        rc = VBoxServiceControlExecResolveExecutable(pszExec, szExecExp, sizeof(szExecExp));
     989#ifdef VBOXSERVICE_TOOLBOX
     990    }
     991#endif
     992    if (RT_SUCCESS(rc))
     993    {
     994        char **papszArgsExp;
     995        rc = VBoxServiceControlExecPrepareArgv(pszExec /* Always use the unmodified executable name as argv0. */,
     996                                               papszArgs /* Append the rest of the argument vector (if any). */, &papszArgsExp);
     997        if (RT_SUCCESS(rc))
     998        {
     999            uint32_t uProcFlags = 0;
     1000            if (fFlags)
     1001            {
     1002                /* Process Main flag "ExecuteProcessFlag_Hidden". */
     1003                if (fFlags & RT_BIT(2))
     1004                    uProcFlags |= RTPROC_FLAGS_HIDDEN;
     1005                /* Process Main flag "ExecuteProcessFlag_NoProfile". */
     1006                if (fFlags & RT_BIT(3))
     1007                    uProcFlags |= RTPROC_FLAGS_NO_PROFILE;
     1008            }
     1009
     1010            /* If no user name specified run with current credentials (e.g.
     1011             * full service/system rights). This is prohibited via official Main API!
     1012             *
     1013             * Otherwise use the RTPROC_FLAGS_SERVICE to use some special authentication
     1014             * code (at least on Windows) for running processes as different users
     1015             * started from our system service. */
     1016            if (*pszAsUser)
     1017                uProcFlags |= RTPROC_FLAGS_SERVICE;
     1018#ifdef DEBUG
     1019            VBoxServiceVerbose(3, "ControlExec: Command: %s\n", szExecExp);
     1020            for (size_t i = 0; papszArgsExp[i]; i++)
     1021                VBoxServiceVerbose(3, "ControlExec:\targv[%ld]: %s\n", i, papszArgsExp[i]);
     1022#endif
     1023            /* Do normal execution. */
     1024            rc = RTProcCreateEx(szExecExp, papszArgsExp, hEnv, uProcFlags,
     1025                                phStdIn, phStdOut, phStdErr,
     1026                                *pszAsUser   ? pszAsUser   : NULL,
     1027                                *pszPassword ? pszPassword : NULL,
     1028                                phProcess);
     1029            RTGetOptArgvFree(papszArgsExp);
     1030        }
     1031    }
     1032    return rc;
     1033}
     1034
     1035/**
     1036 * The actual worker routine (lopp) for a started guest process.
     1037 *
     1038 * @return  IPRT status code.
     1039 * @param   PVBOXSERVICECTRLTHREAD         Thread data associated with a started process.
     1040 */
     1041static DECLCALLBACK(int) VBoxServiceControlExecProcessWorker(PVBOXSERVICECTRLTHREAD pThread)
     1042{
     1043    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
     1044    VBoxServiceVerbose(3, "ControlExec: Thread of process \"%s\" started\n", pThread->pszCmd);
     1045
     1046    int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
    1571047    if (RT_FAILURE(rc))
    158         VBoxServiceControlExecThreadDataDestroy(pData);
     1048    {
     1049        VBoxServiceError("ControlExec: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
     1050        RTThreadUserSignal(RTThreadSelf());
     1051        return rc;
     1052    }
     1053
     1054    bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
     1055
     1056    /*
     1057     * Create the environment.
     1058     */
     1059    RTENV hEnv;
     1060    rc = RTEnvClone(&hEnv, RTENV_DEFAULT);
     1061    if (RT_SUCCESS(rc))
     1062    {
     1063        size_t i;
     1064        for (i = 0; i < pThread->uNumEnvVars && pThread->papszEnv; i++)
     1065        {
     1066            rc = RTEnvPutEx(hEnv, pThread->papszEnv[i]);
     1067            if (RT_FAILURE(rc))
     1068                break;
     1069        }
     1070        if (RT_SUCCESS(rc))
     1071        {
     1072            /*
     1073             * Setup the redirection of the standard stuff.
     1074             */
     1075            /** @todo consider supporting: gcc stuff.c >file 2>&1.  */
     1076            RTHANDLE    hStdIn;
     1077            PRTHANDLE   phStdIn;
     1078            rc = VBoxServiceControlExecSetupPipe(0 /*STDIN_FILENO*/, &hStdIn, &phStdIn, &pThread->pipeStdInW);
     1079            if (RT_SUCCESS(rc))
     1080            {
     1081                RTHANDLE    hStdOut;
     1082                PRTHANDLE   phStdOut;
     1083                RTPIPE      hStdOutR;
     1084                rc = VBoxServiceControlExecSetupPipe(1 /*STDOUT_FILENO*/, &hStdOut, &phStdOut, &hStdOutR);
     1085                if (RT_SUCCESS(rc))
     1086                {
     1087                    RTHANDLE    hStdErr;
     1088                    PRTHANDLE   phStdErr;
     1089                    RTPIPE      hStdErrR;
     1090                    rc = VBoxServiceControlExecSetupPipe(2 /*STDERR_FILENO*/, &hStdErr, &phStdErr, &hStdErrR);
     1091                    if (RT_SUCCESS(rc))
     1092                    {
     1093                        /*
     1094                         * Create a poll set for the pipes and let the
     1095                         * transport layer add stuff to it as well.
     1096                         */
     1097                        RTPOLLSET hPollSet;
     1098                        rc = RTPollSetCreate(&hPollSet);
     1099                        if (RT_SUCCESS(rc))
     1100                        {
     1101                            /* Stdin. */
     1102                            if (RT_SUCCESS(rc))
     1103                                rc = RTPollSetAddPipe(hPollSet, pThread->pipeStdInW, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDIN);
     1104                            /* Stdout. */
     1105                            if (RT_SUCCESS(rc))
     1106                                rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT);
     1107                            /* Stderr. */
     1108                            if (RT_SUCCESS(rc))
     1109                                rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR);
     1110                            /* IPC notification pipe. */
     1111                            if (RT_SUCCESS(rc))
     1112                                rc = RTPipeCreate(&pThread->hNotificationPipeR, &pThread->hNotificationPipeW, 0 /* Flags */);
     1113                            if (RT_SUCCESS(rc))
     1114                                rc = RTPollSetAddPipe(hPollSet, pThread->hNotificationPipeR, RTPOLL_EVT_READ, VBOXSERVICECTRLPIPEID_IPC_NOTIFY);
     1115
     1116                            if (RT_SUCCESS(rc))
     1117                            {
     1118                                RTPROCESS hProcess;
     1119                                rc = VBoxServiceControlExecCreateProcess(pThread->pszCmd, pThread->papszArgs, hEnv, pThread->uFlags,
     1120                                                                         phStdIn, phStdOut, phStdErr,
     1121                                                                         pThread->pszUser, pThread->pszPassword,
     1122                                                                         &hProcess);
     1123                                if (RT_FAILURE(rc))
     1124                                    VBoxServiceError("ControlExec: Error starting process, rc=%Rrc\n", rc);
     1125                                /*
     1126                                 * Tell the control thread that it can continue
     1127                                 * spawning services. This needs to be done after the new
     1128                                 * process has been started because otherwise signal handling
     1129                                 * on (Open) Solaris does not work correctly (see #5068).
     1130                                 */
     1131                                int rc2 = RTThreadUserSignal(RTThreadSelf());
     1132                                if (RT_FAILURE(rc2))
     1133                                    rc = rc2;
     1134                                fSignalled = true;
     1135
     1136                                if (RT_SUCCESS(rc))
     1137                                {
     1138                                    /*
     1139                                     * Close the child ends of any pipes and redirected files.
     1140                                     */
     1141                                    rc2 = RTHandleClose(phStdIn);   AssertRC(rc2);
     1142                                    phStdIn    = NULL;
     1143                                    rc2 = RTHandleClose(phStdOut);  AssertRC(rc2);
     1144                                    phStdOut   = NULL;
     1145                                    rc2 = RTHandleClose(phStdErr);  AssertRC(rc2);
     1146                                    phStdErr   = NULL;
     1147
     1148                                    /* Enter the process loop. */
     1149                                    rc = VBoxServiceControlExecProcLoop(pThread,
     1150                                                                        hProcess, pThread->uTimeLimitMS, hPollSet,
     1151                                                                        &pThread->pipeStdInW, &hStdOutR, &hStdErrR);
     1152
     1153                                    /* Before cleaning up everything else, remove the thread from our thread list. */
     1154                                    VBoxServiceControlRemoveThread(pThread);
     1155
     1156                                    /*
     1157                                     * The handles that are no longer in the set have
     1158                                     * been closed by the above call in order to prevent
     1159                                     * the guest from getting stuck accessing them.
     1160                                     * So, NIL the handles to avoid closing them again.
     1161                                     */
     1162                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_IPC_NOTIFY, NULL)))
     1163                                    {
     1164                                        pThread->hNotificationPipeR = NIL_RTPIPE;
     1165                                        pThread->hNotificationPipeW = NIL_RTPIPE;
     1166                                    }
     1167                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDERR, NULL)))
     1168                                        hStdErrR = NIL_RTPIPE;
     1169                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDOUT, NULL)))
     1170                                        hStdOutR = NIL_RTPIPE;
     1171                                    if (RT_FAILURE(RTPollSetQueryHandle(hPollSet, VBOXSERVICECTRLPIPEID_STDIN, NULL)))
     1172                                        pThread->pipeStdInW = NIL_RTPIPE;
     1173                                }
     1174                                else /* Something went wrong; report error! */
     1175                                {
     1176                                    VBoxServiceError("ControlExec: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
     1177                                                     pThread->pszCmd, pThread->uContextID, rc);
     1178
     1179                                    rc2 = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID, pThread->uPID,
     1180                                                                          PROC_STS_ERROR, rc,
     1181                                                                          NULL /* pvData */, 0 /* cbData */);
     1182                                    if (RT_FAILURE(rc2))
     1183                                        VBoxServiceError("ControlExec: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
     1184                                                         rc2, rc);
     1185                                }
     1186                            }
     1187                            RTPollSetDestroy(hPollSet);
     1188
     1189                            RTPipeClose(pThread->hNotificationPipeR);
     1190                            pThread->hNotificationPipeR = NIL_RTPIPE;
     1191                            RTPipeClose(pThread->hNotificationPipeW);
     1192                            pThread->hNotificationPipeW = NIL_RTPIPE;
     1193                        }
     1194                        RTPipeClose(hStdErrR);
     1195                        hStdErrR = NIL_RTPIPE;
     1196                        RTHandleClose(phStdErr);
     1197                    }
     1198                    RTPipeClose(hStdOutR);
     1199                    hStdOutR = NIL_RTPIPE;
     1200                    RTHandleClose(phStdOut);
     1201                }
     1202                RTPipeClose(pThread->pipeStdInW);
     1203                RTHandleClose(phStdIn);
     1204            }
     1205        }
     1206        RTEnvDestroy(hEnv);
     1207    }
     1208
     1209    VBoxServiceVerbose(3, "ControlExec: [PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n",
     1210                       pThread->uPID, pThread->pszCmd, rc);
     1211
     1212    /* Disconnect from guest control service. */
     1213    VbglR3GuestCtrlDisconnect(pThread->uClientID);
     1214    pThread->uClientID = 0;
     1215
     1216    ASMAtomicXchgBool(&pThread->fStarted, false);
     1217    ASMAtomicXchgBool(&pThread->fStopped, true);
     1218
     1219    int rc2 = VBoxServiceControlExecThreadShutdown(pThread);
     1220    if (RT_SUCCESS(rc2))
     1221        VBoxServiceControlExecThreadDestroy(pThread);
     1222
     1223    /*
     1224     * If something went wrong signal the user event so that others don't wait
     1225     * forever on this thread.
     1226     */
     1227    if (RT_FAILURE(rc) && !fSignalled)
     1228        RTThreadUserSignal(RTThreadSelf());
     1229
     1230    return rc;
     1231}
     1232
     1233
     1234/**
     1235 * Thread main routine for a started process.
     1236 *
     1237 * @return IPRT status code.
     1238 * @param  RTTHREAD             Pointer to the thread's data.
     1239 * @param  void*                User-supplied argument pointer.
     1240 *
     1241 */
     1242static DECLCALLBACK(int) VBoxServiceControlExecThread(RTTHREAD ThreadSelf, void *pvUser)
     1243{
     1244    PVBOXSERVICECTRLTHREAD pThread = (VBOXSERVICECTRLTHREAD*)pvUser;
     1245    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
     1246    return VBoxServiceControlExecProcessWorker(pThread);
     1247}
     1248
     1249
     1250/**
     1251 * Executes (starts) a process on the guest. This causes a new thread to be created
     1252 * so that this function will not block the overall program execution.
     1253 *
     1254 * @return  IPRT status code.
     1255 * @param   uClientID                   Client ID for accessing host service.
     1256 * @param   uContextID                  Context ID to associate the process to start with.
     1257 * @param   pszCmd                      Full qualified path of process to start (without arguments).
     1258 * @param   uFlags                      Process execution flags.
     1259 * @param   pszArgs                     String of arguments to pass to the process to start.
     1260 * @param   uNumArgs                    Number of arguments specified in pszArgs.
     1261 * @param   pszEnv                      String of environment variables ("FOO=BAR") to pass to the process
     1262 *                                      to start.
     1263 * @param   cbEnv                       Size (in bytes) of environment variables.
     1264 * @param   uNumEnvVars                 Number of environment variables specified in pszEnv.
     1265 * @param   pszUser                     User name (account) to start the process under.
     1266 * @param   pszPassword                 Password of specified user name (account).
     1267 * @param   uTimeLimitMS                Time limit (in ms) of the process' life time.
     1268 * @param   ppNode                       The thread's list node to insert into the global thread list
     1269 *                                      on success.
     1270 */
     1271int VBoxServiceControlThreadStart(uint32_t uClientID, uint32_t uContextID,
     1272                                  const char *pszCmd, uint32_t uFlags,
     1273                                  const char *pszArgs, uint32_t uNumArgs,
     1274                                  const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
     1275                                  const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS,
     1276                                  PRTLISTNODE *ppNode)
     1277{
     1278    AssertPtrReturn(ppNode, VERR_INVALID_POINTER);
     1279
     1280    /*
     1281     * Allocate new thread data and assign it to our thread list.
     1282     */
     1283    PVBOXSERVICECTRLTHREAD pThread = (PVBOXSERVICECTRLTHREAD)RTMemAlloc(sizeof(VBOXSERVICECTRLTHREAD));
     1284    if (!pThread)
     1285        return VERR_NO_MEMORY;
     1286
     1287    int rc = VBoxServiceControlExecThreadAlloc(pThread,
     1288                                               uContextID,
     1289                                               pszCmd, uFlags,
     1290                                               pszArgs, uNumArgs,
     1291                                               pszEnv, cbEnv, uNumEnvVars,
     1292                                               pszUser, pszPassword,
     1293                                               uTimeLimitMS);
     1294    if (RT_SUCCESS(rc))
     1295    {
     1296        static uint32_t uCtrlExecThread = 0;
     1297        char szThreadName[32];
     1298        if (!RTStrPrintf(szThreadName, sizeof(szThreadName), "controlexec%ld", uCtrlExecThread++))
     1299            AssertMsgFailed(("Unable to create unique control exec thread name!\n"));
     1300
     1301        rc = RTThreadCreate(&pThread->Thread, VBoxServiceControlExecThread,
     1302                            (void *)(PVBOXSERVICECTRLTHREAD*)pThread, 0,
     1303                            RTTHREADTYPE_DEFAULT, RTTHREADFLAGS_WAITABLE, szThreadName);
     1304        if (RT_FAILURE(rc))
     1305        {
     1306            VBoxServiceError("ControlExec: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
     1307                             rc, pThread);
     1308        }
     1309        else
     1310        {
     1311            VBoxServiceVerbose(4, "ControlExec: Waiting for thread to initialize ...\n");
     1312
     1313            /* Wait for the thread to initialize. */
     1314            RTThreadUserWait(pThread->Thread, 60 * 1000 /* 60 seconds max. */);
     1315            if (pThread->fShutdown)
     1316            {
     1317                VBoxServiceError("ControlExec: Thread for process \"%s\" failed to start!\n", pszCmd);
     1318                rc = VERR_GENERAL_FAILURE;
     1319            }
     1320            else
     1321            {
     1322                pThread->fStarted = true;
     1323
     1324                /* Return the thread's node. */
     1325                *ppNode = &pThread->Node;
     1326            }
     1327        }
     1328    }
     1329
     1330    if (RT_FAILURE(rc))
     1331        RTMemFree(pThread);
     1332
    1591333    return rc;
    1601334}
     
    1691343 * @param   uPID           PID to assign to the specified guest control execution thread.
    1701344 */
    171 int VBoxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREADDATAEXEC pData, uint32_t uPID)
    172 {
    173     AssertPtrReturn(pData, VERR_INVALID_POINTER);
     1345int vboxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID)
     1346{
     1347    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    1741348    AssertReturn(uPID, VERR_INVALID_PARAMETER);
    1751349
    176     int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
    177     if (RT_SUCCESS(rc))
    178     {
    179         /* Search an old thread using the desired PID and shut it down completely -- it's
    180          * not used anymore. */
    181         PVBOXSERVICECTRLTHREAD pOldNode = vboxServiceControlExecThreadGetByPID(uPID);
    182         if (   pOldNode
    183             && pOldNode->pvData != pData)
    184         {
    185             PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pOldNode->Node, VBOXSERVICECTRLTHREAD, Node);
    186 
    187             VBoxServiceVerbose(3, "ControlExec: PID %u was used before, shutting down stale exec thread ...\n",
    188                                uPID);
    189             AssertPtr(pOldNode->pvData);
    190             rc = VBoxServiceControlExecThreadShutdown(pOldNode);
    191             if (RT_FAILURE(rc))
     1350    int rc = VINF_SUCCESS;
     1351
     1352    /* Search an old thread using the desired PID and shut it down completely -- it's
     1353     * not used anymore. */
     1354    PVBOXSERVICECTRLTHREAD pOldThread = VBoxServiceControlGetThreadByPID(uPID);
     1355    if (   pOldThread
     1356        && pOldThread != pThread)
     1357    {
     1358        PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pOldThread->Node, VBOXSERVICECTRLTHREAD, Node);
     1359
     1360        VBoxServiceVerbose(3, "ControlExec: PID %u was used before, shutting down stale exec thread ...\n",
     1361                           uPID);
     1362        rc = VBoxServiceControlExecThreadShutdown(pOldThread);
     1363        if (RT_FAILURE(rc))
     1364        {
     1365            VBoxServiceVerbose(3, "ControlExec: Unable to shut down stale exec thread, rc=%Rrc\n", rc);
     1366            /* Keep going. */
     1367        }
     1368    }
     1369
     1370    /* Assign PID to current thread. */
     1371    pThread->uPID = uPID;
     1372
     1373    return rc;
     1374}
     1375
     1376
     1377/**
     1378 * TODO
     1379 *
     1380 * @return  IPRT status code.
     1381 * @return  int
     1382 * @param   uPID
     1383 * @param   pRequest
     1384 */
     1385int VBoxServiceControlExecThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest)
     1386{
     1387    AssertPtrReturn(pRequest, VERR_INVALID_POINTER);
     1388    AssertReturn(pRequest->enmType > VBOXSERVICECTRLREQUEST_UNKNOWN, VERR_INVALID_PARAMETER);
     1389    /* Rest in pRequest is optional (based on the request type). */
     1390
     1391    VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performing enmType=%u, pvData=0x%p, cbData=%u ...\n",
     1392                       uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData);
     1393
     1394    int rc;
     1395    const PVBOXSERVICECTRLTHREAD pThread = VBoxServiceControlGetThreadByPID(uPID);
     1396    if (pThread)
     1397    {
     1398        rc = RTCritSectEnter(&pThread->CritSect);
     1399        if (RT_SUCCESS(rc))
     1400        {
     1401            /* Set request structure pointer. */
     1402            pThread->pRequest = pRequest;
     1403
     1404            /** @todo To speed up simultaneous guest process handling we could add a worker threads in order
     1405             *        to wait for the request to happen. Later. */
     1406
     1407            /* Wake up guest thrad by sending a wakeup byte to the notification pipe so
     1408             * that RTPoll unblocks (returns) and we then can do our requested operation. */
     1409            if (pThread->hNotificationPipeW == NIL_RTPIPE)
     1410                rc = VERR_BROKEN_PIPE;
     1411            size_t cbWritten;
     1412            if (RT_SUCCESS(rc))
     1413                rc = RTPipeWrite(pThread->hNotificationPipeW, "i", 1, &cbWritten);
     1414            if (RT_SUCCESS(rc) && cbWritten)
    1921415            {
    193                 VBoxServiceVerbose(3, "ControlExec: Unable to shut down stale exec thread, rc=%Rrc\n", rc);
    194                 /* Keep going. */
    195             }
    196 
    197             RTListNodeRemove(&pOldNode->Node);
    198             RTMemFree(pOldNode);
    199         }
    200 
    201         /* Assign PID to current thread. */
    202         pData->uPID = uPID;
    203         VBoxServicePipeBufSetPID(&pData->stdIn, pData->uPID);
    204         VBoxServicePipeBufSetPID(&pData->stdOut, pData->uPID);
    205         VBoxServicePipeBufSetPID(&pData->stdErr, pData->uPID);
    206 
    207         int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
    208         if (RT_SUCCESS(rc))
    209             rc = rc2;
    210     }
    211 
    212     return rc;
    213 }
    214 
    215 
    216 /**
    217  *  Frees an allocated thread data structure along with all its allocated parameters.
    218  *
    219  * @param   pData          Pointer to thread data to free.
    220  */
    221 void VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pData)
    222 {
    223     if (pData)
    224     {
    225         VBoxServiceVerbose(3, "ControlExec: [PID %u]: Destroying thread data ...\n",
    226                            pData->uPID);
    227 
    228         RTStrFree(pData->pszCmd);
    229         if (pData->uNumEnvVars)
    230         {
    231             for (uint32_t i = 0; i < pData->uNumEnvVars; i++)
    232                 RTStrFree(pData->papszEnv[i]);
    233             RTMemFree(pData->papszEnv);
    234         }
    235         RTGetOptArgvFree(pData->papszArgs);
    236         RTStrFree(pData->pszUser);
    237         RTStrFree(pData->pszPassword);
    238 
    239         VBoxServicePipeBufDestroy(&pData->stdOut);
    240         VBoxServicePipeBufDestroy(&pData->stdErr);
    241         VBoxServicePipeBufDestroy(&pData->stdIn);
    242 
    243         RTMemFree(pData);
    244         pData = NULL;
    245     }
    246 }
    247 
    248 
    249 /**
    250  * Finds a (formerly) started process given by its PID.
    251  * Internal function, does not do locking -- this must be done from the caller function!
    252  *
    253  * @return  PVBOXSERVICECTRLTHREAD      Process structure if found, otherwise NULL.
    254  * @param   uPID                        PID to search for.
    255  */
    256 PVBOXSERVICECTRLTHREAD vboxServiceControlExecThreadGetByPID(uint32_t uPID)
    257 {
    258     PVBOXSERVICECTRLTHREAD pNode = NULL;
    259     RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
    260     {
    261         if (pNode->enmType == kVBoxServiceCtrlThreadDataExec)
    262         {
    263             PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
    264             if (pData && pData->uPID == uPID)
    265                 return pNode;
    266         }
    267     }
    268     return NULL;
    269 }
    270 
    271 
    272 /**
    273  * Injects input to a specified running process.
    274  *
    275  * @return  IPRT status code.
    276  * @param   uPID                    PID of process to set the input for.
    277  * @param   fPendingClose           Flag indicating whether this is the last input block sent to the process.
    278  * @param   pBuf                    Pointer to a buffer containing the actual input data.
    279  * @param   cbSize                  Size (in bytes) of the input buffer data.
    280  * @param   pcbWritten              Pointer to number of bytes written to the process.  Optional.
    281  */
    282 int VBoxServiceControlExecThreadSetInput(uint32_t uPID, bool fPendingClose, uint8_t *pBuf,
    283                                          uint32_t cbSize, uint32_t *pcbWritten)
    284 {
    285     AssertPtrReturn(pBuf, VERR_INVALID_PARAMETER);
    286 
    287     int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
    288     if (RT_SUCCESS(rc))
    289     {
    290         PVBOXSERVICECTRLTHREAD pNode = vboxServiceControlExecThreadGetByPID(uPID);
    291         if (pNode)
    292         {
    293             PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pNode->pvData;
    294             AssertPtr(pData);
    295 
    296             if (VBoxServicePipeBufIsEnabled(&pData->stdIn))
    297             {
    298                 /*
    299                  * Feed the data to the pipe.
    300                  */
    301                 uint32_t cbWritten;
    302                 rc = VBoxServicePipeBufWriteToBuf(&pData->stdIn, pBuf,
    303                                                   cbSize, fPendingClose, &cbWritten);
    304                 if (pcbWritten)
    305                     *pcbWritten = cbWritten;
    306             }
    307             else
    308             {
    309                 /* If input buffer is not enabled anymore we cannot handle that data ... */
    310                 rc = VERR_BAD_PIPE;
    311             }
    312         }
    313         else
    314             rc = VERR_NOT_FOUND; /* PID not found! */
    315         RTCritSectLeave(&g_GuestControlThreadsCritSect);
    316     }
    317     return rc;
    318 }
    319 
    320 
    321 /**
    322  * Gets output from stdout/stderr of a specified process.
    323  *
    324  * @return  IPRT status code.
    325  * @param   uPID                    PID of process to retrieve the output from.
    326  * @param   uHandleId               Stream ID (stdout = 0, stderr = 2) to get the output from.
    327  * @param   uTimeout                Timeout (in ms) to wait for output becoming available.
    328  * @param   pBuf                    Pointer to a pre-allocated buffer to store the output.
    329  * @param   cbSize                  Size (in bytes) of the pre-allocated buffer.
    330  * @param   pcbRead                 Pointer to number of bytes read.  Optional.
    331  */
    332 int VBoxServiceControlExecThreadGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout,
    333                                           uint8_t *pBuf, uint32_t cbSize, uint32_t *pcbRead)
    334 {
    335     AssertPtrReturn(pBuf, VERR_INVALID_POINTER);
    336     AssertReturn(cbSize, VERR_INVALID_PARAMETER);
    337 
    338     int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
    339     if (RT_SUCCESS(rc))
    340     {
    341         const PVBOXSERVICECTRLTHREAD pThread = vboxServiceControlExecThreadGetByPID(uPID);
    342         if (pThread)
    343         {
    344             const PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
    345             AssertPtr(pData);
    346 
    347             PVBOXSERVICECTRLEXECPIPEBUF pPipeBuf = NULL;
    348             switch (uHandleId)
    349             {
    350                 case OUTPUT_HANDLE_ID_STDERR:
    351                     pPipeBuf = &pData->stdErr;
    352                     break;
    353 
    354                 case OUTPUT_HANDLE_ID_STDOUT:
    355                 case OUTPUT_HANDLE_ID_STDOUT_DEPRECATED:
    356                     pPipeBuf = &pData->stdOut;
    357                     break;
    358 
    359                 default:
    360                     rc = VERR_NOT_FOUND; /* Handle ID not found! */
    361                     break;
    362             }
    363 
    364             if (RT_SUCCESS(rc))
    365             {
    366                 AssertPtr(pPipeBuf);
    367 
    368     #ifdef DEBUG_andy
    369                 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Getting output from pipe buffer %u ...\n",
    370                                    uPID, pPipeBuf->uPipeId);
    371     #endif
    372                 /* If the stdout pipe buffer is enabled (that is, still could be filled by a running
    373                  * process) wait for the signal to arrive so that we don't return without any actual
    374                  * data read. */
    375                 bool fEnabled = VBoxServicePipeBufIsEnabled(pPipeBuf);
    376                 if (fEnabled)
    377                 {
    378     #ifdef DEBUG_andy
    379                     VBoxServiceVerbose(4, "ControlExec: [PID %u]: Waiting for pipe buffer %u (%ums)\n",
    380                                        uPID, pPipeBuf->uPipeId, uTimeout);
    381     #endif
    382                     rc = VBoxServicePipeBufWaitForEvent(pPipeBuf, uTimeout);
    383                 }
     1416                /* Wait on the request to get completed (or we are asked to abort/shutdown). */
     1417                rc = RTSemEventMultiWait(pThread->RequestEvent, RT_INDEFINITE_WAIT);
    3841418                if (RT_SUCCESS(rc))
    3851419                {
    386                     uint32_t cbRead = cbSize; /* Read as much as possible. */
    387                     rc = VBoxServicePipeBufRead(pPipeBuf, pBuf, cbSize, &cbRead);
    388                     if (RT_SUCCESS(rc))
    389                     {
    390                         if (   !cbRead
    391                             && fEnabled)
    392                         {
    393                             AssertReleaseMsg(!VBoxServicePipeBufIsEnabled(pPipeBuf),
    394                                              ("[PID %u]: Waited (%ums) for active pipe buffer %u (%u size, %u bytes left), but nothing read!\n",
    395                                              uPID, uTimeout, pPipeBuf->uPipeId, pPipeBuf->cbSize, pPipeBuf->cbSize - pPipeBuf->cbOffset));
    396                         }
    397                         if (pcbRead)
    398                             *pcbRead = cbRead;
    399                     }
    400                     else
    401                         VBoxServiceError("ControlExec: [PID %u]: Unable to read from pipe buffer %u, rc=%Rrc\n",
    402                                          uPID, pPipeBuf->uPipeId, rc);
     1420                    VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performed with rc=%Rrc, cbData=%u\n",
     1421                                       uPID, pRequest->rc, pRequest->cbData);
     1422
     1423                    /* Give back overall request result. */
     1424                    rc = pRequest->rc;
     1425
     1426                    /* Reset the semaphore. */
     1427                    int rc2 = RTSemEventMultiReset(pThread->RequestEvent);
     1428                    if (RT_FAILURE(rc2))
     1429                        VBoxServiceError("ControlExec: Unable to reset request event, rc=%Rrc\n", rc2);
    4031430                }
    4041431            }
    405         }
    406         else
    407             rc = VERR_NOT_FOUND; /* PID not found! */
    408 
    409         int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
     1432
     1433            int rc2 = RTCritSectLeave(&pThread->CritSect);
     1434            AssertRCReturn(rc2, rc2);
     1435        }
     1436    }
     1437    else /* PID not found! */
     1438        rc = VERR_NOT_FOUND;
     1439
     1440    VBoxServiceVerbose(4, "ControlExec: [PID %u]: Performed enmType=%u, pvData=0x%p, cbData=%u with rc=%Rrc\n",
     1441                       uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData, rc);
     1442    return rc;
     1443}
     1444
     1445
     1446/**
     1447 * Removes the guest thread from the global guest thread list and finally
     1448 * destroys it. Does not do locking, must be done by the caller!
     1449 *
     1450 * @param   pThread                 Thread to destroy.
     1451 */
     1452void VBoxServiceControlExecThreadDestroy(PVBOXSERVICECTRLTHREAD pThread)
     1453{
     1454    if (!pThread)
     1455        return;
     1456
     1457    if (RTCritSectIsInitialized(&pThread->CritSect))
     1458        RTCritSectDelete(&pThread->CritSect);
     1459
     1460    /* Destroy thread structure as final step. */
     1461    RTMemFree(pThread);
     1462    pThread = NULL;
     1463}
     1464
     1465
     1466/**
     1467 * Shuts down a guest thread.
     1468 * Does not do locking, must be done by the caller!
     1469 *
     1470 * @return  IPRT status code.
     1471 * @param   pThread                 Thread to shut down.
     1472 */
     1473int VBoxServiceControlExecThreadShutdown(PVBOXSERVICECTRLTHREAD pThread)
     1474{
     1475    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
     1476
     1477    if (ASMAtomicReadBool(&pThread->fShutdown)) /* Already shut down. */
     1478        return VINF_SUCCESS;
     1479
     1480    /* First, signal shut down. */
     1481    VBoxServiceControlThreadSignalShutdown(pThread);
     1482
     1483    /*
     1484     * Wait for thread to shutdown.
     1485     */
     1486    VBoxServiceVerbose(2, "ControlExec: [PID %u]: Waitng for shutting down ...\n",
     1487                       pThread->uPID);
     1488    int rc;
     1489    for (int i = 0; i < 3; i++)
     1490    {
     1491        rc = vboxServiceControlThreadWaitForShutdown(pThread);
    4101492        if (RT_SUCCESS(rc))
    411             rc = rc2;
    412     }
    413     return rc;
    414 }
    415 
    416 
    417 int VBoxServiceControlExecThreadRemove(uint32_t uPID)
    418 {
    419     int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
    420     if (RT_SUCCESS(rc))
    421     {
    422         PVBOXSERVICECTRLTHREAD pThread = vboxServiceControlExecThreadGetByPID(uPID);
    423         if (pThread)
    424         {
    425             Assert(pThread->fStarted != pThread->fStopped);
    426             if (pThread->fStopped) /* Only shut down stopped threads. */
    427             {
    428                 VBoxServiceVerbose(4, "ControlExec: [PID %u]: Removing thread ... \n",
    429                                    uPID);
    430 
    431                 rc = VBoxServiceControlExecThreadShutdown(pThread);
    432 
    433                 RTListNodeRemove(&pThread->Node);
    434                 RTMemFree(pThread);
    435             }
    436         }
    437         else
    438             rc = VERR_NOT_FOUND;
    439 
    440         int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
    441         if (RT_SUCCESS(rc))
    442             rc = rc2;
    443     }
    444 
    445     return rc;
    446 }
    447 
    448 /* Does not do locking, must be done by the caller! */
    449 int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread)
    450 {
    451     AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    452 
    453     if (pThread->enmType == kVBoxServiceCtrlThreadDataExec)
    454     {
    455         PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
    456         if (!pData) /* Already destroyed execution data. */
    457             return VINF_SUCCESS;
    458         if (pThread->fStarted)
    459         {
    460             VBoxServiceVerbose(2, "ControlExec: [PID %u]: Shutting down a still running thread without stopping is not possible!\n",
    461                                pData->uPID);
    462             return VERR_INVALID_PARAMETER;
    463         }
    464 
    465         VBoxServiceVerbose(2, "ControlExec: [PID %u]: Shutting down, will not be served anymore\n",
    466                            pData->uPID);
    467         VBoxServiceControlExecThreadDataDestroy(pData);
    468     }
    469 
    470     VBoxServiceControlThreadSignalShutdown(pThread);
    471     return VBoxServiceControlThreadWaitForShutdown(pThread);
    472 }
    473 
    474 
    475 int VBoxServiceControlExecThreadStartAllowed(bool *pbAllowed)
    476 {
    477     AssertPtrReturn(pbAllowed, VERR_INVALID_POINTER);
    478 
    479     int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
    480     if (RT_SUCCESS(rc))
    481     {
    482         /*
    483          * Check if we're respecting our memory policy by checking
    484          * how many guest processes are started and served already.
    485          */
    486         bool fLimitReached = false;
    487         if (g_GuestControlProcsMaxKept) /* If we allow unlimited processes (=0), take a shortcut. */
    488         {
    489             /** @todo Put running/stopped (+ memory alloc) stats into global struct! */
    490             uint32_t uProcsRunning = 0;
    491             uint32_t uProcsStopped = 0;
    492             PVBOXSERVICECTRLTHREAD pNode;
    493             RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
    494             {
    495                 if (pNode->enmType == kVBoxServiceCtrlThreadDataExec)
    496                 {
    497                     Assert(pNode->fStarted != pNode->fStopped);
    498                     if (pNode->fStarted)
    499                         uProcsRunning++;
    500                     else if (pNode->fStopped)
    501                         uProcsStopped++;
    502                     else
    503                         AssertMsgFailed(("Process neither started nor stopped!?\n"));
    504                 }
    505             }
    506 
    507             VBoxServiceVerbose(2, "ControlExec: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
    508                                g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped);
    509 
    510             int32_t iProcsLeft = (g_GuestControlProcsMaxKept - uProcsRunning - 1);
    511             if (iProcsLeft < 0)
    512             {
    513                 VBoxServiceVerbose(3, "ControlExec: Maximum running guest processes reached (%u)\n",
    514                                    g_GuestControlProcsMaxKept);
    515                 fLimitReached = true;
    516             }
    517             else if (uProcsStopped > (uint32_t)iProcsLeft)
    518             {
    519                 uint32_t uProcsToKill = uProcsStopped - iProcsLeft;
    520                 Assert(uProcsToKill);
    521                 VBoxServiceVerbose(3, "ControlExec: Shutting down %ld stopped guest processes\n", uProcsToKill);
    522 
    523                 RTListForEach(&g_GuestControlThreads, pNode, VBOXSERVICECTRLTHREAD, Node)
    524                 {
    525                     if (   pNode->enmType == kVBoxServiceCtrlThreadDataExec
    526                         && pNode->fStopped)
    527                     {
    528                         PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pNode->Node, VBOXSERVICECTRLTHREAD, Node);
    529 
    530                         int rc2 = VBoxServiceControlExecThreadShutdown(pNode);
    531                         if (RT_FAILURE(rc2))
    532                         {
    533                             VBoxServiceError("ControlExec: Unable to shut down thread due to policy, rc=%Rrc\n", rc2);
    534                             if (RT_SUCCESS(rc))
    535                                 rc = rc2;
    536                             /* Keep going. */
    537                         }
    538 
    539                         RTListNodeRemove(&pNode->Node);
    540                         RTMemFree(pNode);
    541                         pNode = pNext;
    542 
    543                         Assert(uProcsToKill);
    544                         uProcsToKill--;
    545                         if (!uProcsToKill)
    546                             break;
    547                     }
    548                 }
    549                 Assert(uProcsToKill == 0);
    550             }
    551         }
    552 
    553         *pbAllowed = !fLimitReached;
    554 
    555         int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
    556         if (RT_SUCCESS(rc))
    557             rc = rc2;
    558     }
    559 
    560     return rc;
    561 }
    562 
    563 
    564 /**
    565  * Marks an guest execution thread as stopped and cleans up its internal pipe buffers.
    566  *
    567  * @param   pThread                 Pointer to guest execution thread.
    568  */
    569 void VBoxServiceControlExecThreadStop(const PVBOXSERVICECTRLTHREAD pThread)
    570 {
    571     AssertPtr(pThread);
    572 
    573     int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
    574     if (RT_SUCCESS(rc))
    575     {
    576         if (pThread->fStarted)
    577         {
    578             const PVBOXSERVICECTRLTHREADDATAEXEC pData = (PVBOXSERVICECTRLTHREADDATAEXEC)pThread->pvData;
    579             if (pData)
    580             {
    581                 VBoxServiceVerbose(3, "ControlExec: [PID %u]: Marking as stopped\n", pData->uPID);
    582 
    583                 VBoxServicePipeBufSetStatus(&pData->stdIn, false /* Disabled */);
    584                 VBoxServicePipeBufSetStatus(&pData->stdOut, false /* Disabled */);
    585                 VBoxServicePipeBufSetStatus(&pData->stdErr, false /* Disabled */);
    586 
    587                 /* Since the process is not alive anymore, destroy its local
    588                  * stdin pipe buffer - it's not used anymore and can eat up quite
    589                  * a bit of memory. */
    590                 VBoxServicePipeBufDestroy(&pData->stdIn);
    591             }
    592 
    593             /* Mark as stopped. */
    594             ASMAtomicXchgBool(&pThread->fStarted, false);
    595             ASMAtomicXchgBool(&pThread->fStopped, true);
    596         }
    597 
    598         RTCritSectLeave(&g_GuestControlThreadsCritSect);
    599     }
    600 }
    601 
     1493            break;
     1494
     1495        VBoxServiceError("ControlExec: [PID %u]: Attempt #%d: Waiting for shutdown failed with rc=%Rrc ...\n",
     1496                         pThread->uPID, i + 1, rc);
     1497        RTThreadSleep(500); /* Wait a bit before next try. */
     1498    }
     1499
     1500    /* Do not destroy critical section here! */
     1501
     1502    /*
     1503     * Destroy thread-specific data.
     1504     */
     1505    vboxServiceControlExecThreadFree(pThread);
     1506
     1507    return rc;
     1508}
     1509
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlExecThread.h

    r38587 r39279  
    2121#include "VBoxServiceInternal.h"
    2222
    23 int VBoxServiceControlExecThreadAlloc(PVBOXSERVICECTRLTHREAD pThread,
    24                                       uint32_t u32ContextID,
    25                                       const char *pszCmd, uint32_t uFlags,
    26                                       const char *pszArgs, uint32_t uNumArgs,
    27                                       const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
    28                                       const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS);
    29 int VBoxServiceControlExecThreadAssignPID(PVBOXSERVICECTRLTHREADDATAEXEC pData, uint32_t uPID);
    30 void VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pData);
    31 int VBoxServiceControlExecThreadGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout,
    32                                           uint8_t *pBuf, uint32_t cbSize, uint32_t *pcbRead);
    33 int VBoxServiceControlExecThreadRemove(uint32_t uPID);
    34 int VBoxServiceControlExecThreadSetInput(uint32_t uPID, bool fPendingClose, uint8_t *pBuf,
    35                                          uint32_t cbSize, uint32_t *pcbWritten);
    36 int VBoxServiceControlExecThreadStartAllowed(bool *pbAllowed);
    37 void VBoxServiceControlExecThreadStop(const PVBOXSERVICECTRLTHREAD pThread);
     23void VBoxServiceControlExecThreadDestroy(PVBOXSERVICECTRLTHREAD pThread);
     24int VBoxServiceControlExecThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest);
     25int VBoxServiceControlExecThreadShutdown(const PVBOXSERVICECTRLTHREAD pThread);
     26
    3827#endif  /* !___VBoxServiceControlExecThread_h */
    3928
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h

    r38633 r39279  
    108108
    109109#ifdef VBOX_WITH_GUEST_CONTROL
    110 typedef enum VBOXSERVICECTRLTHREADDATATYPE
    111 {
    112     kVBoxServiceCtrlThreadDataUnknown = 0,
    113     kVBoxServiceCtrlThreadDataExec
    114 } VBOXSERVICECTRLTHREADDATATYPE;
    115 
     110/**
     111 * Pipe IDs for handling the guest process poll set.
     112 */
    116113typedef enum VBOXSERVICECTRLPIPEID
    117114{
    118     VBOXSERVICECTRLPIPEID_STDIN = 0,
    119     VBOXSERVICECTRLPIPEID_STDIN_ERROR,
    120     VBOXSERVICECTRLPIPEID_STDIN_WRITABLE,
    121     VBOXSERVICECTRLPIPEID_STDIN_INPUT_NOTIFY,
    122     VBOXSERVICECTRLPIPEID_STDOUT,
    123     VBOXSERVICECTRLPIPEID_STDERR
     115    VBOXSERVICECTRLPIPEID_UNKNOWN           = 0,
     116    VBOXSERVICECTRLPIPEID_STDIN             = 10,
     117    VBOXSERVICECTRLPIPEID_STDIN_WRITABLE    = 11,
     118    /** Pipe for reading from guest process' stdout. */
     119    VBOXSERVICECTRLPIPEID_STDOUT            = 40,
     120    /** Pipe for reading from guest process' stderr. */
     121    VBOXSERVICECTRLPIPEID_STDERR            = 50,
     122    /** Notification pipe for waking up the guest process
     123     *  control thread. */
     124    VBOXSERVICECTRLPIPEID_IPC_NOTIFY        = 100
    124125} VBOXSERVICECTRLPIPEID;
    125126
    126127/**
    127  * Structure for holding buffered pipe data.
    128  */
    129 typedef struct
    130 {
    131     /** The PID the pipe is assigned to. */
    132     uint32_t    uPID;
    133     /** The pipe's Id of enum VBOXSERVICECTRLPIPEID. */
    134     uint8_t     uPipeId;
    135     /** The data buffer. */
    136     uint8_t    *pbData;
    137     /** The amount of allocated buffer space. */
    138     uint32_t    cbAllocated;
    139     /** The actual used/occupied buffer space. */
    140     uint32_t    cbSize;
    141     /** Helper variable for keeping track of what
    142      *  already was processed and what not. */
    143     uint32_t    cbOffset;
    144     /** Critical section protecting this buffer structure. */
    145     RTCRITSECT  CritSect;
    146     /** Flag indicating whether this pipe buffer accepts new
    147      *  data to be written to or not. If not enabled, already
    148      *  (allocated) buffered data still can be read out. */
    149     bool        fEnabled;
    150     /** Set if it's necessary to write to the notification pipe. */
    151     bool        fNeedNotification;
    152     /** Set if the pipe needs to be closed after the next read/write. */
    153     bool        fPendingClose;
    154     /** The notification pipe associated with this buffer.
    155      * This is NIL_RTPIPE for output pipes. */
    156     RTPIPE      hNotificationPipeW;
    157     /** The other end of hNotificationPipeW. */
    158     RTPIPE      hNotificationPipeR;
    159     /** The event semaphore for getting notified whether something
    160      *  has changed, e.g. written to this buffer or enabled/disabled it. */
    161     RTSEMEVENT  hEventSem;
    162 } VBOXSERVICECTRLEXECPIPEBUF;
    163 /** Pointer to buffered pipe data. */
    164 typedef VBOXSERVICECTRLEXECPIPEBUF *PVBOXSERVICECTRLEXECPIPEBUF;
    165 
    166 /**
    167  * Structure for holding guest exection relevant data.
    168  */
    169 typedef struct
    170 {
    171     uint32_t  uPID;
    172     char     *pszCmd;
    173     uint32_t  uFlags;
    174     char    **papszArgs;
    175     uint32_t  uNumArgs;
    176     char    **papszEnv;
    177     uint32_t  uNumEnvVars;
    178     char     *pszUser;
    179     char     *pszPassword;
    180     uint32_t  uTimeLimitMS;
    181 
    182     RTPIPE                     pipeStdInW;
    183     VBOXSERVICECTRLEXECPIPEBUF stdIn;
    184     VBOXSERVICECTRLEXECPIPEBUF stdOut;
    185     VBOXSERVICECTRLEXECPIPEBUF stdErr;
    186 
    187 } VBOXSERVICECTRLTHREADDATAEXEC;
    188 /** Pointer to thread data. */
    189 typedef VBOXSERVICECTRLTHREADDATAEXEC *PVBOXSERVICECTRLTHREADDATAEXEC;
    190 
    191 /* Structure for holding thread relevant data. */
     128 * Request types to perform on a started guest process.
     129 */
     130typedef enum VBOXSERVICECTRLREQUESTTYPE
     131{
     132    /** Unknown request. */
     133    VBOXSERVICECTRLREQUEST_UNKNOWN          = 0,
     134    /** Main control thread asked used to quit. */
     135    VBOXSERVICECTRLREQUEST_QUIT             = 1,
     136    /** Performs reading from stdout. */
     137    VBOXSERVICECTRLREQUEST_STDOUT_READ      = 50,
     138    /** Performs reading from stderr. */
     139    VBOXSERVICECTRLREQUEST_STDERR_READ      = 60,
     140    /** Performs writing to stdin. */
     141    VBOXSERVICECTRLREQUEST_STDIN_WRITE      = 70,
     142    /** Same as VBOXSERVICECTRLREQUEST_STDIN_WRITE, but
     143     *  marks the end of input. */
     144    VBOXSERVICECTRLREQUEST_STDIN_WRITE_EOF  = 71,
     145    /** Kill process.
     146     *  @todo Implement this! */
     147    VBOXSERVICECTRLREQUEST_KILL             = 90,
     148    /** Gently ask process to terminate.
     149     *  @todo Implement this! */
     150    VBOXSERVICECTRLREQUEST_HANGUP           = 91
     151} VBOXSERVICECTRLREQUESTTYPE;
     152
     153/**
     154 * Structure to perform a request on a started guest
     155 * process. Needed for letting the main guest control thread
     156 * to communicate (and wait) for a certain operation which
     157 * will be done in context of the started guest process thread.
     158 */
     159typedef struct VBOXSERVICECTRLREQUEST
     160{
     161    /** The request type to handle. */
     162    VBOXSERVICECTRLREQUESTTYPE enmType;
     163    /** On input, this contains the (maximum) amount
     164     *  of buffered data to read or write. On output,
     165     *  this show the actual amount of data read/written. */
     166    size_t                     cbData;
     167    /** The provided, allocated data buffer for input/output. */
     168    void                      *pvData;
     169    /** The overall result of the operation. */
     170    int                        rc;
     171} VBOXSERVICECTRLREQUEST;
     172/** Pointer to request. */
     173typedef VBOXSERVICECTRLREQUEST *PVBOXSERVICECTRLREQUEST;
     174
     175/**
     176 * Structure for holding data for one (started) guest process.
     177 */
    192178typedef struct VBOXSERVICECTRLTHREAD
    193179{
     
    206192    /** Context ID. */
    207193    uint32_t                        uContextID;
    208     /** Type of thread.  See VBOXSERVICECTRLTHREADDATATYPE for more info. */
    209     VBOXSERVICECTRLTHREADDATATYPE   enmType;
    210     /** Pointer to actual thread data, depending on enmType. */
    211     void                           *pvData;
     194    /** Critical section for thread-safe use. */
     195    RTCRITSECT                      CritSect;
     196
     197    /** @todo Document me! */
     198    uint32_t                        uPID;
     199    char                           *pszCmd;
     200    uint32_t                        uFlags;
     201    char                          **papszArgs;
     202    uint32_t                        uNumArgs;
     203    char                          **papszEnv;
     204    uint32_t                        uNumEnvVars;
     205    /** Name of specified user account to run the
     206     *  guest process under. */
     207    char                           *pszUser;
     208    /** Password of specified user account. */
     209    char                           *pszPassword;
     210    /** Overall time limit (in ms) that the guest process
     211     *  is allowed to run. 0 for indefinite time. */
     212    uint32_t                        uTimeLimitMS;
     213    RTSEMEVENTMULTI                 RequestEvent;
     214    /** Pointer to the current IPC request being
     215     *  processed. */
     216    PVBOXSERVICECTRLREQUEST         pRequest;
     217    /** StdIn pipe for addressing writes to the
     218     *  guest process' stdin.*/
     219    RTPIPE                          pipeStdInW;
     220    /** The notification pipe associated with this guest process.
     221     *  This is NIL_RTPIPE for output pipes. */
     222    RTPIPE                          hNotificationPipeW;
     223    /** The other end of hNotificationPipeW. */
     224    RTPIPE                          hNotificationPipeR;
    212225} VBOXSERVICECTRLTHREAD;
    213226/** Pointer to thread data. */
     
    308321
    309322#ifdef VBOX_WITH_GUEST_CONTROL
    310 extern void         VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread);
    311 extern int          VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread);
    312 
    313 extern int          VBoxServiceControlExecHandleCmdStartProcess(uint32_t u32ClientId, uint32_t uNumParms);
    314 extern int          VBoxServiceControlExecHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize);
    315 extern int          VBoxServiceControlExecHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms);
    316 extern int          VBoxServiceControlExecProcess(uint32_t uClientID, uint32_t uContext,
     323/* Guest control functions. */
     324extern int          VBoxServiceControlHandleCmdStartProc(uint32_t u32ClientId, uint32_t uNumParms);
     325extern int          VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize);
     326extern int          VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms);
     327extern const PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadByPID(uint32_t uPID);
     328extern void         VBoxServiceControlRemoveThread(PVBOXSERVICECTRLTHREAD pThread);
     329/* Guest process functions. */
     330extern int          VBoxServiceControlThreadStart(uint32_t uClientID, uint32_t uContext,
    317331                                                  const char *pszCmd, uint32_t uFlags,
    318332                                                  const char *pszArgs, uint32_t uNumArgs,
    319333                                                  const char *pszEnv, uint32_t cbEnv, uint32_t uNumEnvVars,
    320                                                   const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS);
    321 extern void         VBoxServiceControlExecThreadDataDestroy(PVBOXSERVICECTRLTHREADDATAEXEC pThread);
     334                                                  const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS,
     335                                                  PRTLISTNODE *ppNode);
     336extern void         VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread);
     337extern int          VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread);
    322338#endif /* VBOX_WITH_GUEST_CONTROL */
    323339
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceVMInfo-win.cpp

    r39106 r39279  
    674674        { szSysDir, "VBoxCredProv.dll" },
    675675
    676  /* On 64-bit we don't yet have the OpenGL DLLs in native format.
    677     So just enumerate the 32-bit files in the SYSWOW directory. */
     676        /* Handy-to-know stuff, mostly patched stuff. */
     677        { szSysDir, "libWine.dll" },
     678        { szSysDir, "wined3d.dll" },
     679        { szSysDir, "d3d8.dll" },
     680        { szSysDir, "d3d9.dll" },
     681
     682        /* Direct3D. */
     683        { szSysDir, "VBoxDispD3D.dll" },
     684        { szSysDir, "VBoxD3D8.dll" },
     685        { szSysDir, "VBoxD3D9.dll" },
     686
     687        /* OpenGL. */
     688        { szSysDir, "VBoxOGLarrayspu.dll" },
     689        { szSysDir, "VBoxOGLcrutil.dll" },
     690        { szSysDir, "VBoxOGLerrorspu.dll" },
     691        { szSysDir, "VBoxOGLpackspu.dll" },
     692        { szSysDir, "VBoxOGLpassthroughspu.dll" },
     693        { szSysDir, "VBoxOGLfeedbackspu.dll" },
     694        { szSysDir, "VBoxOGL.dll" },
     695
     696        /* On 64-bit OSes we also have our OpenGL + D3D DLLs in 32-bit,
     697           so enumerate these files in the SYSWOW directory. */
    678698# ifdef RT_ARCH_AMD64
    679699        { szSysWowDir, "VBoxOGLarrayspu.dll" },
     
    684704        { szSysWowDir, "VBoxOGLfeedbackspu.dll" },
    685705        { szSysWowDir, "VBoxOGL.dll" },
    686 # else  /* !RT_ARCH_AMD64 */
    687         { szSysDir, "VBoxOGLarrayspu.dll" },
    688         { szSysDir, "VBoxOGLcrutil.dll" },
    689         { szSysDir, "VBoxOGLerrorspu.dll" },
    690         { szSysDir, "VBoxOGLpackspu.dll" },
    691         { szSysDir, "VBoxOGLpassthroughspu.dll" },
    692         { szSysDir, "VBoxOGLfeedbackspu.dll" },
    693         { szSysDir, "VBoxOGL.dll" },
    694 # endif /* !RT_ARCH_AMD64 */
    695 
     706
     707        { szSysWowDir, "VBoxDispD3D.dll" },
     708# endif /* RT_ARCH_AMD64 */
     709
     710        /* Installed drivers. */
    696711        { szDriversDir, "VBoxGuest.sys" },
    697712        { szDriversDir, "VBoxMouse.sys" },
    698713        { szDriversDir, "VBoxSF.sys"    },
    699714        { szDriversDir, "VBoxVideo.sys" },
     715        { szDriversDir, "VBoxVideoWddm.sys" },
    700716    };
    701717
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