VirtualBox

Changeset 39431 in vbox


Ignore:
Timestamp:
Nov 28, 2011 9:32:33 AM (13 years ago)
Author:
vboxsync
Message:

VBoxManage/GuestCtrl: Support for outputting stderr, improved timeout handling.

File:
1 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp

    r39418 r39431  
    453453}
    454454
     455/**
     456 * Prints the desired guest output to a stream.
     457 *
     458 * @return  IPRT status code.
     459 * @param   pGuest          Pointer to IGuest interface.
     460 * @param   uPID            PID of guest process to get the output from.
     461 * @param   fOutputFlags    Output flags of type ProcessOutputFlag.
     462 * @param   cMsTimeout      Timeout value (in ms) to wait for output.
     463 */
     464static int ctrlExecPrintOutput(IGuest *pGuest, ULONG uPID,
     465                               PRTSTREAM pStrmOutput, uint32_t fOutputFlags,
     466                               uint32_t cMsTimeout)
     467{
     468    AssertPtrReturn(pGuest, VERR_INVALID_POINTER);
     469    AssertReturn(uPID, VERR_INVALID_PARAMETER);
     470    AssertPtrReturn(pStrmOutput, VERR_INVALID_POINTER);
     471
     472    SafeArray<BYTE> aOutputData;
     473    ULONG cbOutputData = 0;
     474
     475    int vrc = VINF_SUCCESS;
     476    HRESULT rc = pGuest->GetProcessOutput(uPID, fOutputFlags,
     477                                          cMsTimeout,
     478                                          _64K, ComSafeArrayAsOutParam(aOutputData));
     479    if (FAILED(rc))
     480    {
     481        vrc = ctrlPrintError(pGuest, COM_IIDOF(IGuest));
     482        cbOutputData = 0;
     483    }
     484    else
     485    {
     486        cbOutputData = aOutputData.size();
     487        if (cbOutputData > 0)
     488        {
     489            BYTE *pBuf = aOutputData.raw();
     490            AssertPtr(pBuf);
     491            pBuf[cbOutputData - 1] = 0; /* Properly terminate buffer. */
     492
     493            /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a
     494            *        generic problem and the new VFS APIs will handle it more
     495            *        transparently. (requires writing dos2unix/unix2dos filters ofc) */
     496
     497            /*
     498             * If aOutputData is text data from the guest process' stdout or stderr,
     499             * it has a platform dependent line ending. So standardize on
     500             * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
     501             * Windows. Otherwise we end up with CR/CR/LF on Windows.
     502             */
     503
     504            char *pszBufUTF8;
     505            vrc = RTStrCurrentCPToUtf8(&pszBufUTF8, (const char*)aOutputData.raw());
     506            if (RT_SUCCESS(vrc))
     507            {
     508                cbOutputData = strlen(pszBufUTF8);
     509
     510                ULONG cbOutputDataPrint = cbOutputData;
     511                for (char *s = pszBufUTF8, *d = s;
     512                     s - pszBufUTF8 < (ssize_t)cbOutputData;
     513                     s++, d++)
     514                {
     515                    if (*s == '\r')
     516                    {
     517                        /* skip over CR, adjust destination */
     518                        d--;
     519                        cbOutputDataPrint--;
     520                    }
     521                    else if (s != d)
     522                        *d = *s;
     523                }
     524
     525                vrc = RTStrmWrite(pStrmOutput, pszBufUTF8, cbOutputDataPrint);
     526                if (RT_FAILURE(vrc))
     527                    RTMsgError("Unable to write output, rc=%Rrc\n", vrc);
     528
     529                RTStrFree(pszBufUTF8);
     530            }
     531            else
     532                RTMsgError("Unable to convert output, rc=%Rrc\n", vrc);
     533        }
     534    }
     535
     536    return vrc;
     537}
     538
     539/**
     540 * Returns the remaining time (in ms) based on the start time and a set
     541 * timeout value. Returns RT_INDEFINITE_WAIT if no timeout was specified.
     542 *
     543 * @return  RTMSINTERVAL    Time left (in ms).
     544 * @param   u64StartMs      Start time (in ms).
     545 * @param   u32TimeoutMs    Timeout value (in ms).
     546 */
     547inline RTMSINTERVAL ctrlExecGetRemainingTime(uint64_t u64StartMs, uint32_t u32TimeoutMs)
     548{
     549    if (!u32TimeoutMs) /* If no timeout specified, wait forever. */
     550        return RT_INDEFINITE_WAIT;
     551
     552    uint64_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs;
     553    if (u64ElapsedMs >= u32TimeoutMs)
     554        return 0;
     555
     556    return u32TimeoutMs - u64ElapsedMs;
     557}
     558
    455559/* <Missing docuemntation> */
    456 static int handleCtrlExecProgram(ComPtr<IGuest> guest, HandlerArg *pArg)
     560static int handleCtrlExecProgram(ComPtr<IGuest> pGuest, HandlerArg *pArg)
    457561{
    458562    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     
    488592
    489593    Utf8Str                 Utf8Cmd;
    490     uint32_t                fExecFlags = ExecuteProcessFlag_None;
    491     uint32_t                fOutputFlags = ProcessOutputFlag_None;
     594    uint32_t                fExecFlags      = ExecuteProcessFlag_None;
    492595    com::SafeArray<IN_BSTR> args;
    493596    com::SafeArray<IN_BSTR> env;
     
    498601    bool                    fOutputBinary   = false;
    499602    bool                    fWaitForExit    = false;
    500     bool                    fWaitForStdOut  = false;
    501603    bool                    fVerbose        = false;
    502604
     
    571673            case GETOPTDEF_EXEC_WAITFORSTDOUT:
    572674                fExecFlags |= ExecuteProcessFlag_WaitForStdOut;
    573                 fWaitForExit = fWaitForStdOut = true;
     675                fWaitForExit = true;
    574676                break;
    575677
    576678            case GETOPTDEF_EXEC_WAITFORSTDERR:
    577679                fExecFlags |= ExecuteProcessFlag_WaitForStdErr;
    578                 fWaitForExit = (fOutputFlags |= ProcessOutputFlag_StdErr) ? true : false;
     680                fWaitForExit = true;
    579681                break;
    580682
     
    622724    ComPtr<IProgress> progress;
    623725    ULONG uPID = 0;
    624     rc = guest->ExecuteProcess(Bstr(Utf8Cmd).raw(),
     726    rc = pGuest->ExecuteProcess(Bstr(Utf8Cmd).raw(),
    625727                               fExecFlags,
    626728                               ComSafeArrayAsInParam(args),
     
    632734                               progress.asOutParam());
    633735    if (FAILED(rc))
    634         return ctrlPrintError(guest, COM_IIDOF(IGuest));
     736        return ctrlPrintError(pGuest, COM_IIDOF(IGuest));
    635737
    636738    if (fVerbose)
     
    668770            RTMsgError("Unable to set stdout's binary mode, rc=%Rrc\n", vrc);
    669771
     772        PRTSTREAM pStream = g_pStdOut; /* StdOut by default. */
     773        AssertPtr(pStream);
     774
    670775        /* Wait for process to exit ... */
    671776        BOOL fCompleted    = FALSE;
     
    673778        while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
    674779        {
    675             SafeArray<BYTE> aOutputData;
    676             ULONG cbOutputData = 0;
    677 
    678             /*
    679              * Some data left to output?
    680              */
    681             if (fOutputFlags || fWaitForStdOut)
    682             {
    683                 /** @todo r=bird: The timeout argument is bogus in several
    684                  * ways:
    685                  *  1. RT_MAX will evaluate the arguments twice, which may
    686                  *     result in different values because RTTimeMilliTS()
    687                  *     returns a higher value the 2nd time. Worst case:
    688                  *     Imagine when RT_MAX calculates the remaining time
    689                  *     out (first expansion) there is say 60 ms left.  Then
    690                  *     we're preempted and rescheduled after, say, 120 ms.
    691                  *     We call RTTimeMilliTS() again and ends up with a
    692                  *     value -60 ms, which translate to a UINT32_MAX - 59
    693                  *     ms timeout.
    694                  *
    695                  *  2. When the period expires, we will wait forever since
    696                  *     both 0 and -1 mean indefinite timeout with this API,
    697                  *     at least that's one way of reading the main code.
    698                  *
    699                  *  3. There is a signed/unsigned ambiguity in the
    700                  *     RT_MAX expression.  The left hand side is signed
    701                  *     integer (0), the right side is unsigned 64-bit. From
    702                  *     what I can tell, the compiler will treat this as
    703                  *     unsigned 64-bit and never return 0.
    704                  */
    705                 rc = guest->GetProcessOutput(uPID, fOutputFlags,
    706                                              RT_MAX(0, cMsTimeout - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */,
    707                                              _64K, ComSafeArrayAsOutParam(aOutputData));
    708                 if (FAILED(rc))
    709                 {
    710                     vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
    711                     cbOutputData = 0;
    712                 }
    713                 else
    714                 {
    715                     cbOutputData = aOutputData.size();
    716                     if (cbOutputData > 0)
    717                     {
    718                         BYTE *pBuf = aOutputData.raw();
    719                         AssertPtr(pBuf);
    720                         pBuf[cbOutputData - 1] = 0; /* Properly terminate buffer. */
    721 
    722                         /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a
    723                         *        generic problem and the new VFS APIs will handle it more
    724                         *        transparently. (requires writing dos2unix/unix2dos filters ofc) */
    725 
    726                         /*
    727                          * If aOutputData is text data from the guest process' stdout or stderr,
    728                          * it has a platform dependent line ending. So standardize on
    729                          * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
    730                          * Windows. Otherwise we end up with CR/CR/LF on Windows.
    731                          */
    732 
    733                         char *pszBufUTF8;
    734                         vrc = RTStrCurrentCPToUtf8(&pszBufUTF8, (const char*)aOutputData.raw());
    735                         if (RT_SUCCESS(vrc))
    736                         {
    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++)
    743                             {
    744                                 if (*s == '\r')
    745                                 {
    746                                     /* skip over CR, adjust destination */
    747                                     d--;
    748                                     cbOutputDataPrint--;
    749                                 }
    750                                 else if (s != d)
    751                                     *d = *s;
    752                             }
    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);
    759                         }
    760                         else
    761                             RTMsgError("Unable to convert output, rc=%Rrc\n", vrc);
    762                     }
    763                 }
    764             }
    765 
    766             /* No more output data left? */
    767             if (cbOutputData <= 0)
    768             {
    769                 /* Only break out from process handling loop if we processed (displayed)
    770                  * all output data or if there simply never was output data and the process
    771                  * has been marked as complete. */
    772                 if (fCompleted)
    773                     break;
     780            /* Do we need to output stuff? */
     781            uint32_t cMsTimeLeft;
     782            if (fExecFlags & ExecuteProcessFlag_WaitForStdOut)
     783            {
     784                cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout);
     785                if (cMsTimeLeft)
     786                    vrc = ctrlExecPrintOutput(pGuest, uPID,
     787                                              pStream, ProcessOutputFlag_None /* StdOut */,
     788                                              cMsTimeLeft == RT_INDEFINITE_WAIT ? 0 : cMsTimeLeft);
     789            }
     790
     791            if (fExecFlags & ExecuteProcessFlag_WaitForStdErr)
     792            {
     793                cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout);
     794                if (cMsTimeLeft)
     795                    vrc = ctrlExecPrintOutput(pGuest, uPID,
     796                                              pStream, ProcessOutputFlag_StdErr /* StdErr */,
     797                                              cMsTimeLeft == RT_INDEFINITE_WAIT ? 0 : cMsTimeLeft);
    774798            }
    775799
     
    820844                ExecuteProcessStatus_T retStatus;
    821845                ULONG uRetExitCode, uRetFlags;
    822                 rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
     846                rc = pGuest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
    823847                if (SUCCEEDED(rc) && fVerbose)
    824848                    RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, retStatus, ctrlExecProcessStatusToText(retStatus), uRetFlags);
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