VirtualBox

Changeset 36206 in vbox for trunk/src/VBox/Frontends


Ignore:
Timestamp:
Mar 8, 2011 4:32:21 PM (14 years ago)
Author:
vboxsync
Message:

Guest Execution: more review stuff.

File:
1 edited

Legend:

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

    r36189 r36206  
    2929#include <VBox/com/ErrorInfo.h>
    3030#include <VBox/com/errorprint.h>
    31 
    3231#include <VBox/com/VirtualBox.h>
    3332#include <VBox/com/EventQueue.h>
    3433
    35 #include <VBox/HostServices/GuestControlSvc.h> /* for PROC_STS_XXX */
     34#include <VBox/err.h>
     35#include <VBox/log.h>
    3636
    3737#include <iprt/asm.h>
     
    108108};
    109109
    110 enum OUTPUT_TYPE
    111 {
    112     OUTPUT_TYPE_UNDEFINED = 0,
    113     OUTPUT_TYPE_DOS2UNIX  = 10,
    114     OUTPUT_TYPE_UNIX2DOS  = 20
     110enum OUTPUTTYPE
     111{
     112    OUTPUTTYPE_UNDEFINED = 0,
     113    OUTPUTTYPE_DOS2UNIX  = 10,
     114    OUTPUTTYPE_UNIX2DOS  = 20
    115115};
    116116
     
    120120{
    121121    RTStrmPrintf(pStrm,
    122                  "VBoxManage guestcontrol     <vmname>|<uuid> exec[ute]\n"
     122                 "VBoxManage guestcontrol     <vmname>|<uuid>\n"
     123                 "                            exec[ute]\n"
    123124                 "                            --image <path to program>\n"
    124125                 "                            --username <name> --password <password>\n"
     
    133134                  *        stuff, e.g. "VBoxManage guestcontrol execute <VMName> --username <> ... -- /bin/rm -Rf /foo". */
    134135                 "\n"
    135                  "                            <vmname>|<uuid> copyto|cp\n"
     136                 "                            copyto|cp\n"
    136137                 "                            <source on host> <destination on guest>\n"
    137138                 "                            --username <name> --password <password>\n"
    138139                 "                            [--dryrun] [--follow] [--recursive] [--verbose]\n"
    139140                 "\n"
    140                  "                            <vmname>|<uuid> createdir[ectory]|mkdir|md\n"
     141                 "                            createdir[ectory]|mkdir|md\n"
    141142                 "                            <directory to create on guest>\n"
    142143                 "                            --username <name> --password <password>\n"
    143144                 "                            [--parents] [--mode <mode>] [--verbose]\n"
    144145                 "\n"
    145                  "                            <vmname>|<uuid> updateadditions\n"
     146                 "                            updateadditions\n"
    146147                 "                            [--source <guest additions .ISO>] [--verbose]\n"
    147148                 "\n");
     
    402403    Utf8Str                 Utf8Password;
    403404    uint32_t                cMsTimeout      = 0;
    404     OUTPUT_TYPE             eOutputType     = OUTPUT_TYPE_UNDEFINED;
     405    OUTPUTTYPE              eOutputType     = OUTPUTTYPE_UNDEFINED;
    405406    bool                    fOutputBinary   = false;
    406407    bool                    fWaitForExit    = false;
     
    417418        {
    418419            case GETOPTDEF_EXEC_DOS2UNIX:
    419                 if (eOutputType != OUTPUT_TYPE_UNDEFINED)
     420                if (eOutputType != OUTPUTTYPE_UNDEFINED)
    420421                    return errorSyntax(USAGE_GUESTCONTROL, "More than one output type (dos2unix/unix2dos) specified!");
    421                 eOutputType = OUTPUT_TYPE_DOS2UNIX;
     422                eOutputType = OUTPUTTYPE_DOS2UNIX;
    422423                break;
    423424
     
    456457
    457458            case GETOPTDEF_EXEC_UNIX2DOS:
    458                 if (eOutputType != OUTPUT_TYPE_UNDEFINED)
     459                if (eOutputType != OUTPUTTYPE_UNDEFINED)
    459460                    return errorSyntax(USAGE_GUESTCONTROL, "More than one output type (dos2unix/unix2dos) specified!");
    460                 eOutputType = OUTPUT_TYPE_UNIX2DOS;
     461                eOutputType = OUTPUTTYPE_UNIX2DOS;
    461462                break;
    462463
     
    485486            case VINF_GETOPT_NOT_OPTION:
    486487            {
     488#if 0 /** @todo r=bird: enable this when the argv[0] issue has been addressed. */
     489                if (args.size() == 0 && Utf8Cmd.isEmpty())
     490                    Utf8Cmd = ValueUnion.psz;
     491                args.push_back(Bstr(ValueUnion.psz).raw());
     492#else
    487493                if (Utf8Cmd.isEmpty())
     494                {
    488495                    Utf8Cmd = ValueUnion.psz;
     496                    if (Utf8Cmd.isEmpty())
     497                        return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The first argument cannot be empty yet, wait for 4.1.0");
     498                }
    489499                else
    490                 {
    491                     /* Push current parameter to vector. */
    492500                    args.push_back(Bstr(ValueUnion.psz).raw());
    493 
    494                     /*
    495                      * Add all following parameters after this one to our guest process
    496                      * argument vector.
    497                      */
    498                     while (   (ch = RTGetOpt(&GetState, &ValueUnion))
    499                            && RT_SUCCESS(vrc))
    500                     {
    501                         /*
    502                          * Is this option unknown or not recognized an option? Then just
    503                          * add the raw string value to our argument vector.
    504                          */
    505                         if (   ch == VINF_GETOPT_NOT_OPTION
    506                             || ch == VERR_GETOPT_UNKNOWN_OPTION)
    507                             args.push_back(Bstr(ValueUnion.psz).raw());
    508                         /*
    509                          * If this is an option/parameter we already defined for our actual
    510                          * execution command we need to take the pDef->pszLong value instead.
    511                          */
    512                         else if (ValueUnion.pDef)
    513                             args.push_back(Bstr(ValueUnion.pDef->pszLong).raw());
    514                         else
    515                             AssertMsgFailed(("Unknown parameter type detected!\n"));
    516                     }
    517                 }
     501#endif
    518502                break;
    519503            }
     
    560544    if (FAILED(rc))
    561545        return ctrlPrintError(guest, COM_IIDOF(IGuest));
    562     else
     546
     547    if (fVerbose)
     548        RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.c_str(), uPID);
     549    if (fWaitForExit)
    563550    {
    564551        if (fVerbose)
    565             RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.c_str(), uPID);
    566         if (fWaitForExit)
    567         {
    568             if (fVerbose)
    569             {
    570                 if (cMsTimeout) /* Wait with a certain timeout. */
     552        {
     553            if (cMsTimeout) /* Wait with a certain timeout. */
     554            {
     555                /* Calculate timeout value left after process has been started.  */
     556                uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS;
     557                /* Is timeout still bigger than current difference? */
     558                if (cMsTimeout > u64Elapsed)
     559                    RTPrintf("Waiting for process to exit (%ums left) ...\n", cMsTimeout - u64Elapsed);
     560                else
     561                    RTPrintf("No time left to wait for process!\n"); /** @todo a bit misleading ... */
     562            }
     563            else /* Wait forever. */
     564                RTPrintf("Waiting for process to exit ...\n");
     565        }
     566
     567        /* Setup signal handling if cancelable. */
     568        ASSERT(progress);
     569        bool fCanceledAlready = false;
     570        BOOL fCancelable;
     571        HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable);
     572        if (FAILED(hrc))
     573            fCancelable = FALSE;
     574        if (fCancelable)
     575            ctrlSignalHandlerInstall();
     576
     577        /* Wait for process to exit ... */
     578        BOOL fCompleted    = FALSE;
     579        BOOL fCanceled     = FALSE;
     580        int  cMilliesSleep = 0;
     581        while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
     582        {
     583            SafeArray<BYTE> aOutputData;
     584            ULONG cbOutputData = 0;
     585
     586            /*
     587             * Some data left to output?
     588             */
     589            if (   fWaitForStdOut
     590                || fWaitForStdErr)
     591            {
     592                /** @todo r=bird: The timeout argument is bogus in several
     593                 * ways:
     594                 *  1. RT_MAX will evaluate the arguments twice, which may
     595                 *     result in different values because RTTimeMilliTS()
     596                 *     returns a higher value the 2nd time. Worst case:
     597                 *     Imagine when RT_MAX calculates the remaining time
     598                 *     out (first expansion) there is say 60 ms left.  Then
     599                 *     we're preempted and rescheduled after, say, 120 ms.
     600                 *     We call RTTimeMilliTS() again and ends up with a
     601                 *     value -60 ms, which translate to a UINT32_MAX - 59
     602                 *     ms timeout.
     603                 *
     604                 *  2. When the period expires, we will wait forever since
     605                 *     both 0 and -1 mean indefinite timeout with this API,
     606                 *     at least that's one way of reading the main code.
     607                 *
     608                 *  3. There is a signed/unsigned ambiguity in the
     609                 *     RT_MAX expression.  The left hand side is signed
     610                 *     integer (0), the right side is unsigned 64-bit. From
     611                 *     what I can tell, the compiler will treat this as
     612                 *     unsigned 64-bit and never return 0.
     613                 */
     614                /** @todo r=bird: We must separate stderr and stdout
     615                 *        output, seems bunched together here which
     616                 *        won't do the trick for unix BOFHs. */
     617                rc = guest->GetProcessOutput(uPID, 0 /* aFlags */,
     618                                             RT_MAX(0, cMsTimeout - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */,
     619                                             _64K, ComSafeArrayAsOutParam(aOutputData));
     620                if (FAILED(rc))
    571621                {
    572                     /* Calculate timeout value left after process has been started.  */
    573                     uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS;
    574                     /* Is timeout still bigger than current difference? */
    575                     if (cMsTimeout > u64Elapsed)
    576                         RTPrintf("Waiting for process to exit (%ums left) ...\n", cMsTimeout - u64Elapsed);
    577                     else
    578                         RTPrintf("No time left to wait for process!\n"); /** @todo a bit misleading ... */
     622                    vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
     623                    cbOutputData = 0;
    579624                }
    580                 else /* Wait forever. */
    581                     RTPrintf("Waiting for process to exit ...\n");
    582             }
    583 
    584             /* Setup signal handling if cancelable. */
    585             ASSERT(progress);
    586             bool fCanceledAlready = false;
    587             BOOL fCancelable;
    588             HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable);
    589             if (FAILED(hrc))
    590                 fCancelable = FALSE;
    591             if (fCancelable)
    592                 ctrlSignalHandlerInstall();
    593 
    594             /* Wait for process to exit ... */
    595             BOOL fCompleted    = FALSE;
    596             BOOL fCanceled     = FALSE;
    597             int  cMilliesSleep = 0;
    598             while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
    599             {
    600                 SafeArray<BYTE> aOutputData;
    601                 ULONG cbOutputData = 0;
    602 
    603                 /*
    604                  * Some data left to output?
    605                  */
    606                 if (   fWaitForStdOut
    607                     || fWaitForStdErr)
     625                else
    608626                {
    609                     /** @todo r=bird: The timeout argument is bogus in several
    610                      * ways:
    611                      *  1. RT_MAX will evaluate the arguments twice, which may
    612                      *     result in different values because RTTimeMilliTS()
    613                      *     returns a higher value the 2nd time. Worst case:
    614                      *     Imagine when RT_MAX calculates the remaining time
    615                      *     out (first expansion) there is say 60 ms left.  Then
    616                      *     we're preempted and rescheduled after, say, 120 ms.
    617                      *     We call RTTimeMilliTS() again and ends up with a
    618                      *     value -60 ms, which translate to a UINT32_MAX - 59
    619                      *     ms timeout.
    620                      *
    621                      *  2. When the period expires, we will wait forever since
    622                      *     both 0 and -1 mean indefinite timeout with this API,
    623                      *     at least that's one way of reading the main code.
    624                      *
    625                      *  3. There is a signed/unsigned ambiguity in the
    626                      *     RT_MAX expression.  The left hand side is signed
    627                      *     integer (0), the right side is unsigned 64-bit. From
    628                      *     what I can tell, the compiler will treat this as
    629                      *     unsigned 64-bit and never return 0.
    630                      */
    631                     /** @todo r=bird: We must separate stderr and stdout
    632                      *        output, seems bunched together here which
    633                      *        won't do the trick for unix BOFHs. */
    634                     rc = guest->GetProcessOutput(uPID, 0 /* aFlags */,
    635                                                  RT_MAX(0, cMsTimeout - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */,
    636                                                  _64K, ComSafeArrayAsOutParam(aOutputData));
    637                     if (FAILED(rc))
     627                    cbOutputData = aOutputData.size();
     628                    if (cbOutputData > 0)
    638629                    {
    639                         vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
    640                         cbOutputData = 0;
    641                     }
    642                     else
    643                     {
    644                         cbOutputData = aOutputData.size();
    645                         if (cbOutputData > 0)
     630                        /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a
     631                        *        generic problem and the new VFS APIs will handle it more
     632                        *        transparently. (requires writing dos2unix/unix2dos filters ofc) */
     633                        if (eOutputType != OUTPUTTYPE_UNDEFINED)
    646634                        {
    647                             /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a
    648                             *        generic problem and the new VFS APIs will handle it more
    649                             *        transparently. (requires writing dos2unix/unix2dos filters ofc) */
    650                             if (eOutputType != OUTPUT_TYPE_UNDEFINED)
     635                            /*
     636                             * If aOutputData is text data from the guest process' stdout or stderr,
     637                             * it has a platform dependent line ending. So standardize on
     638                             * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
     639                             * Windows. Otherwise we end up with CR/CR/LF on Windows.
     640                             */
     641                            ULONG cbOutputDataPrint = cbOutputData;
     642                            for (BYTE *s = aOutputData.raw(), *d = s;
     643                                 s - aOutputData.raw() < (ssize_t)cbOutputData;
     644                                 s++, d++)
    651645                            {
    652                                 /*
    653                                  * If aOutputData is text data from the guest process' stdout or stderr,
    654                                  * it has a platform dependent line ending. So standardize on
    655                                  * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
    656                                  * Windows. Otherwise we end up with CR/CR/LF on Windows.
    657                                  */
    658                                 ULONG cbOutputDataPrint = cbOutputData;
    659                                 for (BYTE *s = aOutputData.raw(), *d = s;
    660                                      s - aOutputData.raw() < (ssize_t)cbOutputData;
    661                                      s++, d++)
     646                                if (*s == '\r')
    662647                                {
    663                                     if (*s == '\r')
    664                                     {
    665                                         /* skip over CR, adjust destination */
    666                                         d--;
    667                                         cbOutputDataPrint--;
    668                                     }
    669                                     else if (s != d)
    670                                         *d = *s;
     648                                    /* skip over CR, adjust destination */
     649                                    d--;
     650                                    cbOutputDataPrint--;
    671651                                }
    672                                 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
     652                                else if (s != d)
     653                                    *d = *s;
    673654                            }
    674                             else /* Just dump all data as we got it ... */
    675                                 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputData);
     655                            RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
    676656                        }
     657                        else /* Just dump all data as we got it ... */
     658                            RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputData);
    677659                    }
    678660                }
    679 
    680                 /* No more output data left? */
    681                 if (cbOutputData <= 0)
    682                 {
    683                     /* Only break out from process handling loop if we processed (displayed)
    684                      * all output data or if there simply never was output data and the process
    685                      * has been marked as complete. */
    686                     if (fCompleted)
    687                         break;
    688                 }
    689 
    690                 /* Process async cancelation */
    691                 if (g_fGuestCtrlCanceled && !fCanceledAlready)
    692                 {
    693                     hrc = progress->Cancel();
    694                     if (SUCCEEDED(hrc))
    695                         fCanceledAlready = TRUE;
    696                     else
    697                         g_fGuestCtrlCanceled = false;
    698                 }
    699 
    700                 /* Progress canceled by Main API? */
    701                 if (   SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled)))
    702                     && fCanceled)
     661            }
     662
     663            /* No more output data left? */
     664            if (cbOutputData <= 0)
     665            {
     666                /* Only break out from process handling loop if we processed (displayed)
     667                 * all output data or if there simply never was output data and the process
     668                 * has been marked as complete. */
     669                if (fCompleted)
    703670                    break;
    704 
    705                 /* Did we run out of time? */
    706                 if (   cMsTimeout
    707                     && RTTimeMilliTS() - u64StartMS > cMsTimeout)
    708                 {
    709                     progress->Cancel();
    710                     break;
    711                 }
    712             } /* while */
    713 
    714             /* Undo signal handling */
    715             if (fCancelable)
    716                 ctrlSignalHandlerUninstall();
    717 
    718             /* Report status back to the user. */
    719             if (fCanceled)
    720             {
    721                 if (fVerbose)
    722                     RTPrintf("Process execution canceled!\n");
    723                 rcProc = EXITCODE_EXEC_CANCELED;
    724             }
    725             else if (   fCompleted
    726                      && SUCCEEDED(rc)) /* The GetProcessOutput rc. */
    727             {
    728                 LONG iRc;
    729                 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
    730                 if (FAILED(iRc))
    731                     vrc = ctrlPrintProgressError(progress);
     671            }
     672
     673            /* Process async cancelation */
     674            if (g_fGuestCtrlCanceled && !fCanceledAlready)
     675            {
     676                hrc = progress->Cancel();
     677                if (SUCCEEDED(hrc))
     678                    fCanceledAlready = TRUE;
    732679                else
    733                 {
    734                     ExecuteProcessStatus_T retStatus;
    735                     ULONG uRetExitCode, uRetFlags;
    736                     rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
    737                     if (SUCCEEDED(rc) && fVerbose)
    738                         RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, retStatus, ctrlExecProcessStatusToText(retStatus), uRetFlags);
    739                     rcProc = ctrlExecProcessStatusToExitCode(retStatus, uRetExitCode);
    740                 }
    741             }
     680                    g_fGuestCtrlCanceled = false;
     681            }
     682
     683            /* Progress canceled by Main API? */
     684            if (   SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled)))
     685                && fCanceled)
     686                break;
     687
     688            /* Did we run out of time? */
     689            if (   cMsTimeout
     690                && RTTimeMilliTS() - u64StartMS > cMsTimeout)
     691            {
     692                progress->Cancel();
     693                break;
     694            }
     695        } /* while */
     696
     697        /* Undo signal handling */
     698        if (fCancelable)
     699            ctrlSignalHandlerUninstall();
     700
     701        /* Report status back to the user. */
     702        if (fCanceled)
     703        {
     704            if (fVerbose)
     705                RTPrintf("Process execution canceled!\n");
     706            rcProc = EXITCODE_EXEC_CANCELED;
     707        }
     708        else if (   fCompleted
     709                 && SUCCEEDED(rc)) /* The GetProcessOutput rc. */
     710        {
     711            LONG iRc;
     712            CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
     713            if (FAILED(iRc))
     714                vrc = ctrlPrintProgressError(progress);
    742715            else
    743716            {
    744                 if (fVerbose)
    745                     RTPrintf("Process execution aborted!\n");
    746                 rcProc = EXITCODE_EXEC_TERM_ABEND;
    747             }
     717                ExecuteProcessStatus_T retStatus;
     718                ULONG uRetExitCode, uRetFlags;
     719                rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus);
     720                if (SUCCEEDED(rc) && fVerbose)
     721                    RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, retStatus, ctrlExecProcessStatusToText(retStatus), uRetFlags);
     722                rcProc = ctrlExecProcessStatusToExitCode(retStatus, uRetExitCode);
     723            }
     724        }
     725        else
     726        {
     727            if (fVerbose)
     728                RTPrintf("Process execution aborted!\n");
     729            rcProc = EXITCODE_EXEC_TERM_ABEND;
    748730        }
    749731    }
     
    761743 * @param   pszFileDest         Full qualified destination path (optional).
    762744 * @param   pList               Copy list used for insertion.
     745 *
     746 * @todo    r=bird: Since everyone is maintaining an entry count, it would make
     747 *          sense to abstract the list. To simplify cleanup work, it would be
     748 *          preferable to use a standard C++ container template class.
    763749 */
    764750static int ctrlDirectoryEntryAppend(const char *pszFileSource, const char *pszFileDest,
     
    850836    AssertPtrReturn(pList, VERR_INVALID_POINTER);
    851837
     838    /*
     839     * Construct current path.
     840     */
     841    char szCurDir[RTPATH_MAX];
     842    int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszRootDir);
     843    if (RT_SUCCESS(rc) && pszSubDir != NULL)
     844        rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
     845
     846    /*
     847     * Open directory without a filter - RTDirOpenFiltered unfortunately
     848     * cannot handle sub directories so we have to do the filtering ourselves.
     849     */
    852850    PRTDIR pDir = NULL;
    853 
    854     int rc = VINF_SUCCESS;
    855     char szCurDir[RTPATH_MAX];
    856     /* Construct current path. */
    857     if (RTStrPrintf(szCurDir, sizeof(szCurDir), pszRootDir))
    858     {
    859         if (pszSubDir != NULL)
    860             rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);
    861     }
    862     else
    863         rc = VERR_NO_MEMORY;
    864 
    865851    if (RT_SUCCESS(rc))
    866852    {
    867         /* Open directory without a filter - RTDirOpenFiltered unfortunately
    868          * cannot handle sub directories so we have to do the filtering ourselves. */
    869853        rc = RTDirOpen(&pDir, szCurDir);
    870         for (;RT_SUCCESS(rc);)
     854        if (RT_FAILURE(rc))
     855            pDir = NULL;
     856    }
     857    if (RT_SUCCESS(rc))
     858    {
     859        /*
     860         * Enumerate the directory tree.
     861         */
     862        while (RT_SUCCESS(rc))
    871863        {
    872864            RTDIRENTRY DirEntry;
     
    881873            {
    882874                case RTDIRENTRYTYPE_DIRECTORY:
    883                     /* Skip "." and ".." entrires. */
     875                    /* Skip "." and ".." entries. */
    884876                    if (   !strcmp(DirEntry.szName, ".")
    885877                        || !strcmp(DirEntry.szName, ".."))
    886                     {
    887878                        break;
    888                     }
     879
    889880                    if (fFlags & CopyFileFlag_Recursive)
    890881                    {
     
    918909                case RTDIRENTRYTYPE_FILE:
    919910                {
    920                     bool fProcess = false;
    921                     if (pszFilter && RTStrSimplePatternMatch(pszFilter, DirEntry.szName))
    922                         fProcess = true;
    923                     else if (!pszFilter)
    924                         fProcess = true;
    925 
    926                     if (fProcess)
     911                    if (   !pszFilter
     912                        || RTStrSimplePatternMatch(pszFilter, DirEntry.szName))
    927913                    {
    928914                        char *pszFileSource = NULL;
     
    946932                            rc = ctrlDirectoryEntryAppend(pszFileSource, pszFileDest, pList);
    947933                            if (RT_SUCCESS(rc))
    948                                 *pcObjects = *pcObjects + 1;
     934                                *pcObjects += 1;
    949935                        }
    950936
     
    963949                break;
    964950        }
    965     }
    966 
    967     if (pDir)
     951
    968952        RTDirClose(pDir);
     953    }
    969954    return rc;
    970955}
     
    11451130    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
    11461131
     1132    /** @todo r=bird: This command isn't very unix friendly in general. mkdir
     1133     * is much better (partly because it is much simpler of course).  The main
     1134     * arguments against this is that (1) all but two options conflicts with
     1135     * what 'man cp' tells me on a GNU/Linux system, (2) wildchar matching is
     1136     * done windows CMD style (though not in a 100% compatible way), and (3)
     1137     * that only one source is allowed - efficiently sabotaging default
     1138     * wildcard expansion by a unix shell.  The best solution here would be
     1139     * two different variant, one windowsy (xcopy) and one unixy (gnu cp). */
     1140    /** @todo r=bird: Why isn't IGuest::CopyToGuest powerful enough to do
     1141     *        all this recursive copying? */
     1142
    11471143    static const RTGETOPTDEF s_aOptions[] =
    11481144    {
    1149         { "--dryrun",              'd',         RTGETOPT_REQ_NOTHING },
    1150         { "--follow",              'F',         RTGETOPT_REQ_NOTHING },
    1151         { "--password",            'p',         RTGETOPT_REQ_STRING  },
    1152         { "--recursive",           'R',         RTGETOPT_REQ_NOTHING },
    1153         { "--username",            'u',         RTGETOPT_REQ_STRING  },
     1145        { "--dryrun",              'd',         RTGETOPT_REQ_NOTHING }, /**< @todo r=bird: '-d' incompatible with GNU cp.  */
     1146        { "--follow",              'F',         RTGETOPT_REQ_NOTHING }, /**< @todo r=bird: This isn't a GNU cp option. This is instead spread over several options.  */
     1147        { "--password",            'p',         RTGETOPT_REQ_STRING  }, /**< @todo r=bird: Just drop these short options since (BSD, GNU, ++) cp uses '-p' to indicate that ownership, timestamp and other stuff should be preserved.  Other file commands probably use '-p' as well. */
     1148        { "--recursive",           'R',         RTGETOPT_REQ_NOTHING }, /**< @todo r=bird: Most cp implementations treats '-r' as an alias for '-R'. */
     1149        { "--username",            'u',         RTGETOPT_REQ_STRING  }, /**< @todo r=bird: Just drop these short options since GNU cp uses '-u' to indicate update-only (as does 4nt).  Other file commands probably uses '-u' as well. */
    11541150        { "--verbose",             'v',         RTGETOPT_REQ_NOTHING }
    11551151    };
     
    11711167    int vrc = VINF_SUCCESS;
    11721168    uint32_t idxNonOption = 0;
    1173     while (ch = RTGetOpt(&GetState, &ValueUnion))
     1169    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    11741170    {
    11751171        /* For options that require an argument, ValueUnion has received the value. */
     
    12131209                        break;
    12141210
     1211                    /* @todo r=bird: VBoxManage guestcontrol execute <VMName> copyto hostfile.1 hostfile.2 hostfile.3 guestdir/
     1212                     * A better way to implement this would be to tell RTGetOptInit to sort the argument, so
     1213                     * that when VINF_GETOPT_NOT_OPTION is returned, the remainder of pArg->argv are all arguments. */
     1214
    12151215                    default:
    12161216                        return errorSyntax(USAGE_GUESTCONTROL, "Too many parameters specified, only source and destination allowed!");
    1217                         /* Never reached. */
    1218                         break;
    12191217                }
    12201218                idxNonOption++;
     
    13201318    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
    13211319
     1320    /*
     1321     * Parse arguments.
     1322     *
     1323     * Note! No direct returns here, everyone must go thru the cleanup at the
     1324     *       end of this function.
     1325     */
    13221326    static const RTGETOPTDEF s_aOptions[] =
    13231327    {
     
    13371341    Utf8Str Utf8Password;
    13381342    uint32_t fFlags = CreateDirectoryFlag_None;
    1339     uint32_t uMode = 0;
     1343    uint32_t fDirMode = 0; /* 0 == default mode, right? */
    13401344    bool fVerbose = false;
    13411345
     
    13441348    RTListInit(&listDirs);
    13451349
    1346     int vrc = VINF_SUCCESS;
    1347     bool fUsageOK = true;
     1350    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
    13481351    while (   (ch = RTGetOpt(&GetState, &ValueUnion))
    1349            && RT_SUCCESS(vrc))
     1352           && rcExit == RTEXITCODE_SUCCESS)
    13501353    {
    13511354        /* For options that require an argument, ValueUnion has received the value. */
     
    13531356        {
    13541357            case 'm': /* Mode */
    1355                 uMode = ValueUnion.u32;
     1358                fDirMode = ValueUnion.u32;
    13561359                break;
    13571360
     
    13741377            case VINF_GETOPT_NOT_OPTION:
    13751378            {
    1376                 vrc = ctrlDirectoryEntryAppend(NULL,              /* No source given */
    1377                                                ValueUnion.psz,    /* Destination */
    1378                                                &listDirs);
     1379/** @todo r=bird: Simplify by letting RTGetOptInit sort the argv, just like
     1380 *        for cp. */
     1381                int vrc = ctrlDirectoryEntryAppend(NULL,              /* No source given */
     1382                                                   ValueUnion.psz,    /* Destination */
     1383                                                   &listDirs);
    13791384                if (RT_SUCCESS(vrc))
    13801385                {
    13811386                    cDirs++;
    13821387                    if (cDirs == UINT32_MAX)
    1383                     {
    1384                         RTMsgError("Too many directories specified! Aborting.\n");
    1385                         vrc = VERR_TOO_MUCH_DATA;
    1386                     }
     1388                        rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many directories specified! Aborting.");
    13871389                }
     1390                else
     1391                    rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Failed to append directory: %Rrc", vrc);
    13881392                break;
    13891393            }
    13901394
    13911395            default:
    1392                 return RTGetOptPrintError(ch, &ValueUnion);
    1393         }
    1394     }
    1395 
    1396     if (!fUsageOK)
    1397         return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
    1398 
    1399     if (!cDirs)
    1400         return errorSyntax(USAGE_GUESTCONTROL,
    1401                            "No directory to create specified!");
    1402 
    1403     if (Utf8UserName.isEmpty())
    1404         return errorSyntax(USAGE_GUESTCONTROL,
    1405                            "No user name specified!");
    1406 
    1407     HRESULT rc = S_OK;
    1408     if (fVerbose && cDirs > 1)
    1409         RTPrintf("Creating %u directories ...\n", cDirs);
    1410 
    1411     PDIRECTORYENTRY pNode;
    1412     RTListForEach(&listDirs, pNode, DIRECTORYENTRY, Node)
    1413     {
    1414         if (fVerbose)
    1415             RTPrintf("Creating directory \"%s\" ...\n", pNode->pszDestPath);
    1416 
    1417         ComPtr<IProgress> progress;
    1418         rc = guest->CreateDirectory(Bstr(pNode->pszDestPath).raw(),
    1419                                     Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
    1420                                     uMode, fFlags, progress.asOutParam());
    1421         if (FAILED(rc))
    1422         {
    1423             ctrlPrintError(guest, COM_IIDOF(IGuest)); /* (return code ignored, save original rc) */
    1424             break;
    1425         }
    1426     }
     1396                rcExit = RTGetOptPrintError(ch, &ValueUnion);
     1397                break;
     1398        }
     1399    }
     1400
     1401    if (rcExit == RTEXITCODE_SUCCESS && !cDirs)
     1402        rcExit = errorSyntax(USAGE_GUESTCONTROL, "No directory to create specified!");
     1403
     1404    if (rcExit == RTEXITCODE_SUCCESS && Utf8UserName.isEmpty())
     1405        rcExit = errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
     1406
     1407    if (rcExit == RTEXITCODE_SUCCESS)
     1408    {
     1409        /*
     1410         * Create the directories.
     1411         */
     1412        HRESULT hrc = S_OK;
     1413        if (fVerbose && cDirs > 1)
     1414            RTPrintf("Creating %u directories ...\n", cDirs);
     1415
     1416        PDIRECTORYENTRY pNode;
     1417        RTListForEach(&listDirs, pNode, DIRECTORYENTRY, Node)
     1418        {
     1419            if (fVerbose)
     1420                RTPrintf("Creating directory \"%s\" ...\n", pNode->pszDestPath);
     1421
     1422            ComPtr<IProgress> progress;
     1423            hrc = guest->CreateDirectory(Bstr(pNode->pszDestPath).raw(),
     1424                                         Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
     1425                                         fDirMode, fFlags, progress.asOutParam());
     1426            if (FAILED(hrc))
     1427            {
     1428                ctrlPrintError(guest, COM_IIDOF(IGuest)); /* (return code ignored, save original rc) */
     1429                break;
     1430            }
     1431        }
     1432        if (FAILED(hrc))
     1433            rcExit = RTEXITCODE_FAILURE;
     1434    }
     1435
     1436    /*
     1437     * Clean up and return.
     1438     */
    14271439    ctrlDirectoryListDestroy(&listDirs);
    1428     return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
     1440
     1441    return rcExit;
    14291442}
    14301443
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