VirtualBox

Changeset 39418 in vbox


Ignore:
Timestamp:
Nov 25, 2011 10:11:06 AM (13 years ago)
Author:
vboxsync
Message:

GuestCtrl: Added support for explicitly waiting on stdout/stderr, bugfixes, logging adjustments.

Location:
trunk
Files:
14 edited

Legend:

Unmodified
Added
Removed
  • trunk/include/VBox/HostServices/GuestControlSvc.h

    r38870 r39418  
    7575#define INPUT_FLAG_NONE             0
    7676#define INPUT_FLAG_EOF              RT_BIT(0)
     77
     78/**
     79 * Execution flags.
     80 * Note: Has to match Main's ExecuteProcessFlag_* flags!
     81 */
     82#define EXECUTEPROCESSFLAG_NONE             0x0
     83#define EXECUTEPROCESSFLAG_WAIT_START       RT_BIT(1)
     84#define EXECUTEPROCESSFLAG_HIDDEN           RT_BIT(2)
     85#define EXECUTEPROCESSFLAG_NO_PROFILE       RT_BIT(3)
     86#define EXECUTEPROCESSFLAG_WAIT_STDOUT      RT_BIT(4)
     87#define EXECUTEPROCESSFLAG_WAIT_STDERR      RT_BIT(5)
    7788
    7889/**
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControl.cpp

    r39392 r39418  
    314314            /* Tell the host. */
    315315            rc = VbglR3GuestCtrlExecReportStatus(uClientID, uContextID, 0 /* PID, invalid. */,
    316                                                  PROC_STS_ERROR, VERR_MAX_PROCS_REACHED,
     316                                                 PROC_STS_ERROR,
     317                                                 !fAllowed ? VERR_MAX_PROCS_REACHED : rc,
    317318                                                 NULL /* pvData */, 0 /* cbData */);
    318319        }
     
    335336 * @param   pcbRead                 Pointer to number of bytes read.  Optional.
    336337 */
    337 int VBoxServiceControlExecGetOutput(uint32_t uPID, uint32_t uHandleId, uint32_t uTimeout,
     338int VBoxServiceControlExecGetOutput(uint32_t uPID, uint32_t uCID,
     339                                    uint32_t uHandleId, uint32_t uTimeout,
    338340                                    void *pvBuf, uint32_t cbBuf, uint32_t *pcbRead)
    339341{
     
    345347
    346348    VBOXSERVICECTRLREQUEST ctrlRequest;
     349    ctrlRequest.uCID   = uCID;
    347350    ctrlRequest.cbData = cbBuf;
    348351    ctrlRequest.pvData = (uint8_t*)pvBuf;
     
    389392 * @param   pcbWritten              Pointer to number of bytes written to the process.  Optional.
    390393 */
    391 int VBoxServiceControlSetInput(uint32_t uPID, bool fPendingClose,
     394int VBoxServiceControlSetInput(uint32_t uPID, uint32_t uCID,
     395                               bool fPendingClose,
    392396                               void *pvBuf, uint32_t cbBuf,
    393397                               uint32_t *pcbWritten)
     
    400404
    401405    VBOXSERVICECTRLREQUEST ctrlRequest;
     406    ctrlRequest.uCID    = uCID;
    402407    ctrlRequest.cbData  = cbBuf;
    403408    ctrlRequest.pvData  = pvBuf;
     
    470475        }
    471476
    472         rc = VBoxServiceControlSetInput(uPID, fPendingClose, pabBuffer,
     477        rc = VBoxServiceControlSetInput(uPID, uContextID, fPendingClose, pabBuffer,
    473478                                        cbSize, &cbWritten);
    474         VBoxServiceVerbose(4, "Control: [PID %u]: Written input, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
    475                            uPID, rc, uFlags, fPendingClose, cbSize, cbWritten);
     479        VBoxServiceVerbose(4, "Control: [PID %u]: Written input, CID=%u, rc=%Rrc, uFlags=0x%x, fPendingClose=%d, cbSize=%u, cbWritten=%u\n",
     480                           uPID, uContextID, rc, uFlags, fPendingClose, cbSize, cbWritten);
    476481        if (RT_SUCCESS(rc))
    477482        {
     
    543548        if (pBuf)
    544549        {
    545             rc = VBoxServiceControlExecGetOutput(uPID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
     550            rc = VBoxServiceControlExecGetOutput(uPID, uContextID, uHandleID, RT_INDEFINITE_WAIT /* Timeout */,
    546551                                                 pBuf, _64K /* cbSize */, &cbRead);
     552
     553            /** Note: Don't convert/touch/modify/whatever the output data here! This might be binary
     554             *        data which the host needs to work with -- so just pass through all data unfiltered! */
     555
    547556            if (RT_SUCCESS(rc))
    548                 VBoxServiceVerbose(3, "Control: [PID %u]: Got output, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
     557                VBoxServiceVerbose(2, "Control: Got output, PID=%u, CID=%u, cbRead=%u, uHandle=%u, uFlags=%u\n",
    549558                                   uPID, uContextID, cbRead, uHandleID, uFlags);
     559            else if (rc == VERR_NOT_FOUND)
     560                VBoxServiceVerbose(2, "Control: PID=%u not found, CID=%u, uHandle=%u\n",
     561                                   uPID, uContextID, uHandleID, rc);
    550562            else
    551                 VBoxServiceError("Control: [PID %u]: Failed to retrieve output, CID=%u, uHandle=%u, rc=%Rrc\n",
     563                VBoxServiceError("Control: Failed to retrieve output for PID=%u, CID=%u, uHandle=%u, rc=%Rrc\n",
    552564                                 uPID, uContextID, uHandleID, rc);
    553565            /* Note: Since the context ID is unique the request *has* to be completed here,
     
    598610
    599611
     612/**
     613 * Destroys all guest process threads which are still active.
     614 */
    600615static void VBoxServiceControlDestroyThreads(void)
    601616{
    602     VBoxServiceVerbose(3, "Control: Destroying threads ...\n");
     617    VBoxServiceVerbose(2, "Control: Destroying threads ...\n");
    603618
    604619    /* Signal all threads that we want to shutdown. */
     
    625640    }
    626641
     642#ifdef DEBUG
     643        PVBOXSERVICECTRLTHREAD pThreadCur;
     644        uint32_t cThreads = 0;
     645        RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
     646            cThreads++;
     647        VBoxServiceVerbose(4, "Control: Guest process threads left=%u\n", cThreads);
     648#endif
    627649    AssertMsg(RTListIsEmpty(&g_GuestControlThreads),
    628650              ("Guest process thread list still contains children when it should not\n"));
     
    680702            RTListForEach(&g_GuestControlThreads, pThread, VBOXSERVICECTRLTHREAD, Node)
    681703            {
    682     // THREAD LOCKING!!
    683                 Assert(pThread->fStarted != pThread->fStopped);
    684                 if (pThread->fStarted)
     704                VBOXSERVICECTRLTHREADSTATUS enmStatus = VBoxServiceControlThreadGetStatus(pThread);
     705                if (enmStatus == VBOXSERVICECTRLTHREADSTATUS_STARTED)
    685706                    uProcsRunning++;
    686                 else if (pThread->fStopped)
     707                else if (enmStatus == VBOXSERVICECTRLTHREADSTATUS_STOPPED)
    687708                    uProcsStopped++;
    688709                else
     
    690711            }
    691712
    692             VBoxServiceVerbose(2, "Control: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
     713            VBoxServiceVerbose(3, "Control: Maximum served guest processes set to %u, running=%u, stopped=%u\n",
    693714                               g_GuestControlProcsMaxKept, uProcsRunning, uProcsStopped);
    694715
     
    714735
    715736/**
    716  * Finds a (formerly) started process given by its PID.
     737 * Finds a (formerly) started process given by its PID and locks it. Must be unlocked
     738 * by the caller with VBoxServiceControlThreadUnlock().
    717739 *
    718740 * @return  PVBOXSERVICECTRLTHREAD      Process structure if found, otherwise NULL.
    719741 * @param   uPID                        PID to search for.
    720742 */
    721 PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadByPID(uint32_t uPID)
     743const PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadLocked(uint32_t uPID)
    722744{
    723745    PVBOXSERVICECTRLTHREAD pThread = NULL;
     
    730752            if (pThreadCur->uPID == uPID)
    731753            {
    732                 pThread = pThreadCur;
     754                rc = RTCritSectEnter(&pThreadCur->CritSect);
     755                if (RT_SUCCESS(rc))
     756                    pThread = pThreadCur;
    733757                break;
    734758            }
     
    745769
    746770/**
     771 * Unlocks a previously locked guest process thread.
     772 *
     773 * @param   pThread                 Thread to unlock.
     774 */
     775void VBoxServiceControlThreadUnlock(const PVBOXSERVICECTRLTHREAD pThread)
     776{
     777    AssertPtr(pThread);
     778
     779    int rc = RTCritSectLeave(&pThread->CritSect);
     780    AssertRC(rc);
     781}
     782
     783
     784/**
     785 * Assigns a valid PID to a guest control thread and also checks if there already was
     786 * another (stale) guest process which was using that PID before and destroys it.
     787 *
     788 * @return  IPRT status code.
     789 * @param   pThread        Thread to assign PID to.
     790 * @param   uPID           PID to assign to the specified guest control execution thread.
     791 */
     792int VBoxServiceControlAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID)
     793{
     794    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
     795    AssertReturn(uPID, VERR_INVALID_PARAMETER);
     796
     797    int rc = RTCritSectEnter(&g_GuestControlThreadsCritSect);
     798    if (RT_SUCCESS(rc))
     799    {
     800        /* Search old threads using the desired PID and shut them down completely -- it's
     801         * not used anymore. */
     802        PVBOXSERVICECTRLTHREAD pThreadCur;
     803        RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
     804        {
     805            if (   pThreadCur->uPID == uPID
     806                && pThreadCur       != pThread)
     807            {
     808                VBoxServiceVerbose(2, "ControlThread: PID %u was used before, shutting down stale exec thread ...\n",
     809                                   uPID);
     810                rc = VBoxServiceControlThreadSignalShutdown(pThreadCur);
     811                if (RT_SUCCESS(rc))
     812                    rc = VBoxServiceControlThreadWaitForShutdown(pThreadCur,
     813                                                                 30 * 1000 /* Wait 30 seconds max. */);
     814            }
     815        }
     816
     817        /* Assign PID to current thread. */
     818        pThread->uPID = uPID;
     819
     820        rc = RTCritSectLeave(&g_GuestControlThreadsCritSect);
     821        AssertRC(rc);
     822    }
     823
     824    return rc;
     825}
     826
     827
     828/**
    747829 * Removes the specified guest process thread from the global thread
    748830 * list.
     
    751833 * @param   pThread             Thread to remove.
    752834 */
    753 void VBoxServiceControlRemoveThread(PVBOXSERVICECTRLTHREAD pThread)
     835void VBoxServiceControlRemoveThread(const PVBOXSERVICECTRLTHREAD pThread)
    754836{
    755837    if (!pThread)
     
    763845        RTListNodeRemove(&pThread->Node);
    764846
    765         int rc2 = RTCritSectLeave(&g_GuestControlThreadsCritSect);
    766         AssertRC(rc2);
     847#ifdef DEBUG
     848        PVBOXSERVICECTRLTHREAD pThreadCur;
     849        uint32_t cThreads = 0;
     850        RTListForEach(&g_GuestControlThreads, pThreadCur, VBOXSERVICECTRLTHREAD, Node)
     851            cThreads++;
     852        VBoxServiceVerbose(4, "Control: Guest process threads left=%u\n", cThreads);
     853#endif
     854        rc = RTCritSectLeave(&g_GuestControlThreadsCritSect);
     855        AssertRC(rc);
    767856    }
    768857}
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceControlThread.cpp

    r39393 r39418  
    4242
    4343/* Internal functions. */
    44 static int vboxServiceControlThreadAssignPID(PVBOXSERVICECTRLTHREAD pData, uint32_t uPID);
    45 static void vboxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pData);
    46 static int vboxServiceControlThreadShutdown(PVBOXSERVICECTRLTHREAD pThread);
     44static int vboxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pThread);
    4745
    4846/**
     
    154152
    155153/**
     154 * Frees a guest thread.
     155 *
     156 * @return  IPRT status code.
     157 * @param   pThread                 Thread to shut down.
     158 */
     159static int vboxServiceControlThreadFree(PVBOXSERVICECTRLTHREAD pThread)
     160{
     161    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
     162
     163    VBoxServiceVerbose(1, "ControlThread: [PID %u]: Shutting down ...\n",
     164                       pThread->uPID);
     165
     166    /* Signal the request event to unblock potential waiters. */
     167    int rc = RTSemEventMultiSignal(pThread->RequestEvent);
     168    if (RT_FAILURE(rc))
     169        VBoxServiceError("ControlThread: [PID %u]: Signalling request event failed, rc=%Rrc\n",
     170                         pThread->uPID, rc);
     171
     172    rc = RTCritSectEnter(&pThread->CritSect);
     173    if (RT_SUCCESS(rc))
     174    {
     175        VBoxServiceVerbose(3, "ControlThread: [PID %u]: Freeing thread data ...\n",
     176                           pThread->uPID);
     177
     178        RTStrFree(pThread->pszCmd);
     179        if (pThread->uNumEnvVars)
     180        {
     181            for (uint32_t i = 0; i < pThread->uNumEnvVars; i++)
     182                RTStrFree(pThread->papszEnv[i]);
     183            RTMemFree(pThread->papszEnv);
     184        }
     185        RTGetOptArgvFree(pThread->papszArgs);
     186        RTStrFree(pThread->pszUser);
     187        RTStrFree(pThread->pszPassword);
     188
     189        rc = RTSemEventMultiDestroy(pThread->RequestEvent);
     190        AssertRC(rc);
     191
     192        VBoxServiceVerbose(3, "ControlThread: [PID %u]: Cleaning up ...\n",
     193                           pThread->uPID);
     194
     195        /* Set stopped status. */
     196        ASMAtomicXchgBool(&pThread->fStopped, true);
     197
     198        rc = RTCritSectLeave(&pThread->CritSect);
     199        AssertRC(rc);
     200    }
     201
     202    /*
     203     * Destroy other thread data.
     204     */
     205    if (RTCritSectIsInitialized(&pThread->CritSect))
     206        RTCritSectDelete(&pThread->CritSect);
     207
     208    /*
     209     * Destroy thread structure as final step.
     210     */
     211    RTMemFree(pThread);
     212    pThread = NULL;
     213
     214    return rc;
     215}
     216
     217
     218/**
    156219 * Signals a guest process thread that we want it to shut down in
    157220 * a gentle way.
     
    164227    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    165228
    166     VBoxServiceVerbose(3, "ControlThead: [PID %u]: Signalling shutdown ...\n",
     229    VBoxServiceVerbose(3, "ControlThread: [PID %u]: Signalling shutdown ...\n",
    167230                       pThread->uPID);
    168231
     
    176239    int rc = VBoxServiceControlThreadPerform(pThread->uPID, &ctrlRequest);
    177240    if (RT_FAILURE(rc))
    178         VBoxServiceVerbose(3, "ControlThead: [PID %u]: Sending quit request failed with rc=%Rrc\n",
     241        VBoxServiceVerbose(3, "ControlThread: [PID %u]: Sending quit request failed with rc=%Rrc\n",
    179242                           pThread->uPID, rc);
    180243    return rc;
     
    197260        && !ASMAtomicReadBool(&pThread->fStopped)) /* Only shutdown threads which aren't yet. */
    198261    {
    199         VBoxServiceVerbose(3, "ControlThead: [PID %u]: Waiting for shutdown ...\n",
     262        VBoxServiceVerbose(2, "ControlThread: [PID %u]: Waiting for shutdown ...\n",
    200263                           pThread->uPID);
    201264
     
    205268        if (RT_FAILURE(rcThread))
    206269        {
    207             VBoxServiceError("ControlThead: [PID %u]: Shutdown returned error rc=%Rrc\n",
     270            VBoxServiceError("ControlThread: [PID %u]: Shutdown returned error rc=%Rrc\n",
    208271                             pThread->uPID, rcThread);
    209272            if (RT_SUCCESS(rc))
     
    216279
    217280/**
    218  * Frees an allocated thread data structure along with all its allocated parameters.
    219  *
    220  * @param   pThread             Pointer to thread data to free.
    221  */
    222 static void vboxServiceControlThreadFree(const PVBOXSERVICECTRLTHREAD pThread)
    223 {
    224     if (!pThread)
    225         return;
    226 
    227     VBoxServiceVerbose(3, "ControlThead: [PID %u]: Freeing thread data ...\n",
    228                        pThread->uPID);
    229 
    230     RTStrFree(pThread->pszCmd);
    231     if (pThread->uNumEnvVars)
    232     {
    233         for (uint32_t i = 0; i < pThread->uNumEnvVars; i++)
    234             RTStrFree(pThread->papszEnv[i]);
    235         RTMemFree(pThread->papszEnv);
    236     }
    237     RTGetOptArgvFree(pThread->papszArgs);
    238     RTStrFree(pThread->pszUser);
    239     RTStrFree(pThread->pszPassword);
    240 
    241     if (pThread->RequestEvent != NIL_RTSEMEVENTMULTI)
    242     {
    243         int rc2 = RTSemEventMultiDestroy(pThread->RequestEvent);
    244         AssertRC(rc2);
    245     }
     281 * Returns the guest process thread's current status.
     282 *
     283 * @return  VBOXSERVICECTRLTHREADSTATUS
     284 * @param   pThread             Thread to determine current status for.
     285 */
     286VBOXSERVICECTRLTHREADSTATUS VBoxServiceControlThreadGetStatus(const PVBOXSERVICECTRLTHREAD pThread)
     287{
     288    AssertPtrReturn(pThread, VBOXSERVICECTRLTHREADSTATUS_UNKNOWN);
     289
     290    int rc = RTCritSectEnter(&pThread->CritSect);
     291    if (RT_SUCCESS(rc))
     292    {
     293        Assert(pThread->fStarted != pThread->fStopped);
     294
     295        VBOXSERVICECTRLTHREADSTATUS enmStatus = VBOXSERVICECTRLTHREADSTATUS_UNKNOWN;
     296        /** @todo Add more logic here. */
     297        /** @todo Remove fStarted/fStopped and just use this VBOXSERVICECTRLTHREADSTATUS. */
     298        if (pThread->fStarted)
     299            enmStatus = VBOXSERVICECTRLTHREADSTATUS_STARTED;
     300        else if (pThread->fStopped)
     301            enmStatus = VBOXSERVICECTRLTHREADSTATUS_STOPPED;
     302        else
     303            AssertMsgFailed(("ControlThread: Uknown thread status (0x%x)\n"));
     304
     305        rc = RTCritSectLeave(&pThread->CritSect);
     306        AssertRC(rc);
     307
     308        return enmStatus;
     309    }
     310
     311    return VBOXSERVICECTRLTHREADSTATUS_UNKNOWN;
    246312}
    247313
     
    294360 *
    295361 */
     362static int VBoxServiceControlThreadHandleOutputError(RTPOLLSET hPollSet, uint32_t fPollEvt,
     363                                                     PRTPIPE phPipeR, uint32_t idPollHnd)
     364{
     365    int rc = VINF_SUCCESS;
     366
     367    rc = RTPollSetRemove(hPollSet, idPollHnd);
     368    AssertRC(rc);
     369
     370    rc = RTPipeClose(*phPipeR);
     371    AssertRC(rc);
     372    *phPipeR = NIL_RTPIPE;
     373
     374    return rc;
     375}
     376
     377
     378/**
     379 * Handle pending output data or error on standard out or standard error.
     380 *
     381 * @returns IPRT status code from client send.
     382 * @param   hPollSet            The polling set.
     383 * @param   fPollEvt            The event mask returned by RTPollNoResume.
     384 * @param   phPipeR             The pipe handle.
     385 * @param   idPollHnd           The pipe ID to handle.
     386 *
     387 */
    296388static int VBoxServiceControlThreadHandleOutputEvent(RTPOLLSET hPollSet, uint32_t fPollEvt,
    297389                                                     PRTPIPE phPipeR, uint32_t idPollHnd)
    298390{
    299391    int rc = VINF_SUCCESS;
    300 
    301 #if 0
    302     if (fPollEvt & RTPOLL_EVT_READ)
    303     {
    304         /** @todo Later: Notify the host about the read operation! */
    305 
    306         /* Make sure we go another poll round in case there was too much data
    307            for the buffer to hold. */
    308         fPollEvt &= RTPOLL_EVT_ERROR;
    309 
    310         /* Remove read event from poll set and just poll for errors from now on.
    311          * This is necessary for doing a RTPipeRead. */
    312         rc = RTPollSetEventsChange(hPollSet, idPollHnd, RTPOLL_EVT_ERROR);
    313         AssertRC(rc);
    314     }
     392    if (fPollEvt == RTPOLL_EVT_READ)
     393    {
     394        char abBuf[_64K];
     395        size_t cbRead;
     396        int rc = RTPipeRead(*phPipeR, abBuf, sizeof(abBuf), &cbRead);
     397        if (RT_SUCCESS(rc) && cbRead)
     398        {
     399#ifdef DEBUG
     400            VBoxServiceVerbose(4, "ControlThread: Drain idPollHnd=%u, abBuf=0x%p, cbRead=%u\n",
     401                               idPollHnd, abBuf, cbRead);
    315402#endif
    316 
     403            /* Goes to bit bucket ... */
     404
     405            /* Make sure we go another poll round in case there was too much data
     406               for the buffer to hold. */
     407            fPollEvt &= RTPOLL_EVT_ERROR;
     408        }
     409        else if (RT_FAILURE(rc))
     410        {
     411            fPollEvt |= RTPOLL_EVT_ERROR;
     412            AssertMsg(rc == VERR_BROKEN_PIPE, ("%Rrc\n", rc));
     413        }
     414    }
     415
     416    if (fPollEvt & RTPOLL_EVT_ERROR)
     417        rc = VBoxServiceControlThreadHandleOutputError(hPollSet, fPollEvt,
     418                                                       phPipeR, idPollHnd);
     419    return rc;
     420}
     421
     422
     423static int VBoxServiceControlThreadHandleRequest(RTPOLLSET hPollSet, uint32_t fPollEvt,
     424                                                 PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR,
     425                                                 PVBOXSERVICECTRLTHREAD pThread)
     426{
    317427#ifdef DEBUG_andy
    318     VBoxServiceVerbose(4, "ControlThead: HandleOutputEvent fPollEvt=0x%x, idPollHnd=%u\n",
    319                        fPollEvt, idPollHnd);
    320 #endif
    321 
    322     /*
    323      * If an error was raised signalled
    324      */
    325     if (fPollEvt & RTPOLL_EVT_ERROR)
    326     {
    327         rc = RTPollSetRemove(hPollSet, idPollHnd);
    328         AssertRC(rc);
    329 
    330         rc = RTPipeClose(*phPipeR);
    331         AssertRC(rc);
    332         *phPipeR = NIL_RTPIPE;
    333     }
    334 
    335     return rc;
    336 }
    337 
    338 
    339 static int VBoxServiceControlThreadHandleIPCRequest(RTPOLLSET hPollSet, uint32_t fPollEvt,
    340                                                     PRTPIPE phStdInW, PRTPIPE phStdOutR, PRTPIPE phStdErrR,
    341                                                     PVBOXSERVICECTRLTHREAD pThread)
    342 {
    343 #ifdef DEBUG_andy
    344     VBoxServiceVerbose(4, "ControlThead: HandleIPCRequest\n");
     428    VBoxServiceVerbose(4, "ControlThread: HandleIPCRequest\n");
    345429#endif
    346430
     
    350434    AssertPtrReturn(phStdErrR, VERR_INVALID_POINTER);
    351435
    352     int rc = RTCritSectEnter(&pThread->CritSect);
     436    /*int rc = RTCritSectEnter(&pThread->CritSect);
    353437    if (RT_SUCCESS(rc))
    354     {
     438    {*/
    355439        /* Drain the notification pipe. */
    356440        uint8_t abBuf[8];
     
    358442        rc = RTPipeRead(pThread->hNotificationPipeR, abBuf, sizeof(abBuf), &cbIgnore);
    359443        if (RT_FAILURE(rc))
    360             VBoxServiceError("ControlThead: Draining IPC notification pipe failed with rc=%Rrc\n", rc);
     444            VBoxServiceError("ControlThread: Draining IPC notification pipe failed with rc=%Rrc\n", rc);
    361445
    362446        int rcReq = VINF_SUCCESS; /* Actual request result. */
     
    438522                     ? rcReq : rc;
    439523
    440         VBoxServiceVerbose(4, "ControlThead: Handled IPC request with rcReq=%Rrc, enmType=%u, cbData=%u\n",
    441                            rcReq, pRequest->enmType, pRequest->cbData);
     524        VBoxServiceVerbose(2, "ControlThread: [PID %u]: Handled req=%u, CID=%u, rcReq=%Rrc, cbData=%u\n",
     525                           pThread->uPID, pRequest->enmType, pRequest->uCID, rcReq, pRequest->cbData);
    442526
    443527        /* In any case, regardless of the result, we notify
     
    448532         * or modified already! */
    449533
    450         rc2 = RTCritSectLeave(&pThread->CritSect);
     534        /*rc2 = RTCritSectLeave(&pThread->CritSect);
    451535        AssertRC(rc2);
    452     }
     536    }*/
    453537
    454538    return rc;
     
    475559    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    476560    AssertPtrReturn(phStdInW, VERR_INVALID_PARAMETER);
    477     AssertPtrReturn(phStdOutR, VERR_INVALID_PARAMETER);
    478     AssertPtrReturn(phStdErrR, VERR_INVALID_PARAMETER);
     561    /* Rest is optional. */
    479562
    480563    int                         rc;
     
    495578     * the first (stale) entry will be found and we get really weird results!
    496579     */
    497     rc = vboxServiceControlThreadAssignPID(pThread, hProcess);
     580    rc = VBoxServiceControlAssignPID(pThread, hProcess);
    498581    if (RT_FAILURE(rc))
    499582    {
    500         VBoxServiceError("ControlThead: Unable to assign PID to new thread, rc=%Rrc\n", rc);
     583        VBoxServiceError("ControlThread: Unable to assign PID=%u, to new thread, rc=%Rrc\n",
     584                         hProcess, rc);
    501585        return rc;
    502586    }
     
    506590     * and that it's now OK to send input to the process.
    507591     */
    508     VBoxServiceVerbose(3, "ControlThead: [PID %u]: Process started, CID=%u, User=%s\n",
    509                        pThread->uPID, pThread->uContextID, pThread->pszUser);
     592    VBoxServiceVerbose(2, "ControlThread: [PID %u]: Process \"%s\" started, CID=%u, User=%s\n",
     593                       pThread->uPID, pThread->pszCmd, pThread->uContextID, pThread->pszUser);
    510594    rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
    511595                                         pThread->uPID, PROC_STS_STARTED, 0 /* u32Flags */,
     
    531615        if (RT_SUCCESS(rc2))
    532616        {
    533             /*VBoxServiceVerbose(4, "ControlThead: [PID %u}: RTPollNoResume idPollHnd=%u\n",
     617            /*VBoxServiceVerbose(4, "ControlThread: [PID %u}: RTPollNoResume idPollHnd=%u\n",
    534618                                 pThread->uPID, idPollHnd);*/
    535619            switch (idPollHnd)
     
    550634
    551635                case VBOXSERVICECTRLPIPEID_IPC_NOTIFY:
    552                     rc = VBoxServiceControlThreadHandleIPCRequest(hPollSet, fPollEvt,
    553                                                                   phStdInW,phStdOutR,phStdErrR, pThread);
     636                    rc = VBoxServiceControlThreadHandleRequest(hPollSet, fPollEvt,
     637                                                               phStdInW, phStdOutR, phStdErrR, pThread);
    554638                    break;
    555639            }
     
    608692            if (cMsElapsed >= cMsTimeout)
    609693            {
    610                 VBoxServiceVerbose(3, "ControlThead: [PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...",
     694                VBoxServiceVerbose(3, "ControlThread: [PID %u]: Timed out (%ums elapsed > %ums timeout), killing ...",
    611695                                   pThread->uPID, cMsElapsed, cMsTimeout);
    612696
     
    644728        if (MsProcessKilled == UINT64_MAX)
    645729        {
    646             VBoxServiceVerbose(3, "ControlThead: [PID %u]: Is still alive and not killed yet\n",
     730            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Is still alive and not killed yet\n",
    647731                               pThread->uPID);
    648732
     
    654738        for (size_t i = 0; i < 10; i++)
    655739        {
    656             VBoxServiceVerbose(4, "ControlThead: [PID %u]: Kill attempt %d/10: Waiting to exit ...\n",
     740            VBoxServiceVerbose(4, "ControlThread: [PID %u]: Kill attempt %d/10: Waiting to exit ...\n",
    657741                               pThread->uPID, i + 1);
    658742            rc2 = RTProcWait(hProcess, RTPROCWAIT_FLAGS_NOBLOCK, &ProcessStatus);
    659743            if (RT_SUCCESS(rc2))
    660744            {
    661                 VBoxServiceVerbose(4, "ControlThead: [PID %u]: Kill attempt %d/10: Exited\n",
     745                VBoxServiceVerbose(4, "ControlThread: [PID %u]: Kill attempt %d/10: Exited\n",
    662746                                   pThread->uPID, i + 1);
    663747                fProcessAlive = false;
     
    666750            if (i >= 5)
    667751            {
    668                 VBoxServiceVerbose(4, "ControlThead: [PID %u]: Kill attempt %d/10: Trying to terminate ...\n",
     752                VBoxServiceVerbose(4, "ControlThread: [PID %u]: Kill attempt %d/10: Trying to terminate ...\n",
    669753                                   pThread->uPID, i + 1);
    670754                RTProcTerminate(hProcess);
     
    674758
    675759        if (fProcessAlive)
    676             VBoxServiceVerbose(3, "ControlThead: [PID %u]: Could not be killed\n", pThread->uPID);
     760            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Could not be killed\n", pThread->uPID);
    677761    }
    678762
     
    688772        if (     fProcessTimedOut  && !fProcessAlive && MsProcessKilled != UINT64_MAX)
    689773        {
    690             VBoxServiceVerbose(3, "ControlThead: [PID %u]: Timed out and got killed\n",
     774            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Timed out and got killed\n",
    691775                               pThread->uPID);
    692776            uStatus = PROC_STS_TOK;
     
    694778        else if (fProcessTimedOut  &&  fProcessAlive && MsProcessKilled != UINT64_MAX)
    695779        {
    696             VBoxServiceVerbose(3, "ControlThead: [PID %u]: Timed out and did *not* get killed\n",
     780            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Timed out and did *not* get killed\n",
    697781                               pThread->uPID);
    698782            uStatus = PROC_STS_TOA;
     
    700784        else if (pThread->fShutdown && (fProcessAlive || MsProcessKilled != UINT64_MAX))
    701785        {
    702             VBoxServiceVerbose(3, "ControlThead: [PID %u]: Got terminated because system/service is about to shutdown\n",
     786            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Got terminated because system/service is about to shutdown\n",
    703787                               pThread->uPID);
    704788            uStatus = PROC_STS_DWN; /* Service is stopping, process was killed. */
     
    707791        else if (fProcessAlive)
    708792        {
    709             VBoxServiceError("ControlThead: [PID %u]: Is alive when it should not!\n",
     793            VBoxServiceError("ControlThread: [PID %u]: Is alive when it should not!\n",
    710794                             pThread->uPID);
    711795        }
    712796        else if (MsProcessKilled != UINT64_MAX)
    713797        {
    714             VBoxServiceError("ControlThead: [PID %u]: Has been killed when it should not!\n",
     798            VBoxServiceError("ControlThread: [PID %u]: Has been killed when it should not!\n",
    715799                             pThread->uPID);
    716800        }
    717801        else if (ProcessStatus.enmReason == RTPROCEXITREASON_NORMAL)
    718802        {
    719             VBoxServiceVerbose(3, "ControlThead: [PID %u]: Ended with RTPROCEXITREASON_NORMAL (%u)\n",
     803            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Ended with RTPROCEXITREASON_NORMAL (%u)\n",
    720804                               pThread->uPID, ProcessStatus.iStatus);
    721805
     
    725809        else if (ProcessStatus.enmReason == RTPROCEXITREASON_SIGNAL)
    726810        {
    727             VBoxServiceVerbose(3, "ControlThead: [PID %u]: Ended with RTPROCEXITREASON_SIGNAL (%u)\n",
     811            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Ended with RTPROCEXITREASON_SIGNAL (%u)\n",
    728812                               pThread->uPID, ProcessStatus.iStatus);
    729813
     
    733817        else if (ProcessStatus.enmReason == RTPROCEXITREASON_ABEND)
    734818        {
    735             VBoxServiceVerbose(3, "ControlThead: [PID %u]: Ended with RTPROCEXITREASON_ABEND (%u)\n",
     819            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Ended with RTPROCEXITREASON_ABEND (%u)\n",
    736820                               pThread->uPID, ProcessStatus.iStatus);
    737821
     
    740824        }
    741825        else
    742             VBoxServiceVerbose(1, "ControlThead: [PID %u]: Handling process status %u not implemented\n",
     826            VBoxServiceVerbose(1, "ControlThread: [PID %u]: Handling process status %u not implemented\n",
    743827                               pThread->uPID, ProcessStatus.enmReason);
    744828
    745         VBoxServiceVerbose(3, "ControlThead: [PID %u]: Sending final status, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
     829        VBoxServiceVerbose(2, "ControlThread: [PID %u]: Ended, ClientID=%u, CID=%u, Status=%u, Flags=0x%x\n",
    746830                           pThread->uPID, pThread->uClientID, pThread->uContextID, uStatus, uFlags);
    747831        rc = VbglR3GuestCtrlExecReportStatus(pThread->uClientID, pThread->uContextID,
     
    749833                                             NULL /* pvData */, 0 /* cbData */);
    750834        if (RT_FAILURE(rc))
    751             VBoxServiceError("ControlThead: [PID %u]: Error reporting final status to host; rc=%Rrc\n",
     835            VBoxServiceError("ControlThread: [PID %u]: Error reporting final status to host; rc=%Rrc\n",
    752836                             pThread->uPID, rc);
    753837
    754         VBoxServiceVerbose(3, "ControlThead: [PID %u]: Process loop ended with rc=%Rrc\n",
     838        VBoxServiceVerbose(3, "ControlThread: [PID %u]: Process loop ended with rc=%Rrc\n",
    755839                           pThread->uPID, rc);
    756840    }
    757841    else
    758         VBoxServiceError("ControlThead: [PID %u]: Loop failed with rc=%Rrc\n",
     842        VBoxServiceError("ControlThread: [PID %u]: Loop failed with rc=%Rrc\n",
    759843                         pThread->uPID, rc);
    760844    return rc;
     845}
     846
     847
     848static int vboxServiceControlThreadInitPipe(PRTHANDLE ph, PRTPIPE phPipe)
     849{
     850    AssertPtrReturn(ph, VERR_INVALID_PARAMETER);
     851    AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
     852
     853    ph->enmType = RTHANDLETYPE_PIPE;
     854    ph->u.hPipe = NIL_RTPIPE;
     855    *phPipe     = NIL_RTPIPE;
     856
     857    return VINF_SUCCESS;
    761858}
    762859
     
    781878    AssertPtrReturn(phPipe, VERR_INVALID_PARAMETER);
    782879
    783     ph->enmType = RTHANDLETYPE_PIPE;
    784     ph->u.hPipe = NIL_RTPIPE;
    785     *pph        = NULL;
    786     *phPipe     = NIL_RTPIPE;
    787 
    788880    int rc;
    789881
     
    833925#endif
    834926#ifdef DEBUG
    835     VBoxServiceVerbose(3, "ControlThead: VBoxServiceControlExecMakeFullPath: %s -> %s\n",
     927    VBoxServiceVerbose(3, "ControlThread: VBoxServiceControlExecMakeFullPath: %s -> %s\n",
    836928                       pszPath, pszExpanded);
    837929#endif
     
    870962        rc = VBoxServiceControlThreadMakeFullPath(pszExecResolved, pszResolved, cbResolved);
    871963#ifdef DEBUG
    872         VBoxServiceVerbose(3, "ControlThead: VBoxServiceControlExecResolveExecutable: %s -> %s\n",
     964        VBoxServiceVerbose(3, "ControlThread: VBoxServiceControlExecResolveExecutable: %s -> %s\n",
    873965                           pszFileName, pszResolved);
    874966#endif
     
    10241116            if (fFlags)
    10251117            {
    1026                 /* Process Main flag "ExecuteProcessFlag_Hidden". */
    1027                 if (fFlags & RT_BIT(2))
     1118                if (fFlags & EXECUTEPROCESSFLAG_HIDDEN)
    10281119                    uProcFlags |= RTPROC_FLAGS_HIDDEN;
    1029                 /* Process Main flag "ExecuteProcessFlag_NoProfile". */
    1030                 if (fFlags & RT_BIT(3))
     1120                if (fFlags & EXECUTEPROCESSFLAG_NO_PROFILE)
    10311121                    uProcFlags |= RTPROC_FLAGS_NO_PROFILE;
    10321122            }
     
    10411131                uProcFlags |= RTPROC_FLAGS_SERVICE;
    10421132#ifdef DEBUG
    1043             VBoxServiceVerbose(3, "ControlThead: Command: %s\n", szExecExp);
     1133            VBoxServiceVerbose(3, "ControlThread: Command: %s\n", szExecExp);
    10441134            for (size_t i = 0; papszArgsExp[i]; i++)
    1045                 VBoxServiceVerbose(3, "ControlThead:\targv[%ld]: %s\n", i, papszArgsExp[i]);
     1135                VBoxServiceVerbose(3, "ControlThread:\targv[%ld]: %s\n", i, papszArgsExp[i]);
    10461136#endif
    10471137            /* Do normal execution. */
     
    10661156{
    10671157    AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    1068     VBoxServiceVerbose(3, "ControlThead: Thread of process \"%s\" started\n", pThread->pszCmd);
     1158    VBoxServiceVerbose(3, "ControlThread: Thread of process \"%s\" started\n", pThread->pszCmd);
    10691159
    10701160    int rc = VbglR3GuestCtrlConnect(&pThread->uClientID);
    10711161    if (RT_FAILURE(rc))
    10721162    {
    1073         VBoxServiceError("ControlThead: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
     1163        VBoxServiceError("ControlThread: Thread failed to connect to the guest control service, aborted! Error: %Rrc\n", rc);
    10741164        RTThreadUserSignal(RTThreadSelf());
    10751165        return rc;
    10761166    }
    1077     VBoxServiceVerbose(3, "ControlThead: Guest process \"%s\" got client ID=%u\n",
    1078                        pThread->pszCmd, pThread->uClientID);
     1167    VBoxServiceVerbose(3, "ControlThread: Guest process \"%s\" got client ID=%u, flags=0x%x\n",
     1168                       pThread->pszCmd, pThread->uClientID, pThread->uFlags);
    10791169
    10801170    bool fSignalled = false; /* Indicator whether we signalled the thread user event already. */
     
    11301220                            /* Stdout. */
    11311221                            if (RT_SUCCESS(rc))
    1132                                 rc = RTPollSetAddPipe(hPollSet, hStdOutR, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDOUT);
     1222                            {
     1223                                uint32_t uFlags = RTPOLL_EVT_ERROR;
     1224                                if (!(pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_STDOUT))
     1225                                {
     1226                                    uFlags |= RTPOLL_EVT_READ;
     1227                                    VBoxServiceVerbose(3, "ControlThread: Host is not interested in getting stdout for \"%s\", poll flags=0x%x\n",
     1228                                                        pThread->pszCmd, uFlags);
     1229                                }
     1230                                rc = RTPollSetAddPipe(hPollSet, hStdOutR, uFlags, VBOXSERVICECTRLPIPEID_STDOUT);
     1231                            }
    11331232                            /* Stderr. */
    11341233                            if (RT_SUCCESS(rc))
    1135                                 rc = RTPollSetAddPipe(hPollSet, hStdErrR, RTPOLL_EVT_ERROR, VBOXSERVICECTRLPIPEID_STDERR);
     1234                            {
     1235                                uint32_t uFlags = RTPOLL_EVT_ERROR;
     1236                                if (!(pThread->uFlags & EXECUTEPROCESSFLAG_WAIT_STDERR))
     1237                                {
     1238                                    uFlags |= RTPOLL_EVT_READ;
     1239                                    VBoxServiceVerbose(3, "ControlThread: Host is not interested in getting stderr for \"%s\", poll flags=0x%x\n",
     1240                                                        pThread->pszCmd, uFlags);
     1241                                }
     1242                                rc = RTPollSetAddPipe(hPollSet, hStdErrR, uFlags, VBOXSERVICECTRLPIPEID_STDERR);
     1243                            }
    11361244                            /* IPC notification pipe. */
    11371245                            if (RT_SUCCESS(rc))
     
    11481256                                                                           &hProcess);
    11491257                                if (RT_FAILURE(rc))
    1150                                     VBoxServiceError("ControlThead: Error starting process, rc=%Rrc\n", rc);
     1258                                    VBoxServiceError("ControlThread: Error starting process, rc=%Rrc\n", rc);
    11511259                                /*
    11521260                                 * Tell the control thread that it can continue
     
    11761284                                                                          hProcess, pThread->uTimeLimitMS, hPollSet,
    11771285                                                                          &pThread->pipeStdInW, &hStdOutR, &hStdErrR);
    1178 
    1179                                     /*
    1180                                      * Remove thread from global thread list. After this it's safe to shutdown
    1181                                      * and deallocate this thread.
    1182                                      */
    1183                                     VBoxServiceControlRemoveThread(pThread);
    1184 
    11851286                                    /*
    11861287                                     * The handles that are no longer in the set have
     
    12031304                                else /* Something went wrong; report error! */
    12041305                                {
    1205                                     VBoxServiceError("ControlThead: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
     1306                                    VBoxServiceError("ControlThread: Could not start process '%s' (CID: %u)! Error: %Rrc\n",
    12061307                                                     pThread->pszCmd, pThread->uContextID, rc);
    12071308
     
    12101311                                                                          NULL /* pvData */, 0 /* cbData */);
    12111312                                    if (RT_FAILURE(rc2))
    1212                                         VBoxServiceError("ControlThead: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
     1313                                        VBoxServiceError("ControlThread: Could not report process start error! Error: %Rrc (process error %Rrc)\n",
    12131314                                                         rc2, rc);
    12141315                                }
     
    12381339    if (pThread->uClientID)
    12391340    {
    1240         VBoxServiceVerbose(3, "ControlThead: [PID %u]: Cancelling pending waits (client ID=%u)\n",
     1341        VBoxServiceVerbose(3, "ControlThread: [PID %u]: Cancelling pending waits (client ID=%u)\n",
    12411342                           pThread->uPID, pThread->uClientID);
    12421343        int rc2 = VbglR3GuestCtrlCancelPendingWaits(pThread->uClientID);
    12431344        if (RT_FAILURE(rc2))
    12441345        {
    1245             VBoxServiceError("ControlThead: [PID %u]: Cancelling pending waits failed; rc=%Rrc\n",
     1346            VBoxServiceError("ControlThread: [PID %u]: Cancelling pending waits failed; rc=%Rrc\n",
    12461347                             pThread->uPID, rc2);
    12471348            if (RT_SUCCESS(rc))
     
    12501351
    12511352        /* Disconnect from guest control service. */
    1252         VBoxServiceError("ControlThead: [PID %u]: Disconnecting (client ID=%u) ...\n",
    1253                          pThread->uPID, pThread->uClientID);
     1353        VBoxServiceVerbose(3, "ControlThread: [PID %u]: Disconnecting (client ID=%u) ...\n",
     1354                           pThread->uPID, pThread->uClientID);
    12541355        VbglR3GuestCtrlDisconnect(pThread->uClientID);
    12551356        pThread->uClientID = 0;
    12561357    }
    12571358
    1258     VBoxServiceVerbose(3, "ControlThead: [PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n",
     1359    VBoxServiceVerbose(3, "ControlThread: [PID %u]: Thread of process \"%s\" ended with rc=%Rrc\n",
    12591360                       pThread->uPID, pThread->pszCmd, rc);
    12601361
     
    12681369        RTThreadUserSignal(RTThreadSelf());
    12691370
    1270     int rc2 = vboxServiceControlThreadShutdown(pThread);
     1371    /*
     1372     * Remove thread from global thread list. After this it's safe to shutdown
     1373     * and deallocate this thread.
     1374     */
     1375    VBoxServiceControlRemoveThread(pThread);
     1376
     1377    int rc2 = vboxServiceControlThreadFree(pThread);
    12711378    if (RT_SUCCESS(rc))
    12721379        rc = rc2;
     
    13481455        if (RT_FAILURE(rc))
    13491456        {
    1350             VBoxServiceError("ControlThead: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
     1457            VBoxServiceError("ControlThread: RTThreadCreate failed, rc=%Rrc\n, pThread=%p\n",
    13511458                             rc, pThread);
    13521459        }
    13531460        else
    13541461        {
    1355             VBoxServiceVerbose(4, "ControlThead: Waiting for thread to initialize ...\n");
     1462            VBoxServiceVerbose(4, "ControlThread: Waiting for thread to initialize ...\n");
    13561463
    13571464            /* Wait for the thread to initialize. */
     
    13591466            if (ASMAtomicReadBool(&pThread->fShutdown))
    13601467            {
    1361                 VBoxServiceError("ControlThead: Thread for process \"%s\" failed to start!\n", pszCmd);
     1468                VBoxServiceError("ControlThread: Thread for process \"%s\" failed to start!\n", pszCmd);
    13621469                rc = VERR_GENERAL_FAILURE;
    13631470            }
     
    13801487
    13811488/**
    1382  * Assigns a valid PID to a guest control thread and also checks if there already was
    1383  * another (stale) guest process which was using that PID before and destroys it.
    1384  *
    1385  * @return  IPRT status code.
    1386  * @param   pData          Pointer to guest control execution thread data.
    1387  * @param   uPID           PID to assign to the specified guest control execution thread.
    1388  */
    1389 int vboxServiceControlThreadAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID)
    1390 {
    1391     AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    1392     AssertReturn(uPID, VERR_INVALID_PARAMETER);
    1393 
    1394     int rc = VINF_SUCCESS;
    1395 
    1396     /* Search an old thread using the desired PID and shut it down completely -- it's
    1397      * not used anymore. */
    1398     PVBOXSERVICECTRLTHREAD pOldThread = VBoxServiceControlGetThreadByPID(uPID);
    1399     if (   pOldThread
    1400         && pOldThread != pThread)
    1401     {
    1402         PVBOXSERVICECTRLTHREAD pNext = RTListNodeGetNext(&pOldThread->Node, VBOXSERVICECTRLTHREAD, Node);
    1403 
    1404         VBoxServiceVerbose(3, "ControlThead: PID %u was used before, shutting down stale exec thread ...\n",
    1405                            uPID);
    1406         rc = vboxServiceControlThreadShutdown(pOldThread);
    1407         if (RT_FAILURE(rc))
    1408         {
    1409             VBoxServiceVerbose(3, "ControlThead: Unable to shut down stale exec thread, rc=%Rrc\n", rc);
    1410             /* Keep going. */
    1411         }
    1412     }
    1413 
    1414     /* Assign PID to current thread. */
    1415     pThread->uPID = uPID;
    1416 
    1417     return rc;
    1418 }
    1419 
    1420 
    1421 /**
    14221489 * Performs a request to a specific (formerly started) guest process and waits
    14231490 * for its response.
     
    14331500    /* Rest in pRequest is optional (based on the request type). */
    14341501
    1435     VBoxServiceVerbose(4, "ControlThead: Performing PID=%u, enmType=%u, pvData=0x%p, cbData=%u ...\n",
    1436                        uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData);
    1437 
    1438     int rc;
    1439     const PVBOXSERVICECTRLTHREAD pThread = VBoxServiceControlGetThreadByPID(uPID);
    1440     if (   pThread
    1441         && !ASMAtomicReadBool(&pThread->fStopped))
    1442     {
    1443         rc = RTCritSectEnter(&pThread->CritSect);
     1502    int rc = VINF_SUCCESS;
     1503    const PVBOXSERVICECTRLTHREAD pThread = VBoxServiceControlGetThreadLocked(uPID);
     1504    if (pThread)
     1505    {
     1506        /* Set request result to some defined state in case
     1507         * it got cancelled. */
     1508        pRequest->rc = VERR_CANCELLED;
     1509
     1510        /* Set request structure pointer. */
     1511        pThread->pRequest = pRequest;
     1512
     1513        /** @todo To speed up simultaneous guest process handling we could add a worker threads
     1514         *        or queue in order to wait for the request to happen. Later. */
     1515
     1516        /* Wake up guest thrad by sending a wakeup byte to the notification pipe so
     1517         * that RTPoll unblocks (returns) and we then can do our requested operation. */
     1518        if (pThread->hNotificationPipeW == NIL_RTPIPE)
     1519            rc = VERR_BROKEN_PIPE;
     1520        size_t cbWritten;
    14441521        if (RT_SUCCESS(rc))
    1445         {
    1446             /* Set request structure pointer. */
    1447             pThread->pRequest = pRequest;
    1448 
    1449             /** @todo To speed up simultaneous guest process handling we could add a worker threads
    1450              *        or queue in order to wait for the request to happen. Later. */
    1451 
    1452             /* Wake up guest thrad by sending a wakeup byte to the notification pipe so
    1453              * that RTPoll unblocks (returns) and we then can do our requested operation. */
    1454             if (pThread->hNotificationPipeW == NIL_RTPIPE)
    1455                 rc = VERR_BROKEN_PIPE;
    1456             size_t cbWritten;
     1522            rc = RTPipeWrite(pThread->hNotificationPipeW, "i", 1, &cbWritten);
     1523
     1524        if (   RT_SUCCESS(rc)
     1525            && cbWritten)
     1526        {
     1527            VBoxServiceVerbose(3, "ControlThread: [PID %u]: Waiting for response on enmType=%u, pvData=0x%p, cbData=%u\n",
     1528                               uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData);
     1529            /* Wait on the request to get completed (or we are asked to abort/shutdown). */
     1530            rc = RTSemEventMultiWait(pThread->RequestEvent, RT_INDEFINITE_WAIT);
    14571531            if (RT_SUCCESS(rc))
    1458                 rc = RTPipeWrite(pThread->hNotificationPipeW, "i", 1, &cbWritten);
    1459 
    1460             /* Make sure we leave the critical section before doing the wait. */
    1461             int rc2 = RTCritSectLeave(&pThread->CritSect);
    1462             AssertRCReturn(rc2, rc2);
    1463 
    1464             if (RT_SUCCESS(rc) && cbWritten)
    14651532            {
    1466                 VBoxServiceVerbose(4, "ControlThead: [PID %u]: Waiting for response on enmType=%u, pvData=0x%p, cbData=%u\n",
    1467                                    uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData);
    1468                 /* Wait on the request to get completed (or we are asked to abort/shutdown). */
    1469                 rc = RTSemEventMultiWait(pThread->RequestEvent, RT_INDEFINITE_WAIT);
    1470                 if (RT_SUCCESS(rc))
    1471                 {
    1472                     VBoxServiceVerbose(4, "ControlThead: [PID %u]: Performed with rc=%Rrc, cbData=%u\n",
    1473                                        uPID, pRequest->rc, pRequest->cbData);
    1474 
    1475                     rc = RTCritSectEnter(&pThread->CritSect);
    1476                     if (RT_SUCCESS(rc))
    1477                     {
    1478                         /* Give back overall request result. */
    1479                         rc = pRequest->rc;
    1480 
    1481                         /* Reset the semaphore. */
    1482                         rc2 = RTSemEventMultiReset(pThread->RequestEvent);
    1483                         if (RT_FAILURE(rc2))
    1484                             VBoxServiceError("ControlThead: Unable to reset request event, rc=%Rrc\n", rc2);
    1485 
    1486                         rc2 = RTCritSectLeave(&pThread->CritSect);
    1487                         AssertRCReturn(rc2, rc2);
    1488                     }
    1489                 }
     1533                VBoxServiceVerbose(4, "ControlThread: [PID %u]: Performed with rc=%Rrc, cbData=%u\n",
     1534                                   uPID, pRequest->rc, pRequest->cbData);
     1535
     1536                /* Give back overall request result. */
     1537                rc = pRequest->rc;
     1538
     1539                /* Reset the semaphore. */
     1540                int rc2 = RTSemEventMultiReset(pThread->RequestEvent);
     1541                if (RT_FAILURE(rc2))
     1542                    VBoxServiceError("ControlThread: [PID %u]: Unable to reset request event, rc=%Rrc\n",
     1543                                     uPID, rc2);
    14901544            }
    1491         }
     1545            else
     1546                VBoxServiceError("ControlThread: [PID %u]: Wait failed, rc=%Rrc\n",
     1547                                 uPID, rc);
     1548        }
     1549
     1550        VBoxServiceControlThreadUnlock(pThread);
    14921551    }
    14931552    else /* PID not found! */
    14941553        rc = VERR_NOT_FOUND;
    14951554
    1496     VBoxServiceVerbose(4, "ControlThead: Performed PID=%u, enmType=%u, pvData=0x%p, cbData=%u with rc=%Rrc\n",
    1497                        uPID, pRequest->enmType, pRequest->pvData, pRequest->cbData, rc);
    1498     return rc;
    1499 }
    1500 
    1501 
    1502 /**
    1503  * Shuts down a guest thread.
    1504  *
    1505  * @return  IPRT status code.
    1506  * @param   pThread                 Thread to shut down.
    1507  */
    1508 static int vboxServiceControlThreadShutdown(PVBOXSERVICECTRLTHREAD pThread)
    1509 {
    1510     AssertPtrReturn(pThread, VERR_INVALID_POINTER);
    1511 
    1512     int rc = RTCritSectEnter(&pThread->CritSect);
    1513     if (RT_SUCCESS(rc))
    1514     {
    1515         VBoxServiceVerbose(3, "ControlThead: [PID %u]: Shutting down ...\n",
    1516                            pThread->uPID);
    1517 
    1518         /*
    1519          * Destroy thread-specific data.
    1520          */
    1521         vboxServiceControlThreadFree(pThread);
    1522 
    1523         /* Set stopped status. */
    1524         ASMAtomicXchgBool(&pThread->fStopped, true);
    1525 
    1526         rc = RTCritSectLeave(&pThread->CritSect);
    1527     }
    1528 
    1529     /*
    1530      * Destroy other thread data.
    1531      */
    1532     if (RTCritSectIsInitialized(&pThread->CritSect))
    1533         RTCritSectDelete(&pThread->CritSect);
    1534 
    1535     /*
    1536      * Destroy thread structure as final step.
    1537      */
    1538     RTMemFree(pThread);
    1539     pThread = NULL;
    1540 
    1541     return rc;
    1542 }
    1543 
     1555    VBoxServiceVerbose(3, "ControlThread: [PID %u]: Performed enmType=%u, uCID=%u, pvData=0x%p, cbData=%u, rc=%Rrc\n",
     1556                       uPID, pRequest->enmType, pRequest->uCID, pRequest->pvData, pRequest->cbData, rc);
     1557    return rc;
     1558}
     1559
  • trunk/src/VBox/Additions/common/VBoxService/VBoxServiceInternal.h

    r39392 r39418  
    171171    /** Payload data; a pre-allocated data buffer for input/output. */
    172172    void                      *pvData;
     173    /** The context ID which is required to complete the
     174     *  request. */
     175    uint32_t                   uCID;
    173176    /** The overall result of the operation. */
    174177    int                        rc;
     
    230233/** Pointer to thread data. */
    231234typedef VBOXSERVICECTRLTHREAD *PVBOXSERVICECTRLTHREAD;
     235
     236/**
     237 * Request types to perform on a started guest process.
     238 */
     239typedef enum VBOXSERVICECTRLTHREADSTATUS
     240{
     241    /** Unknown status. Do not use / should not happen. */
     242    VBOXSERVICECTRLTHREADSTATUS_UNKNOWN          = 0,
     243    VBOXSERVICECTRLTHREADSTATUS_STARTED          = 100,
     244    VBOXSERVICECTRLTHREADSTATUS_STOPPED          = 200
     245} VBOXSERVICECTRLTHREADSTATUS;
    232246#endif /* VBOX_WITH_GUEST_CONTROL */
    233247#ifdef VBOX_WITH_GUEST_PROPS
     
    329343extern int          VBoxServiceControlHandleCmdSetInput(uint32_t u32ClientId, uint32_t uNumParms, size_t cbMaxBufSize);
    330344extern int          VBoxServiceControlHandleCmdGetOutput(uint32_t u32ClientId, uint32_t uNumParms);
    331 extern PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadByPID(uint32_t uPID);
    332 extern void         VBoxServiceControlRemoveThread(PVBOXSERVICECTRLTHREAD pThread);
     345extern const PVBOXSERVICECTRLTHREAD VBoxServiceControlGetThreadLocked(uint32_t uPID);
     346extern void         VBoxServiceControlThreadUnlock(const PVBOXSERVICECTRLTHREAD pThread);
     347extern int          VBoxServiceControlAssignPID(PVBOXSERVICECTRLTHREAD pThread, uint32_t uPID);
     348extern void         VBoxServiceControlRemoveThread(const PVBOXSERVICECTRLTHREAD pThread);
    333349/* Guest process functions. */
     350extern VBOXSERVICECTRLTHREADSTATUS VBoxServiceControlThreadGetStatus(const PVBOXSERVICECTRLTHREAD pThread);
    334351extern int          VBoxServiceControlThreadStart(uint32_t uClientID, uint32_t uContext,
    335352                                                  const char *pszCmd, uint32_t uFlags,
     
    338355                                                  const char *pszUser, const char *pszPassword, uint32_t uTimeLimitMS,
    339356                                                  PRTLISTNODE *ppNode);
    340 int                 VBoxServiceControlThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest);
     357extern int          VBoxServiceControlThreadPerform(uint32_t uPID, PVBOXSERVICECTRLREQUEST pRequest);
    341358extern int          VBoxServiceControlThreadSignalShutdown(const PVBOXSERVICECTRLTHREAD pThread);
    342359extern int          VBoxServiceControlThreadWaitForShutdown(const PVBOXSERVICECTRLTHREAD pThread, RTMSINTERVAL msTimeout);
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp

    r38656 r39418  
    570570
    571571            case GETOPTDEF_EXEC_WAITFORSTDOUT:
    572                 fWaitForExit = true;
    573                 fWaitForStdOut = true;
     572                fExecFlags |= ExecuteProcessFlag_WaitForStdOut;
     573                fWaitForExit = fWaitForStdOut = true;
    574574                break;
    575575
    576576            case GETOPTDEF_EXEC_WAITFORSTDERR:
     577                fExecFlags |= ExecuteProcessFlag_WaitForStdErr;
    577578                fWaitForExit = (fOutputFlags |= ProcessOutputFlag_StdErr) ? true : false;
    578579                break;
     
    662663        if (fCancelable)
    663664            ctrlSignalHandlerInstall();
     665
     666        vrc = RTStrmSetMode(g_pStdOut, 1 /* Binary mode */, -1 /* Code set, unchanged */);
     667        if (RT_FAILURE(vrc))
     668            RTMsgError("Unable to set stdout's binary mode, rc=%Rrc\n", vrc);
    664669
    665670        /* Wait for process to exit ... */
     
    711716                    if (cbOutputData > 0)
    712717                    {
     718                        BYTE *pBuf = aOutputData.raw();
     719                        AssertPtr(pBuf);
     720                        pBuf[cbOutputData - 1] = 0; /* Properly terminate buffer. */
     721
    713722                        /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a
    714723                        *        generic problem and the new VFS APIs will handle it more
     
    721730                         * Windows. Otherwise we end up with CR/CR/LF on Windows.
    722731                         */
    723                         ULONG cbOutputDataPrint = cbOutputData;
    724                         for (BYTE *s = aOutputData.raw(), *d = s;
    725                              s - aOutputData.raw() < (ssize_t)cbOutputData;
    726                              s++, d++)
     732
     733                        char *pszBufUTF8;
     734                        vrc = RTStrCurrentCPToUtf8(&pszBufUTF8, (const char*)aOutputData.raw());
     735                        if (RT_SUCCESS(vrc))
    727736                        {
    728                             if (*s == '\r')
     737                            cbOutputData = strlen(pszBufUTF8);
     738
     739                            ULONG cbOutputDataPrint = cbOutputData;
     740                            for (char *s = pszBufUTF8, *d = s;
     741                                 s - pszBufUTF8 < (ssize_t)cbOutputData;
     742                                 s++, d++)
    729743                            {
    730                                 /* skip over CR, adjust destination */
    731                                 d--;
    732                                 cbOutputDataPrint--;
     744                                if (*s == '\r')
     745                                {
     746                                    /* skip over CR, adjust destination */
     747                                    d--;
     748                                    cbOutputDataPrint--;
     749                                }
     750                                else if (s != d)
     751                                    *d = *s;
    733752                            }
    734                             else if (s != d)
    735                                 *d = *s;
     753
     754                            vrc = RTStrmWrite(g_pStdOut, pszBufUTF8, cbOutputDataPrint);
     755                            if (RT_FAILURE(vrc))
     756                                RTMsgError("Unable to write output, rc=%Rrc\n", vrc);
     757
     758                            RTStrFree(pszBufUTF8);
    736759                        }
    737                         RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
     760                        else
     761                            RTMsgError("Unable to convert output, rc=%Rrc\n", vrc);
    738762                    }
    739763                }
     
    22402264    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
    22412265
    2242 #ifdef DEBUG_andy
     2266#ifdef DEBUG_andy_disabled
    22432267    if (RT_FAILURE(tstTranslatePath()))
    22442268        return RTEXITCODE_FAILURE;
     
    22902314
    22912315        ctrlUninitVM(pArg);
    2292         return rcExit;
     2316        return RT_FAILURE(rcExit) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
    22932317    }
    22942318    return RTEXITCODE_FAILURE;
  • trunk/src/VBox/Main/idl/VirtualBox.xidl

    r39058 r39418  
    84388438  <enum
    84398439    name="ExecuteProcessFlag"
    8440     uuid="286ceb91-5f66-4c96-9845-4483e90e00ae"
     8440    uuid="1c49b831-b2c7-4a30-97dd-999a2e2cbf90"
    84418441    >
    84428442    <desc>
     
    84558455    </const>
    84568456    <const name="Hidden"                  value="4">
    8457       <desc>Don't show the started process according to the guest OS guidelines.</desc>
     8457      <desc>Do not show the started process according to the guest OS guidelines.</desc>
    84588458    </const>
    84598459    <const name="NoProfile"               value="8">
    84608460      <desc>Do not use the user's profile data when exeuting a process. Only available for Windows guests.</desc>
     8461    </const>
     8462    <const name="WaitForStdOut"           value="16">
     8463      <desc>The guest process waits until all data from stdout is read out.</desc>
     8464    </const>
     8465    <const name="WaitForStdErr"           value="32">
     8466      <desc>The guest process waits until all data from stderr is read out.</desc>
    84618467    </const>
    84628468  </enum>
  • trunk/src/VBox/Main/include/GuestCtrlImplPrivate.h

    r38439 r39418  
    121121    uint32_t GetOffset();
    122122
     123    uint32_t GetSize();
     124
    123125    int ParseBlock(GuestProcessStreamBlock &streamBlock);
    124126
  • trunk/src/VBox/Main/include/GuestImpl.h

    r38439 r39418  
    153153                                  ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
    154154                                  IN_BSTR aUsername, IN_BSTR aPassword,
     155                                  ULONG uFlagsToAdd,
     156                                  GuestCtrlStreamObjects *pObjStdOut, GuestCtrlStreamObjects *pObjStdErr,
    155157                                  IProgress **aProgress, ULONG *aPID);
    156158    HRESULT executeProcessInternal(IN_BSTR aCommand, ULONG aFlags,
     
    158160                                   IN_BSTR aUsername, IN_BSTR aPassword,
    159161                                   ULONG aTimeoutMS, ULONG *aPID, IProgress **aProgress, int *pRC);
     162    HRESULT getProcessOutputInternal(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS,
     163                                     LONG64 aSize, ComSafeArrayOut(BYTE, aData), int *pRC);
    160164    HRESULT executeProcessResult(const char *pszCommand, const char *pszUser, ULONG ulTimeout, PCALLBACKDATAEXECSTATUS pExecStatus, ULONG *puPID);
    161165    HRESULT executeStreamQueryFsObjInfo(IN_BSTR aObjName,GuestProcessStreamBlock &streamBlock, PRTFSOBJINFO pObjInfo, RTFSOBJATTRADD enmAddAttribs);
    162     int     executeStreamDrain(ULONG aPID, GuestProcessStream &stream);
    163     int     executeStreamGetNextBlock(ULONG aPID, GuestProcessStream &stream, GuestProcessStreamBlock &streamBlock);
    164     HRESULT executeStreamParse(ULONG aPID, GuestCtrlStreamObjects &streamObjects);
     166    int     executeStreamDrain(ULONG aPID, ULONG ulFlags, GuestProcessStream &stream);
     167    int     executeStreamGetNextBlock(ULONG ulPID, ULONG ulFlags, GuestProcessStream &stream, GuestProcessStreamBlock &streamBlock);
     168    int     executeStreamParseNextBlock(ULONG ulPID, ULONG ulFlags, GuestProcessStream &stream, GuestProcessStreamBlock &streamBlock);
     169    HRESULT executeStreamParse(ULONG ulPID, ULONG ulFlags, GuestCtrlStreamObjects &streamObjects);
    165170    HRESULT executeWaitForStatusChange(ULONG uPID, ULONG uTimeoutMS, ExecuteProcessStatus_T *pRetStatus, ULONG *puRetExitCode);
    166171    // Internal guest file functions
     
    229234    typedef std::map< uint32_t, VBOXGUESTCTRL_PROCESS >::const_iterator GuestProcessMapIterConst;
    230235
    231     int processAdd(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags);
    232     int processGetByPID(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess);
    233     int processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags);
     236    int  processGetStatus(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess);
     237    int  processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags);
    234238
    235239    // Internal guest directory representation.
  • trunk/src/VBox/Main/src-client/GuestCtrlIO.cpp

    r38439 r39418  
    318318}
    319319
     320uint32_t GuestProcessStream::GetSize()
     321{
     322    return m_cbSize;
     323}
     324
    320325/**
    321326 * Tries to parse the next upcoming pair block within the internal
  • trunk/src/VBox/Main/src-client/GuestCtrlImpl.cpp

    r38627 r39418  
    162162    AssertReturnVoid(uContextID);
    163163
    164     LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));
    165 
    166     /* Notify callback (if necessary). */
    167     int rc = callbackNotifyEx(uContextID, VERR_CANCELLED,
    168                               Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));
    169     AssertRC(rc);
    170 
    171164    CallbackMapIter it = mCallbackMap.find(uContextID);
    172165    if (it != mCallbackMap.end())
    173166    {
     167        LogFlowFunc(("Callback with CID=%u found\n", uContextID));
    174168        if (it->second.pvData)
    175169        {
     170            LogFlowFunc(("Destroying callback with CID=%u ...\n", uContextID));
     171
    176172            callbackFreeUserData(it->second.pvData);
    177173            it->second.cbData = 0;
     
    385381
    386382/**
    387  * TODO
     383 * Notifies a specified callback about its final status.
    388384 *
    389385 * @return  IPRT status code.
     
    395391{
    396392    AssertReturn(uContextID, VERR_INVALID_PARAMETER);
    397 
    398     LogFlowFunc(("Notifying callback with CID=%u, iRC=%d, pszMsg=%s ...\n",
     393    if (RT_FAILURE(iRC))
     394        AssertReturn(pszMessage, VERR_INVALID_PARAMETER);
     395
     396    LogFlowFunc(("Checking whether callback (CID=%u) needs notification iRC=%Rrc, pszMsg=%s\n",
    399397                 uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
    400398
     
    430428            && !fCompleted)
    431429        {
     430            LogFlowFunc(("Notifying callback with CID=%u, iRC=%Rrc, pszMsg=%s\n",
     431                         uContextID, iRC, pszMessage ? pszMessage : "<No message given>"));
     432
    432433            /*
    433434             * To get waitForCompletion completed (unblocked) we have to notify it if necessary (only
     
    437438             * progress object to become signalled.
    438439             */
    439             if (   RT_SUCCESS(iRC)
    440                 && !pszMessage)
     440            if (RT_SUCCESS(iRC))
    441441            {
    442442                hRC = pProgress->notifyComplete(S_OK);
     
    444444            else
    445445            {
    446                 AssertPtrReturn(pszMessage, VERR_INVALID_PARAMETER);
     446
    447447                hRC = pProgress->notifyComplete(VBOX_E_IPRT_ERROR /* Must not be S_OK. */,
    448448                                                COM_IIDOF(IGuest),
     
    450450                                                pszMessage);
    451451            }
    452         }
    453         ComAssertComRC(hRC);
     452
     453            LogFlowFunc(("Notified callback with CID=%u returned %Rhrc (0x%x)\n",
     454                         uContextID, hRC, hRC));
     455        }
     456        else
     457            LogFlowFunc(("Callback with CID=%u already notified\n", uContextID));
    454458
    455459        /*
     
    564568    if (RT_SUCCESS(vrc))
    565569    {
     570        LogFlowFunc(("Waiting for callback completion (CID=%u, Stage=%RI32, timeout=%RI32ms) ...\n",
     571                     uContextID, lStage, lTimeout));
    566572        HRESULT rc;
    567573        if (lStage < 0)
     
    579585    }
    580586
     587    LogFlowFunc(("Callback (CID=%u) completed with rc=%Rrc\n",
     588                 uContextID, vrc));
    581589    return vrc;
    582590}
     
    603611     */
    604612#ifdef DEBUG_andy
    605     LogFlowFunc(("pvExtension = %p, u32Function = %d, pvParms = %p, cbParms = %d\n",
     613    LogFlowFunc(("pvExtension=%p, u32Function=%d, pvParms=%p, cbParms=%d\n",
    606614                 pvExtension, u32Function, pvParms, cbParms));
    607615#endif
     
    686694        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    687695
     696        LogFlowFunc(("Execution status (CID=%u, pData=0x%p)\n",
     697                     uContextID, pData));
     698
    688699        PCALLBACKDATAEXECSTATUS pCallbackData =
    689700            (PCALLBACKDATAEXECSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
     
    695706            /** @todo Copy void* buffer contents? */
    696707        }
    697         else
    698             AssertReleaseMsgFailed(("Process status (PID=%u, CID=%u) does not have allocated callback data!\n",
    699                                     pData->u32PID, uContextID));
    700     }
    701 
    702     int vrc = VINF_SUCCESS;
     708        /* If pCallbackData is NULL this might be an old request for which no user data
     709         * might exist anymore. */
     710    }
     711
     712    int vrc = VINF_SUCCESS; /* Function result. */
     713    int rcCallback = VINF_SUCCESS; /* Callback result. */
    703714    Utf8Str errMsg;
    704715
    705716    /* Was progress canceled before? */
    706     bool fCbCanceled = callbackIsCanceled(uContextID);
    707     if (!fCbCanceled)
    708     {
     717    bool fCanceled = callbackIsCanceled(uContextID);
     718    if (!fCanceled)
     719    {
     720        /* Handle process map. This needs to be done first in order to have a valid
     721         * map in case some callback gets notified a bit below. */
     722
     723        /* Note: PIDs never get removed here in case the guest process signalled its
     724         *       end; instead the next call of GetProcessStatus() will remove the PID
     725         *       from the process map after we got the process' final (exit) status.
     726         *       See waitpid() for an example. */
     727        if (pData->u32PID) /* Only add/change a process if it has a valid PID (>0). */
     728        {
     729            switch (pData->u32Status)
     730            {
     731                /* Interprete u32Flags as the guest process' exit code. */
     732                case PROC_STS_TES:
     733                case PROC_STS_TOK:
     734                    vrc = processSetStatus(pData->u32PID,
     735                                           (ExecuteProcessStatus_T)pData->u32Status,
     736                                           pData->u32Flags /* Exit code. */, 0 /* Flags. */);
     737                    break;
     738                /* Just reach through flags. */
     739                default:
     740                    vrc = processSetStatus(pData->u32PID,
     741                                           (ExecuteProcessStatus_T)pData->u32Status,
     742                                           0 /* Exit code. */, pData->u32Flags);
     743                    break;
     744            }
     745        }
     746
    709747        /* Do progress handling. */
    710748        switch (pData->u32Status)
     
    725763                errMsg = Utf8StrFmt(Guest::tr("Process terminated abnormally with status '%u'"),
    726764                                    pData->u32Flags);
     765                rcCallback = VERR_GENERAL_FAILURE; /** @todo */
    727766                break;
    728767
     
    732771                errMsg = Utf8StrFmt(Guest::tr("Process terminated via signal with status '%u'"),
    733772                                    pData->u32Flags);
     773                rcCallback = VERR_GENERAL_FAILURE; /** @todo */
    734774                break;
    735775
     
    737777                LogRel(("Guest process (PID %u) timed out and was killed\n", pData->u32PID)); /** @todo Add process name */
    738778                errMsg = Utf8StrFmt(Guest::tr("Process timed out and was killed"));
     779                rcCallback = VERR_TIMEOUT;
    739780                break;
    740781
     
    742783                LogRel(("Guest process (PID %u) timed out and could not be killed\n", pData->u32PID)); /** @todo Add process name */
    743784                errMsg = Utf8StrFmt(Guest::tr("Process timed out and could not be killed"));
     785                rcCallback = VERR_TIMEOUT;
    744786                break;
    745787
     
    758800                }
    759801                else
     802                {
    760803                    errMsg = Utf8StrFmt(Guest::tr("Process killed because system is shutting down"));
     804                    rcCallback = VERR_CANCELLED;
     805                }
    761806                break;
    762807
    763808            case PROC_STS_ERROR:
    764                 LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
    765                         pData->u32PID, pData->u32Flags)); /** @todo Add process name */
     809                if (pData->u32PID)
     810                {
     811                    LogRel(("Guest process (PID %u) could not be started because of rc=%Rrc\n",
     812                            pData->u32PID, pData->u32Flags)); /** @todo Add process name */
     813                }
     814                else
     815                {
     816                    switch (pData->u32Flags)
     817                    {
     818                        case VERR_MAX_PROCS_REACHED:
     819                            LogRel(("Guest process could not be started because maximum number of parallel guest processes has been reached\n"));
     820                            break;
     821
     822                        default:
     823                            LogRel(("Guest process could not be started because of rc=%Rrc\n",
     824                                    pData->u32Flags));
     825                    }
     826
     827                }
    766828                errMsg = Utf8StrFmt(Guest::tr("Process execution failed with rc=%Rrc"), pData->u32Flags);
     829                rcCallback = pData->u32Flags; /* Report back rc. */
    767830                break;
    768831
     
    771834                break;
    772835        }
    773 
    774         /* Handle process map. */
    775         /** @todo What happens on/deal with PID reuse? */
    776         /** @todo How to deal with multiple updates at once? */
     836    }
     837    else
     838    {
     839        errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
     840        rcCallback = VERR_CANCELLED;
     841    }
     842
     843    /* Do we need to handle the callback error? */
     844    if (RT_FAILURE(rcCallback))
     845    {
     846        AssertMsg(!errMsg.isEmpty(), ("Error message must not be empty!\n"));
     847
     848        /* Notify all callbacks which are still waiting on something
     849         * which is related to the current PID. */
    777850        if (pData->u32PID)
    778851        {
    779             VBOXGUESTCTRL_PROCESS process;
    780             vrc = processGetByPID(pData->u32PID, &process);
    781             if (vrc == VERR_NOT_FOUND)
    782             {
    783                 /* Not found, add to map. */
    784                 vrc = processAdd(pData->u32PID,
    785                                  (ExecuteProcessStatus_T)pData->u32Status,
    786                                  pData->u32Flags /* Contains exit code. */,
    787                                  0 /*Flags. */);
    788                 AssertRC(vrc);
    789             }
    790             else if (RT_SUCCESS(vrc))
    791             {
    792                 /* Process found, update process map. */
    793                 vrc = processSetStatus(pData->u32PID,
    794                                        (ExecuteProcessStatus_T)pData->u32Status,
    795                                        pData->u32Flags /* Contains exit code. */,
    796                                        0 /*Flags. */);
    797                 AssertRC(vrc);
    798             }
    799             else
    800                 AssertReleaseMsgFailed(("Process was neither found nor absent!?\n"));
    801         }
    802     }
    803     else
    804         errMsg = Utf8StrFmt(Guest::tr("Process execution canceled"));
    805 
    806     if (!callbackIsComplete(uContextID))
    807     {
    808         if (   errMsg.length()
    809             || fCbCanceled) /* If canceled we have to report E_FAIL! */
    810         {
    811             /* Notify all callbacks which are still waiting on something
    812              * which is related to the current PID. */
    813             if (pData->u32PID)
    814             {
    815                 vrc = callbackNotifyAllForPID(pData->u32PID, VERR_GENERAL_FAILURE, errMsg.c_str());
    816                 if (RT_FAILURE(vrc))
    817                     LogFlowFunc(("Failed to notify other callbacks for PID=%u\n",
    818                                  pData->u32PID));
    819             }
    820 
    821             /* Let the caller know what went wrong ... */
    822             int rc2 = callbackNotifyEx(uContextID, VERR_GENERAL_FAILURE, errMsg.c_str());
     852            int rc2 = callbackNotifyAllForPID(pData->u32PID, rcCallback, errMsg.c_str());
    823853            if (RT_FAILURE(rc2))
    824854            {
    825                 LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n",
    826                              uContextID, pData->u32PID));
    827 
     855                LogFlowFunc(("Failed to notify other callbacks for PID=%u\n",
     856                             pData->u32PID));
    828857                if (RT_SUCCESS(vrc))
    829858                    vrc = rc2;
    830859            }
    831             LogFlowFunc(("Process (CID=%u, status=%u) reported error: %s\n",
    832                          uContextID, pData->u32Status, errMsg.c_str()));
    833         }
    834     }
    835     LogFlowFunc(("Returned with rc=%Rrc\n", vrc));
     860        }
     861
     862        /* Let the caller know what went wrong ... */
     863        int rc2 = callbackNotifyEx(uContextID, rcCallback, errMsg.c_str());
     864        if (RT_FAILURE(rc2))
     865        {
     866            LogFlowFunc(("Failed to notify callback CID=%u for PID=%u\n",
     867                         uContextID, pData->u32PID));
     868            if (RT_SUCCESS(vrc))
     869                vrc = rc2;
     870        }
     871        LogFlowFunc(("Process (CID=%u, status=%u) reported: %s\n",
     872                     uContextID, pData->u32Status, errMsg.c_str()));
     873    }
     874    LogFlowFunc(("Returned with rc=%Rrc, rcCallback=%Rrc\n",
     875                 vrc, rcCallback));
    836876    return vrc;
    837877}
     
    851891        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    852892
     893        LogFlowFunc(("Output status (CID=%u, pData=0x%p)\n",
     894                     uContextID, pData));
     895
    853896        PCALLBACKDATAEXECOUT pCallbackData =
    854897            (PCALLBACKDATAEXECOUT)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
     
    878921            }
    879922        }
    880         else
    881             AssertReleaseMsgFailed(("Process output status (PID=%u, CID=%u) does not have allocated callback data!\n",
    882                                     pData->u32PID, uContextID));
     923        /* If pCallbackData is NULL this might be an old request for which no user data
     924         * might exist anymore. */
    883925    }
    884926
     
    909951        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    910952
     953        LogFlowFunc(("Input status (CID=%u, pData=0x%p)\n",
     954                     uContextID, pData));
     955
    911956        PCALLBACKDATAEXECINSTATUS pCallbackData =
    912957            (PCALLBACKDATAEXECINSTATUS)callbackGetUserDataMutableRaw(uContextID, NULL /* cbData */);
     
    915960            /* Save bytes processed. */
    916961            pCallbackData->cbProcessed = pData->cbProcessed;
    917             pCallbackData->u32Status = pData->u32Status;
    918             pCallbackData->u32Flags = pData->u32Flags;
    919             pCallbackData->u32PID = pData->u32PID;
    920         }
    921         else
    922             AssertReleaseMsgFailed(("Process input status (PID=%u, CID=%u) does not have allocated callback data!\n",
    923                                     pData->u32PID, uContextID));
     962            pCallbackData->u32Status   = pData->u32Status;
     963            pCallbackData->u32Flags    = pData->u32Flags;
     964            pCallbackData->u32PID      = pData->u32PID;
     965        }
     966        /* If pCallbackData is NULL this might be an old request for which no user data
     967         * might exist anymore. */
    924968    }
    925969
     
    936980    Assert(uContextID);
    937981
    938     return callbackNotifyEx(uContextID, S_OK,
     982    LogFlowFunc(("Client disconnected (CID=%u)\n,", uContextID));
     983
     984    return callbackNotifyEx(uContextID, VERR_CANCELLED,
    939985                            Guest::tr("Client disconnected"));
    940986}
    941987
    942 int Guest::processAdd(uint32_t u32PID, ExecuteProcessStatus_T enmStatus,
    943                       uint32_t uExitCode, uint32_t uFlags)
     988/**
     989 * Gets guest process information. Removes the process from the map
     990 * after the process was marked as exited/terminated.
     991 *
     992 * @return  IPRT status code.
     993 * @param   u32PID
     994 * @param   pProcess
     995 */
     996int Guest::processGetStatus(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess)
    944997{
    945998    AssertReturn(u32PID, VERR_INVALID_PARAMETER);
    946999
     1000    AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
     1001
     1002    GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
     1003    if (it != mGuestProcessMap.end())
     1004    {
     1005        if (pProcess)
     1006        {
     1007            pProcess->mStatus   = it->second.mStatus;
     1008            pProcess->mExitCode = it->second.mExitCode;
     1009            pProcess->mFlags    = it->second.mFlags;
     1010        }
     1011
     1012        /* If the is marked as stopped/terminated
     1013         * remove it from the map. */
     1014        if (it->second.mStatus != ExecuteProcessStatus_Started)
     1015            mGuestProcessMap.erase(it);
     1016
     1017        return VINF_SUCCESS;
     1018    }
     1019
     1020    return VERR_NOT_FOUND;
     1021}
     1022
     1023int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)
     1024{
     1025    AssertReturn(u32PID, VERR_INVALID_PARAMETER);
     1026
    9471027    AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    9481028
    949     GuestProcessMapIterConst it = mGuestProcessMap.find(u32PID);
    950     if (it == mGuestProcessMap.end())
     1029    GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
     1030    if (it != mGuestProcessMap.end())
     1031    {
     1032        it->second.mStatus = enmStatus;
     1033        it->second.mExitCode = uExitCode;
     1034        it->second.mFlags = uFlags;
     1035    }
     1036    else
    9511037    {
    9521038        VBOXGUESTCTRL_PROCESS process;
     
    9571043
    9581044        mGuestProcessMap[u32PID] = process;
    959 
    960         return VINF_SUCCESS;
    961     }
    962 
    963     return VERR_ALREADY_EXISTS;
    964 }
    965 
    966 int Guest::processGetByPID(uint32_t u32PID, PVBOXGUESTCTRL_PROCESS pProcess)
    967 {
    968     AssertReturn(u32PID, VERR_INVALID_PARAMETER);
    969     AssertPtrReturn(pProcess, VERR_INVALID_PARAMETER);
    970 
    971     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    972 
    973     GuestProcessMapIterConst it = mGuestProcessMap.find(u32PID);
    974     if (it != mGuestProcessMap.end())
    975     {
    976         pProcess->mStatus = it->second.mStatus;
    977         pProcess->mExitCode = it->second.mExitCode;
    978         pProcess->mFlags = it->second.mFlags;
    979 
    980         return VINF_SUCCESS;
    981     }
    982 
    983     return VERR_NOT_FOUND;
    984 }
    985 
    986 int Guest::processSetStatus(uint32_t u32PID, ExecuteProcessStatus_T enmStatus, uint32_t uExitCode, uint32_t uFlags)
    987 {
    988     AssertReturn(u32PID, VERR_INVALID_PARAMETER);
    989 
    990     AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
    991 
    992     GuestProcessMapIter it = mGuestProcessMap.find(u32PID);
    993     if (it != mGuestProcessMap.end())
    994     {
    995         it->second.mStatus = enmStatus;
    996         it->second.mExitCode = uExitCode;
    997         it->second.mFlags = uFlags;
    998 
    999         return VINF_SUCCESS;
    1000     }
    1001 
    1002     return VERR_NOT_FOUND;
     1045    }
     1046
     1047    return VINF_SUCCESS;
    10031048}
    10041049
     
    10801125 * @param   aUsername               Username to execute tool under.
    10811126 * @param   aPassword               The user's password.
     1127 * @param   uFlagsToAdd             ExecuteProcessFlag flags to add to the execution operation.
    10821128 * @param   aProgress               Pointer which receives the tool's progress object. Optional.
    10831129 * @param   aPID                    Pointer which receives the tool's PID. Optional.
     
    10861132                                     ComSafeArrayIn(IN_BSTR, aArguments), ComSafeArrayIn(IN_BSTR, aEnvironment),
    10871133                                     IN_BSTR aUsername, IN_BSTR aPassword,
     1134                                     ULONG uFlagsToAdd,
     1135                                     GuestCtrlStreamObjects *pObjStdOut, GuestCtrlStreamObjects *pObjStdErr,
    10881136                                     IProgress **aProgress, ULONG *aPID)
    10891137{
     
    10911139    ULONG uPID;
    10921140
     1141    ULONG uFlags =   ExecuteProcessFlag_Hidden
     1142                   | ExecuteProcessFlag_WaitForProcessStartOnly;
     1143    if (uFlagsToAdd)
     1144        uFlags |= uFlagsToAdd;
     1145
     1146    bool fWaitForOutput = false;
     1147    if (   (   (uFlags & ExecuteProcessFlag_WaitForStdOut)
     1148            && pObjStdOut)
     1149        || (   (uFlags & ExecuteProcessFlag_WaitForStdErr)
     1150            && pObjStdErr))
     1151    {
     1152        fWaitForOutput = true;
     1153    }
     1154
    10931155    HRESULT rc = ExecuteProcess(aTool,
    1094                                 ExecuteProcessFlag_Hidden,
     1156                                uFlags,
    10951157                                ComSafeArrayInArg(aArguments),
    10961158                                ComSafeArrayInArg(aEnvironment),
    10971159                                aUsername, aPassword,
    1098                                 5 * 1000 /* Wait 5s for getting the process started. */,
     1160                                0 /* No timeout. */,
    10991161                                &uPID, progressTool.asOutParam());
     1162    if (   SUCCEEDED(rc)
     1163        && fWaitForOutput)
     1164    {
     1165        BOOL fCompleted;
     1166        while (   SUCCEEDED(progressTool->COMGETTER(Completed)(&fCompleted))
     1167               && !fCompleted)
     1168        {
     1169            BOOL fCanceled;
     1170            rc = progressTool->COMGETTER(Canceled)(&fCanceled);
     1171            ComAssertRC(rc);
     1172            if (fCanceled)
     1173            {
     1174                rc = setError(VBOX_E_IPRT_ERROR,
     1175                              tr("%s was cancelled"), Utf8Str(aDescription));
     1176                break;
     1177            }
     1178
     1179            if (   (uFlags & ExecuteProcessFlag_WaitForStdOut)
     1180                && pObjStdOut)
     1181            {
     1182                rc = executeStreamParse(uPID, ProcessOutputFlag_None /* StdOut */, *pObjStdOut);
     1183            }
     1184
     1185            if (   (uFlags & ExecuteProcessFlag_WaitForStdErr)
     1186                && pObjStdErr)
     1187            {
     1188                rc = executeStreamParse(uPID, ProcessOutputFlag_StdErr, *pObjStdErr);
     1189            }
     1190
     1191            if (FAILED(rc))
     1192                break;
     1193        }
     1194    }
     1195
    11001196    if (SUCCEEDED(rc))
    11011197    {
    1102         /* Wait for process to exit ... */
    1103         rc = progressTool->WaitForCompletion(-1);
    1104         if (FAILED(rc)) return rc;
    1105 
    1106         BOOL fCompleted = FALSE;
    1107         BOOL fCanceled = FALSE;
    1108         progressTool->COMGETTER(Completed)(&fCompleted);
    1109         if (!fCompleted)
    1110             progressTool->COMGETTER(Canceled)(&fCanceled);
    1111 
    1112         if (fCompleted)
    1113         {
    1114             ExecuteProcessStatus_T retStatus;
    1115             ULONG uRetExitCode, uRetFlags;
    1116             if (SUCCEEDED(rc))
    1117             {
    1118                 rc = GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
    1119                 if (SUCCEEDED(rc))
    1120                 {
    1121                     if (uRetExitCode != 0) /* Not equal 0 means some error occured. */
    1122                     {
    1123                         /** @todo IPRT exit code to string! */
    1124                         rc = setError(VBOX_E_IPRT_ERROR,
    1125                                       tr("%s: Error %u occured"),
    1126                                       Utf8Str(aDescription).c_str(), uRetExitCode);
    1127                     }
    1128                     else /* Return code 0, success. */
    1129                     {
    1130                         if (aProgress)
    1131                         {
    1132                             /* Return the progress to the caller. */
    1133                             progressTool.queryInterfaceTo(aProgress);
    1134                         }
    1135                         if (aPID)
    1136                             *aPID = uPID;
    1137                     }
    1138                 }
    1139             }
    1140         }
    1141         else if (fCanceled)
    1142         {
    1143             rc = setError(VBOX_E_IPRT_ERROR,
    1144                           tr("%s was aborted"), aDescription);
    1145         }
    1146         else
    1147             AssertReleaseMsgFailed(("%s: Operation neither completed nor canceled!?\n",
    1148                                     Utf8Str(aDescription).c_str()));
     1198        if (aProgress)
     1199        {
     1200            /* Return the progress to the caller. */
     1201            progressTool.queryInterfaceTo(aProgress);
     1202        }
     1203
     1204        if (aPID)
     1205            *aPID = uPID;
    11491206    }
    11501207
     
    12671324
    12681325/**
    1269  * Tries to drain the guest's output (from stdout) and fill it into
     1326 * Tries to drain the guest's output and fill it into
    12701327 * a guest process stream object for later usage.
    12711328 *
     1329 * @todo    What's about specifying stderr?
    12721330 * @return  IPRT status code.
    12731331 * @param   aPID                    PID of process to get the output from.
     1332 * @param   aFlags                  Which stream to drain (stdout or stderr).
    12741333 * @param   stream                  Reference to guest process stream to fill.
    12751334 */
    1276 int Guest::executeStreamDrain(ULONG aPID, GuestProcessStream &stream)
     1335int Guest::executeStreamDrain(ULONG aPID, ULONG aFlags, GuestProcessStream &stream)
    12771336{
    12781337    AssertReturn(aPID, VERR_INVALID_PARAMETER);
    1279 
    1280     /** @todo Should we try to drain the stream harder? */
    12811338
    12821339    int rc = VINF_SUCCESS;
    12831340    for (;;)
    12841341    {
    1285         SafeArray<BYTE> aOutputData;
    1286         HRESULT hr = GetProcessOutput(aPID, ProcessOutputFlag_None /* Stdout */,
    1287                                       10 * 1000 /* Timeout in ms */,
    1288                                       _64K, ComSafeArrayAsOutParam(aOutputData));
    1289         if (   SUCCEEDED(hr)
    1290             && aOutputData.size())
    1291         {
    1292             rc = stream.AddData(aOutputData.raw(), aOutputData.size());
    1293             if (RT_UNLIKELY(RT_FAILURE(rc)))
     1342        SafeArray<BYTE> aData;
     1343        HRESULT hr = getProcessOutputInternal(aPID, aFlags,
     1344                                              0 /* Infinite timeout */,
     1345                                              _64K, ComSafeArrayAsOutParam(aData), &rc);
     1346        if (SUCCEEDED(hr))
     1347        {
     1348            if (aData.size())
     1349            {
     1350                rc = stream.AddData(aData.raw(), aData.size());
     1351                if (RT_UNLIKELY(RT_FAILURE(rc)))
     1352                    break;
     1353            }
     1354
     1355            continue; /* Try one more time. */
     1356        }
     1357        else /* No more output and/or error! */
     1358        {
     1359            if (rc == VERR_NOT_FOUND)
     1360                rc = VINF_SUCCESS;
     1361            break;
     1362        }
     1363    }
     1364
     1365    return rc;
     1366}
     1367
     1368/**
     1369 * Tries to retrieve the next stream block of a given stream and
     1370 * drains the process output only as much as needed to get this next
     1371 * stream block.
     1372 *
     1373 * @return  IPRT status code.
     1374 * @param   ulPID
     1375 * @param   ulFlags
     1376 * @param   stream
     1377 * @param   streamBlock
     1378 */
     1379int Guest::executeStreamGetNextBlock(ULONG ulPID,
     1380                                     ULONG ulFlags,
     1381                                     GuestProcessStream &stream,
     1382                                     GuestProcessStreamBlock &streamBlock)
     1383{
     1384    AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);
     1385
     1386    bool fDrainStream = true;
     1387    int rc;
     1388    do
     1389    {
     1390        rc = stream.ParseBlock(streamBlock);
     1391        if (RT_FAILURE(rc)) /* More data needed or empty buffer? */
     1392        {
     1393            if (fDrainStream)
     1394            {
     1395                SafeArray<BYTE> aData;
     1396                HRESULT hr = getProcessOutputInternal(ulPID, ulFlags,
     1397                                                      0 /* Infinite timeout */,
     1398                                                      _64K, ComSafeArrayAsOutParam(aData), &rc);
     1399                if (SUCCEEDED(hr))
     1400                {
     1401                    if (aData.size())
     1402                    {
     1403                        rc = stream.AddData(aData.raw(), aData.size());
     1404                        if (RT_UNLIKELY(RT_FAILURE(rc)))
     1405                            break;
     1406                    }
     1407
     1408                    continue; /* Try one more time. */
     1409                }
     1410                else
     1411                {
     1412                    /* No more output to drain from stream. */
     1413                    if (rc == VERR_NOT_FOUND)
     1414                    {
     1415                        fDrainStream = false;
     1416                        continue;
     1417                    }
     1418
     1419                    break;
     1420                }
     1421            }
     1422            else
     1423            {
     1424                /* We haved drained the stream as much as we can and reached the
     1425                 * end of our stream buffer -- that means that there simply is no
     1426                 * stream block anymore, which is ok. */
     1427                if (rc == VERR_NO_DATA)
     1428                    rc = VINF_SUCCESS;
    12941429                break;
    1295         }
    1296         else /* No more output and/or error! */
    1297             break;
    1298     }
     1430            }
     1431        }
     1432    }
     1433    while (!streamBlock.GetCount());
    12991434
    13001435    return rc;
     
    13061441 *
    13071442 * @return  IPRT status code.
    1308  * @param   aPID                    PID of process to get/parse the output from.
     1443 * @param   ulPID                   PID of process to get/parse the output from.
     1444 * @param   ulFlags                 ?
    13091445 * @param   stream                  Reference to process stream object to use.
    13101446 * @param   streamBlock             Reference that receives the next stream block data.
    13111447 *
    13121448 */
    1313 int Guest::executeStreamGetNextBlock(ULONG aPID, GuestProcessStream &stream,
    1314                                      GuestProcessStreamBlock &streamBlock)
    1315 {
    1316     int rc = executeStreamDrain(aPID, stream);
    1317     if (RT_SUCCESS(rc))
    1318     {
    1319         do
    1320         {
    1321             rc = stream.ParseBlock(streamBlock);
    1322             if (streamBlock.GetCount())
    1323                 break; /* We got a block, bail out! */
    1324         } while (RT_SUCCESS(rc));
    1325 
    1326         /* In case this was the last block, VERR_NO_DATA is returned.
    1327          * Overwrite this to get a proper return value for the last block. */
    1328         if(    streamBlock.GetCount()
    1329             && rc == VERR_NO_DATA)
    1330         {
    1331             rc = VINF_SUCCESS;
    1332         }
    1333     }
     1449int Guest::executeStreamParseNextBlock(ULONG ulPID,
     1450                                       ULONG ulFlags,
     1451                                       GuestProcessStream &stream,
     1452                                       GuestProcessStreamBlock &streamBlock)
     1453{
     1454    AssertReturn(!streamBlock.GetCount(), VERR_INVALID_PARAMETER);
     1455
     1456    int rc;
     1457    do
     1458    {
     1459        rc = stream.ParseBlock(streamBlock);
     1460        if (RT_FAILURE(rc))
     1461            break;
     1462    }
     1463    while (!streamBlock.GetCount());
    13341464
    13351465    return rc;
     
    13421472 *
    13431473 * @return  HRESULT
    1344  * @param   aPID                    PID of process to get/parse the output from.
     1474 * @param   ulPID                   PID of process to get/parse the output from.
     1475 * @param   ulFlags                 ?
    13451476 * @param   streamObjects           Reference to a guest stream object structure for
    13461477 *                                  storing the parsed data.
    13471478 */
    1348 HRESULT Guest::executeStreamParse(ULONG aPID, GuestCtrlStreamObjects &streamObjects)
    1349 {
    1350     GuestProcessStream guestStream;
    1351     HRESULT hr = executeStreamDrain(aPID, guestStream);
    1352     if (SUCCEEDED(hr))
    1353     {
    1354         for (;;)
     1479HRESULT Guest::executeStreamParse(ULONG ulPID, ULONG ulFlags, GuestCtrlStreamObjects &streamObjects)
     1480{
     1481    GuestProcessStream stream;
     1482    int rc = executeStreamDrain(ulPID, ulFlags, stream);
     1483    if (RT_SUCCESS(rc))
     1484    {
     1485        do
    13551486        {
    13561487            /* Try to parse the stream output we gathered until now. If we still need more
    13571488             * data the parsing routine will tell us and we just do another poll round. */
    13581489            GuestProcessStreamBlock curBlock;
    1359             int vrc = guestStream.ParseBlock(curBlock);
    1360             if (RT_SUCCESS(vrc))
    1361             {
    1362                 if (curBlock.GetCount())
    1363                 {
    1364                     streamObjects.push_back(curBlock);
    1365                 }
    1366                 else
    1367                     break; /* No more data. */
    1368             }
    1369             else /* Everything else would be an error! */
    1370                 hr = setError(VBOX_E_IPRT_ERROR,
    1371                               tr("Error while parsing guest output (%Rrc)"), vrc);
    1372         }
    1373     }
    1374 
    1375     /** @todo Add check if there now are any sream objects at all! */
    1376 
    1377     return hr;
     1490            rc = executeStreamParseNextBlock(ulPID, ulFlags, stream, curBlock);
     1491            if (RT_SUCCESS(rc))
     1492                streamObjects.push_back(curBlock);
     1493        } while (RT_SUCCESS(rc));
     1494
     1495        if (rc == VERR_NO_DATA) /* End of data reached. */
     1496            rc = VINF_SUCCESS;
     1497    }
     1498
     1499    if (RT_FAILURE(rc))
     1500        return setError(VBOX_E_IPRT_ERROR,
     1501                        tr("Error while parsing guest output (%Rrc)"), rc);
     1502    return rc;
    13781503}
    13791504
     
    14611586            && !(aFlags & ExecuteProcessFlag_WaitForProcessStartOnly)
    14621587            && !(aFlags & ExecuteProcessFlag_Hidden)
    1463             && !(aFlags & ExecuteProcessFlag_NoProfile))
     1588            && !(aFlags & ExecuteProcessFlag_NoProfile)
     1589            && !(aFlags & ExecuteProcessFlag_WaitForStdOut)
     1590            && !(aFlags & ExecuteProcessFlag_WaitForStdErr))
    14641591        {
    14651592            if (pRC)
     
    16191746            if (RT_SUCCESS(vrc))
    16201747            {
    1621                 LogFlowFunc(("Waiting for HGCM callback (timeout=%dms) ...\n", aTimeoutMS));
     1748                LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));
    16221749
    16231750                /*
     
    16291756                PCALLBACKDATAEXECSTATUS pExecStatus = NULL;
    16301757
    1631                  /*
     1758                /*
    16321759                 * Wait for the first stage (=0) to complete (that is starting the process).
    16331760                 */
     
    17131840    {
    17141841        VBOXGUESTCTRL_PROCESS process;
    1715         int vrc = processGetByPID(aPID, &process);
     1842        int vrc = processGetStatus(aPID, &process);
    17161843        if (RT_SUCCESS(vrc))
    17171844        {
     
    18711998STDMETHODIMP Guest::GetProcessOutput(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS, LONG64 aSize, ComSafeArrayOut(BYTE, aData))
    18721999{
     2000#ifndef VBOX_WITH_GUEST_CONTROL
     2001    ReturnComNotImplemented();
     2002#else  /* VBOX_WITH_GUEST_CONTROL */
     2003    using namespace guestControl;
     2004
     2005    return getProcessOutputInternal(aPID, aFlags, aTimeoutMS,
     2006                                    aSize, ComSafeArrayOutArg(aData), NULL /* rc */);
     2007#endif
     2008}
     2009
     2010HRESULT Guest::getProcessOutputInternal(ULONG aPID, ULONG aFlags, ULONG aTimeoutMS,
     2011                                        LONG64 aSize, ComSafeArrayOut(BYTE, aData), int *pRC)
     2012{
    18732013/** @todo r=bird: Eventually we should clean up all the timeout parameters
    18742014 *        in the API and have the same way of specifying infinite waits!  */
     
    18982038    try
    18992039    {
    1900         VBOXGUESTCTRL_PROCESS process;
    1901         int vrc = processGetByPID(aPID, &process);
     2040        VBOXGUESTCTRL_PROCESS proc;
     2041        int vrc = processGetStatus(aPID, &proc);
    19022042        if (RT_FAILURE(vrc))
     2043        {
    19032044            rc = setError(VBOX_E_IPRT_ERROR,
    1904                           Guest::tr("Cannot get output from non-existent guest process (PID %u)"), aPID);
    1905 
    1906         if (SUCCEEDED(rc))
     2045                          Guest::tr("Guest process (PID %u) does not exist"), aPID);
     2046        }
     2047        else
    19072048        {
    19082049            uint32_t uContextID = 0;
     
    19792120            if (RT_SUCCESS(vrc))
    19802121            {
    1981                 LogFlowFunc(("Waiting for HGCM callback (timeout=%dms) ...\n", aTimeoutMS));
     2122                LogFlowFunc(("Waiting for HGCM callback (timeout=%RI32ms) ...\n", aTimeoutMS));
    19822123
    19832124                /*
     
    19872128                 */
    19882129
    1989                 PCALLBACKDATAEXECOUT pExecOut = NULL;
    1990 
    19912130                /*
    19922131                 * Wait for the first output callback notification to arrive.
    19932132                 */
    1994                 vrc = callbackWaitForCompletion(uContextID, -1 /* No staging */, aTimeoutMS);
     2133                vrc = callbackWaitForCompletion(uContextID, -1 /* No staging required */, aTimeoutMS);
    19952134                if (RT_SUCCESS(vrc))
    19962135                {
     2136                    PCALLBACKDATAEXECOUT pExecOut = NULL;
    19972137                    vrc = callbackGetUserData(uContextID, NULL /* We know the type. */,
    19982138                                              (void**)&pExecOut, NULL /* Don't need the size. */);
     
    20422182            pProgress.setNull();
    20432183        }
     2184
     2185        if (pRC)
     2186            *pRC = vrc;
    20442187    }
    20452188    catch (std::bad_alloc &)
     
    20682211    {
    20692212        VBOXGUESTCTRL_PROCESS process;
    2070         int vrc = processGetByPID(aPID, &process);
     2213        int vrc = processGetStatus(aPID, &process);
    20712214        if (RT_SUCCESS(vrc))
    20722215        {
  • trunk/src/VBox/Main/src-client/GuestCtrlImplDir.cpp

    r39387 r39418  
    126126                                   ComSafeArrayAsInParam(env),
    127127                                   aUsername, aPassword,
     128                                   ExecuteProcessFlag_None,
     129                                   NULL, NULL,
    128130                                   NULL /* Progress */, NULL /* PID */);
    129131    }
     
    301303    {
    302304        return executeStreamGetNextBlock(it->second.mPID,
     305                                         ProcessOutputFlag_None /* StdOut */,
    303306                                         it->second.mStream, streamBlock);
    304307    }
     
    405408
    406409        ULONG uPID;
    407         /** @todo Don't wait for tool to finish! Might take a lot of time! */
     410        /* We only start the directory listing and requesting stdout data but don't get its
     411         * data here; this is done in sequential IGuest::DirectoryRead calls then. */
    408412        hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_LS).raw(), Bstr("Opening directory").raw(),
    409413                                   ComSafeArrayAsInParam(args),
    410414                                   ComSafeArrayAsInParam(env),
    411415                                   aUsername, aPassword,
     416                                   ExecuteProcessFlag_WaitForStdOut,
     417                                   NULL, NULL,
    412418                                   NULL /* Progress */, &uPID);
    413419        if (SUCCEEDED(hr))
     
    477483         */
    478484        ULONG uPID;
     485        GuestCtrlStreamObjects stdOut;
    479486        hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying directory information").raw(),
    480487                                   ComSafeArrayAsInParam(args),
    481488                                   ComSafeArrayAsInParam(env),
    482489                                   aUsername, aPassword,
     490                                   ExecuteProcessFlag_WaitForStdOut,
     491                                   &stdOut, NULL /* stdErr */,
    483492                                   NULL /* Progress */, &uPID);
    484493        if (SUCCEEDED(hr))
    485494        {
    486             GuestCtrlStreamObjects streamObjs;
    487             hr = executeStreamParse(uPID, streamObjs);
    488             if (SUCCEEDED(hr))
     495            int rc = VINF_SUCCESS;
     496
     497            Assert(stdOut.size());
     498            const char *pszFsType = stdOut[0].GetString("ftype");
     499            if (!pszFsType) /* Attribute missing? */
     500                 rc = VERR_NOT_FOUND;
     501            if (   RT_SUCCESS(rc)
     502                && strcmp(pszFsType, "d")) /* Directory? */
    489503            {
    490                 int rc = VINF_SUCCESS;
    491 
    492                 Assert(streamObjs.size());
    493                 const char *pszFsType = streamObjs[0].GetString("ftype");
    494                 if (!pszFsType) /* Attribute missing? */
    495                      rc = VERR_NOT_FOUND;
    496                 if (   RT_SUCCESS(rc)
    497                     && strcmp(pszFsType, "d")) /* Directory? */
    498                 {
    499                      rc = VERR_FILE_NOT_FOUND;
    500                      /* This is not critical for Main, so don't set hr --
    501                       * we will take care of rc then. */
    502                 }
    503                 if (   RT_SUCCESS(rc)
    504                     && aObjInfo) /* Do we want object details? */
    505                 {
    506                     hr = executeStreamQueryFsObjInfo(aDirectory, streamObjs[0],
    507                                                      aObjInfo, enmAddAttribs);
    508                 }
    509 
    510                 if (pRC)
    511                     *pRC = rc;
     504                 rc = VERR_FILE_NOT_FOUND;
     505                 /* This is not critical for Main, so don't set hr --
     506                  * we will take care of rc then. */
    512507            }
     508            if (   RT_SUCCESS(rc)
     509                && aObjInfo) /* Do we want object details? */
     510            {
     511                hr = executeStreamQueryFsObjInfo(aDirectory, stdOut[0],
     512                                                 aObjInfo, enmAddAttribs);
     513            }
     514
     515            if (pRC)
     516                *pRC = rc;
    513517        }
    514518    }
     
    540544        if (RT_SUCCESS(rc))
    541545        {
    542             ComObjPtr <GuestDirEntry> pDirEntry;
    543             hr = pDirEntry.createObject();
    544             ComAssertComRC(hr);
    545 
    546             Assert(streamBlock.GetCount());
    547             hr = pDirEntry->init(this, streamBlock);
    548             if (SUCCEEDED(hr))
     546            if (streamBlock.GetCount())
    549547            {
    550                 pDirEntry.queryInterfaceTo(aDirEntry);
     548                ComObjPtr <GuestDirEntry> pDirEntry;
     549                hr = pDirEntry.createObject();
     550                ComAssertComRC(hr);
     551
     552                Assert(streamBlock.GetCount());
     553                hr = pDirEntry->init(this, streamBlock);
     554                if (SUCCEEDED(hr))
     555                {
     556                    pDirEntry.queryInterfaceTo(aDirEntry);
     557                }
     558                else
     559                    hr = setError(VBOX_E_IPRT_ERROR,
     560                                  Guest::tr("Failed to init guest directory entry"));
    551561            }
    552562            else
    553                 hr = setError(VBOX_E_IPRT_ERROR,
    554                               Guest::tr("Failed to init guest directory entry"));
    555         }
    556         else if (rc == VERR_NO_DATA)
    557         {
    558             /* No more directory entries to read. That's fine. */
    559             hr = E_ABORT; /** @todo Find/define a better rc! */
     563            {
     564                /* No more directory entries to read. That's fine. */
     565                hr = E_ABORT; /** @todo Find/define a better rc! */
     566            }
    560567        }
    561568        else
  • trunk/src/VBox/Main/src-client/GuestCtrlImplFile.cpp

    r38627 r39418  
    135135         */
    136136        ULONG uPID;
     137        GuestCtrlStreamObjects stdOut;
    137138        hr = executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_STAT).raw(), Bstr("Querying file information").raw(),
    138139                                   ComSafeArrayAsInParam(args),
    139140                                   ComSafeArrayAsInParam(env),
    140141                                   aUsername, aPassword,
     142                                   ExecuteProcessFlag_WaitForStdOut,
     143                                   &stdOut, NULL,
    141144                                   NULL /* Progress */, &uPID);
    142145        if (SUCCEEDED(hr))
    143146        {
    144             GuestCtrlStreamObjects streamObjs;
    145             hr = executeStreamParse(uPID, streamObjs);
    146             if (SUCCEEDED(hr))
     147            int rc = VINF_SUCCESS;
     148            const char *pszFsType = stdOut[0].GetString("ftype");
     149            if (!pszFsType) /* Attribute missing? */
     150                rc = VERR_NOT_FOUND;
     151            if (   RT_SUCCESS(rc)
     152                && strcmp(pszFsType, "-")) /* Regular file? */
    147153            {
    148                 int rc = VINF_SUCCESS;
    149                 const char *pszFsType = streamObjs[0].GetString("ftype");
    150                 if (!pszFsType) /* Attribute missing? */
    151                      rc = VERR_NOT_FOUND;
    152                 if (   RT_SUCCESS(rc)
    153                     && strcmp(pszFsType, "-")) /* Regular file? */
    154                 {
    155                      rc = VERR_FILE_NOT_FOUND;
    156                      /* This is not critical for Main, so don't set hr --
    157                       * we will take care of rc then. */
    158                 }
    159                 if (   RT_SUCCESS(rc)
    160                     && aObjInfo) /* Do we want object details? */
    161                 {
    162                     hr = executeStreamQueryFsObjInfo(aFile, streamObjs[0],
    163                                                      aObjInfo, enmAddAttribs);
    164                 }
    165 
    166                 if (pRC)
    167                     *pRC = rc;
     154                rc = VERR_FILE_NOT_FOUND;
     155                /* This is not critical for Main, so don't set hr --
     156                 * we will take care of rc then. */
    168157            }
     158            if (   RT_SUCCESS(rc)
     159                && aObjInfo) /* Do we want object details? */
     160            {
     161                hr = executeStreamQueryFsObjInfo(aFile, stdOut[0],
     162                                                 aObjInfo, enmAddAttribs);
     163            }
     164
     165            if (pRC)
     166                *pRC = rc;
    169167        }
    170168    }
  • trunk/src/VBox/Main/src-client/GuestCtrlImplTasks.cpp

    r38627 r39418  
    522522                 * actual copying, start the guest part now.
    523523                 */
    524                 rc = pGuest->ExecuteProcess(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
    525                                             ExecuteProcessFlag_Hidden,
    526                                             ComSafeArrayAsInParam(args),
    527                                             ComSafeArrayAsInParam(env),
    528                                             Bstr(aTask->strUserName).raw(),
    529                                             Bstr(aTask->strPassword).raw(),
    530                                             5 * 1000 /* Wait 5s for getting the process started. */,
    531                                             &uPID, execProgress.asOutParam());
     524                rc = pGuest->executeAndWaitForTool(Bstr(VBOXSERVICE_TOOL_CAT).raw(),
     525                                                   Bstr("Copying file to host").raw(),
     526                                                   ComSafeArrayAsInParam(args),
     527                                                   ComSafeArrayAsInParam(env),
     528                                                   Bstr(aTask->strUserName).raw(),
     529                                                   Bstr(aTask->strPassword).raw(),
     530                                                   ExecuteProcessFlag_WaitForStdOut,
     531                                                   NULL, NULL,
     532                                                   execProgress.asOutParam(), &uPID);
    532533                if (FAILED(rc))
    533534                    rc = GuestTask::setProgressErrorInfo(rc, aTask->progress, pGuest);
     
    551552                    size_t cbTransfered = 0;
    552553                    SafeArray<BYTE> aOutputData(_64K);
    553                     while (SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted))))
    554                     {
    555                         rc = this->GetProcessOutput(uPID, ProcessOutputFlag_None,
    556                                                     10 * 1000 /* Timeout in ms */,
    557                                                     _64K, ComSafeArrayAsOutParam(aOutputData));
     554                    while (   SUCCEEDED(execProgress->COMGETTER(Completed(&fCompleted)))
     555                           && !fCompleted)
     556                    {
     557                        rc = pGuest->GetProcessOutput(uPID, ProcessOutputFlag_None /* StdOut */,
     558                                                      0 /* No timeout. */,
     559                                                      _64K, ComSafeArrayAsOutParam(aOutputData));
    558560                        if (SUCCEEDED(rc))
    559561                        {
    560                             if (!aOutputData.size())
    561                             {
    562                                 /*
    563                                  * Only bitch about an unexpected end of a file when there already
    564                                  * was data read from that file. If this was the very first read we can
    565                                  * be (almost) sure that this file is not meant to be read by the specified user.
    566                                  */
    567                                 if (   cbTransfered
    568                                     && cbToRead)
     562                            if (aOutputData.size())
     563                            {
     564                                vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */);
     565                                if (RT_FAILURE(vrc))
    569566                                {
    570567                                    rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    571                                                                          Guest::tr("Unexpected end of file \"%s\" (%u bytes left, %u bytes written)"),
    572                                                                          aTask->strSource.c_str(), cbToRead, cbTransfered);
     568                                                                         Guest::tr("Error writing to file \"%s\" (%u bytes left), rc=%Rrc"),
     569                                                                         aTask->strSource.c_str(), cbToRead, vrc);
     570                                    break;
    573571                                }
    574                                 break;
    575                             }
    576 
    577                             vrc = RTFileWrite(hFileDest, aOutputData.raw(), aOutputData.size(), NULL /* No partial writes */);
    578                             if (RT_FAILURE(vrc))
    579                             {
    580                                 rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
    581                                                                      Guest::tr("Error writing to file \"%s\" (%u bytes left), rc=%Rrc"),
    582                                                                      aTask->strSource.c_str(), cbToRead, vrc);
    583                                 break;
    584                             }
    585 
    586                             cbToRead -= aOutputData.size();
    587                             cbTransfered += aOutputData.size();
    588 
    589                             aTask->progress->SetCurrentOperationProgress(cbTransfered / (lFileSize / 100.0));
     572
     573                                cbToRead -= aOutputData.size();
     574                                Assert(cbToRead >= 0);
     575                                cbTransfered += aOutputData.size();
     576
     577                                aTask->progress->SetCurrentOperationProgress(cbTransfered / (lFileSize / 100.0));
     578                            }
     579
     580                            /* Nothing read this time; try next round. */
    590581                        }
    591582                        else
     
    596587                    }
    597588
     589                    RTFileClose(hFileDest);
     590
     591                    if (   cbTransfered
     592                        && (cbTransfered != lFileSize))
     593                    {
     594                        /*
     595                         * Only bitch about an unexpected end of a file when there already
     596                         * was data read from that file. If this was the very first read we can
     597                         * be (almost) sure that this file is not meant to be read by the specified user.
     598                         */
     599                        rc = GuestTask::setProgressErrorInfo(VBOX_E_IPRT_ERROR, aTask->progress,
     600                                                             Guest::tr("Unexpected end of file \"%s\" (%u bytes total, %u bytes transferred)"),
     601                                                             aTask->strSource.c_str(), lFileSize, cbTransfered);
     602                    }
     603
    598604                    if (SUCCEEDED(rc))
    599605                        aTask->progress->notifyComplete(S_OK);
    600 
    601                     RTFileClose(hFileDest);
    602606                }
    603607            }
  • trunk/src/VBox/Main/src-client/GuestImpl.cpp

    r39248 r39418  
    120120        AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
    121121
    122         /* Clean up callback data. */
     122        /* Notify left over callbacks that we are about to shutdown ... */
    123123        CallbackMapIter it;
     124        for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
     125        {
     126            int rc2 = callbackNotifyEx(it->first, VERR_CANCELLED,
     127                                       Guest::tr("VM is shutting down, canceling uncompleted guest requests ..."));
     128            AssertRC(rc2);
     129        }
     130
     131        /* Destroy left over callback data. */
    124132        for (it = mCallbackMap.begin(); it != mCallbackMap.end(); it++)
    125133            callbackDestroy(it->first);
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