VirtualBox

Changeset 39418 in vbox for trunk/src/VBox/Main/src-client


Ignore:
Timestamp:
Nov 25, 2011 10:11:06 AM (13 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
75045
Message:

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

Location:
trunk/src/VBox/Main/src-client
Files:
6 edited

Legend:

Unmodified
Added
Removed
  • 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.

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