VirtualBox

Changeset 35937 in vbox


Ignore:
Timestamp:
Feb 11, 2011 9:18:47 AM (14 years ago)
Author:
vboxsync
Message:

VBoxManage/GuestCtrl: Addressed some todos.

File:
1 edited

Legend:

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

    r35916 r35937  
    55
    66/*
    7  * Copyright (C) 2010 Oracle Corporation
     7 * Copyright (C) 2011 Oracle Corporation
    88 *
    99 * This file is part of VirtualBox Open Source Edition (OSE), as
     
    7676} DIRECTORYENTRY, *PDIRECTORYENTRY;
    7777
     78/*
     79 * Special exit codes for returning errors/information of a
     80 * started guest process to the command line VBoxManage was started from.
     81 * Useful for e.g. scripting.
     82 */
     83enum RTEXITCODE_EXEC
     84{
     85    RTEXITCODE_EXEC_SUCCESS      = RTEXITCODE_SUCCESS,
     86    RTEXITCODE_EXEC_FAILED       = 16,
     87    RTEXITCODE_EXEC_TERM_SIGNAL  = 17,
     88    RTEXITCODE_EXEC_TERM_ABEND   = 18,
     89    RTEXITCODE_EXEC_TIMEOUT      = 19,
     90    RTEXITCODE_EXEC_CANCELED     = 20
     91};
     92
     93/*
     94 * RTGetOpt-IDs for the guest execution control command line.
     95 */
     96enum RTGETOPTDEF_EXEC
     97{
     98    RTGETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES = 1000,
     99    RTGETOPTDEF_EXEC_WAITFOREXIT,
     100    RTGETOPTDEF_EXEC_WAITFORSTDOUT,
     101    RTGETOPTDEF_EXEC_WAITFORSTDERR,
     102    RTGETOPTDEF_EXEC_ARGS
     103};
     104
    78105#endif /* VBOX_ONLY_DOCS */
    79106
     
    81108{
    82109    RTStrmPrintf(pStrm,
    83                  "VBoxManage guestcontrol     exec[ute] <vmname>|<uuid>\n"
    84                  "                            <path to program>\n"
     110                 "VBoxManage guestcontrol     <vmname>|<uuid> exec[ute]\n"
     111                 "                            --image <path to program>\n"
    85112                 "                            --username <name> --password <password>\n"
    86                  "                            [--arguments \"<arguments>\"]\n"
    87113                 "                            [--environment \"<NAME>=<VALUE> [<NAME>=<VALUE>]\"]\n"
    88                  "                            [--flags <flags>] [--timeout <msec>]\n"
    89                  "                            [--verbose] [--wait-for exit,stdout,stderr||]\n"
     114                 "                            [--timeout <msec>] [--verbose]\n"
     115                 "                            [--wait-exit] [--wait-stdout] [--wait-stdout]\n"
     116                 "                            [-- [<argument1>] ... [<argumentN>]\n"
    90117                 /** @todo Add a "--" parameter (has to be last parameter) to directly execute
    91118                  *        stuff, e.g. "VBoxManage guestcontrol execute <VMName> --username <> ... -- /bin/rm -Rf /foo". */
    92119                 "\n"
    93                  "                            copyto|cp <vmname>|<uuid>\n"
     120                 "                            <vmname>|<uuid> copyto|cp\n"
    94121                 "                            <source on host> <destination on guest>\n"
    95122                 "                            --username <name> --password <password>\n"
    96123                 "                            [--dryrun] [--follow] [--recursive] [--verbose]\n"
    97124                 "\n"
    98                  "                            createdir[ectory]|mkdir|md <vmname>|<uuid>\n"
     125                 "                            <vmname>|<uuid> createdir[ectory]|mkdir|md\n"
    99126                 "                            <directory to create on guest>\n"
    100127                 "                            --username <name> --password <password>\n"
    101128                 "                            [--parents] [--mode <mode>] [--verbose]\n"
    102129                 "\n"
    103                  "                            updateadditions <vmname>|<uuid>\n"
     130                 "                            <vmname>|<uuid> updateadditions\n"
    104131                 "                            [--source <guest additions .ISO>] [--verbose]\n"
    105132                 "\n");
     
    148175 * string.
    149176 */
    150 static const char *ctrlExecGetStatus(ULONG uStatus)
     177static const char *ctrlExecProcessStatusToText(ULONG uStatus)
    151178{
    152179    switch (uStatus)
     
    169196            return "error";
    170197        default:
    171             return "unknown";
    172     }
     198            break;
     199    }
     200    return "unknown";
     201}
     202
     203static int ctrlExecProcessStatusToExitCode(ULONG uStatus)
     204{
     205    int rc = RTEXITCODE_EXEC_SUCCESS;
     206    switch (uStatus)
     207    {
     208        case guestControl::PROC_STS_STARTED:
     209            rc = RTEXITCODE_EXEC_SUCCESS;
     210            break;
     211        case guestControl::PROC_STS_TEN:
     212            rc = RTEXITCODE_EXEC_SUCCESS;
     213            break;
     214        case guestControl::PROC_STS_TES:
     215            rc = RTEXITCODE_EXEC_TERM_SIGNAL;
     216            break;
     217        case guestControl::PROC_STS_TEA:
     218            rc = RTEXITCODE_EXEC_TERM_ABEND;
     219            break;
     220        case guestControl::PROC_STS_TOK:
     221            rc = RTEXITCODE_EXEC_TIMEOUT;
     222            break;
     223        case guestControl::PROC_STS_TOA:
     224            rc = RTEXITCODE_EXEC_TIMEOUT;
     225            break;
     226        case guestControl::PROC_STS_DWN:
     227            /* Service/OS is stopping, process was killed, so
     228             * not exactly an error of the started process ... */
     229            rc = RTEXITCODE_EXEC_SUCCESS;
     230            break;
     231        case guestControl::PROC_STS_ERROR:
     232            rc = RTEXITCODE_EXEC_FAILED;
     233            break;
     234        default:
     235            AssertMsgFailed(("Unknown exit code (%u) from guest process returned!\n", uStatus));
     236            break;
     237    }
     238    return rc;
    173239}
    174240
     
    282348
    283349/* <Missing docuemntation> */
    284 static int handleCtrlExecProgram(HandlerArg *a)
    285 {
     350static int handleCtrlExecProgram(ComPtr<IGuest> guest, HandlerArg *pArg)
     351{
     352    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     353
    286354    /*
    287355     * Parse arguments.
    288356     */
    289     if (a->argc < 2) /* At least the command we want to execute in the guest should be present :-). */
     357    if (pArg->argc < 2) /* At least the command we want to execute in the guest should be present :-). */
    290358        return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
    291359
    292360    static const RTGETOPTDEF s_aOptions[] =
    293361    {
    294         { "--arguments",           'a',         RTGETOPT_REQ_STRING  },
    295         { "--environment",         'e',         RTGETOPT_REQ_STRING  },
    296         { "--flags",               'f',         RTGETOPT_REQ_STRING  },
    297         { "--password",            'p',         RTGETOPT_REQ_STRING  },
    298         { "--timeout",             't',         RTGETOPT_REQ_UINT32  },
    299         { "--username",            'u',         RTGETOPT_REQ_STRING  },
    300         { "--verbose",             'v',         RTGETOPT_REQ_NOTHING },
    301         { "--wait-for",            'w',         RTGETOPT_REQ_STRING  }
     362        { "--environment",                  'e',                                        RTGETOPT_REQ_STRING  },
     363        { "--flags",                        'f',                                        RTGETOPT_REQ_STRING  },
     364        { "--ignore-operhaned-processes",   RTGETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES,   RTGETOPT_REQ_NOTHING },
     365        { "--image",                        'i',                                        RTGETOPT_REQ_STRING  },
     366        { "--password",                     'p',                                        RTGETOPT_REQ_STRING  },
     367        { "--timeout",                      't',                                        RTGETOPT_REQ_UINT32  },
     368        { "--username",                     'u',                                        RTGETOPT_REQ_STRING  },
     369        { "--verbose",                      'v',                                        RTGETOPT_REQ_NOTHING },
     370        { "--wait-exit",                    RTGETOPTDEF_EXEC_WAITFOREXIT,               RTGETOPT_REQ_NOTHING },
     371        { "--wait-stdout",                  RTGETOPTDEF_EXEC_WAITFORSTDOUT,             RTGETOPT_REQ_NOTHING },
     372        { "--wait-stderr",                  RTGETOPTDEF_EXEC_WAITFORSTDERR,             RTGETOPT_REQ_NOTHING },
     373        { "--",                             RTGETOPTDEF_EXEC_ARGS,                      RTGETOPT_REQ_STRING }
    302374    };
    303375
     
    305377    RTGETOPTUNION           ValueUnion;
    306378    RTGETOPTSTATE           GetState;
    307     RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
     379    RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
    308380
    309381    Utf8Str                 Utf8Cmd;
     
    327399        switch (ch)
    328400        {
    329             case 'a': /* Arguments */
    330             {
    331                 char **papszArg;
    332                 int cArgs;
    333 
    334                 vrc = RTGetOptArgvFromString(&papszArg, &cArgs, ValueUnion.psz, NULL);
    335                 if (RT_SUCCESS(vrc))
    336                 {
    337                     for (int j = 0; j < cArgs; j++)
    338                         args.push_back(Bstr(papszArg[j]).raw());
    339 
    340                     RTGetOptArgvFree(papszArg);
    341                 }
    342                 break;
    343             }
    344 
    345401            case 'e': /* Environment */
    346402            {
     
    349405
    350406                vrc = RTGetOptArgvFromString(&papszArg, &cArgs, ValueUnion.psz, NULL);
    351                 if (RT_SUCCESS(vrc))
     407                if (RT_FAILURE(vrc))
     408                    return errorSyntax(USAGE_GUESTCONTROL, "Failed to parse environment value, rc=%Rrc", vrc);
     409                else
    352410                {
    353411                    for (int j = 0; j < cArgs; j++)
     
    359417            }
    360418
    361             case 'f': /* Flags */
    362                 /** @todo r=bird: As stated before, this generic flags stuff
    363                  *        does NOT make sense!  The IgnoreOprphanedProcesses
    364                  *        features should have been added as:
    365                  *  { "--ignore-operhaned-processes", DEFINE, RTGETOPT_REQ_NOTHING }
    366                  *
    367                  *  Please, remove -f in 4.1, replace it with above. */
    368                 /** @todo Needs a bit better processing as soon as we have more flags. */
    369                 /** @todo Add a hidden flag. */
    370                 if (!RTStrICmp(ValueUnion.psz, "ignoreorphanedprocesses"))
    371                     fFlags |= ExecuteProcessFlag_IgnoreOrphanedProcesses;
    372                 else
    373                     fUsageOK = false;
    374                 break;
     419            case 'i':
     420                Utf8Cmd = ValueUnion.psz;
     421                break;
     422
     423            case RTGETOPTDEF_EXEC_IGNOREORPHANEDPROCESSES:
     424                fFlags |= ExecuteProcessFlag_IgnoreOrphanedProcesses;
     425                break;
     426
     427            /** @todo Add a hidden flag. */
    375428
    376429            case 'p': /* Password */
     
    390443                break;
    391444
    392             case 'w': /* Wait for ... */
     445            case RTGETOPTDEF_EXEC_WAITFOREXIT:
     446                fWaitForExit = true;
     447                break;
     448
     449            case RTGETOPTDEF_EXEC_WAITFORSTDOUT:
     450                fWaitForExit = true;
     451                fWaitForStdOut = true;
     452                break;
     453
     454            case RTGETOPTDEF_EXEC_WAITFORSTDERR:
     455                fWaitForExit = true;
     456                fWaitForStdErr = true;
     457                break;
     458
     459            case RTGETOPTDEF_EXEC_ARGS:
    393460            {
    394                 /** @todo r=bird: Same as for -f: Use individual options for
    395                  * indicating what to wait for.  This is unix tradition and
    396                  * much simpler to write code for.  It also avoids people
    397                  * finding out that the following sequence does not work
    398                  * (contrary to what one would expect):
    399                  *      -w stdout,stderr
    400                  *  */
    401                 if (!RTStrICmp(ValueUnion.psz, "exit"))
    402                     fWaitForExit = true;
    403                 else if (!RTStrICmp(ValueUnion.psz, "stdout"))
    404                 {
    405                     fWaitForExit = true;
    406                     fWaitForStdOut = true;
    407                 }
    408                 else if (!RTStrICmp(ValueUnion.psz, "stderr"))
    409                 {
    410                     fWaitForExit = true;
    411                     fWaitForStdErr = true;
    412                 }
     461                /* Push current parameter to vector. */
     462                args.push_back(Bstr(ValueUnion.psz).raw());
     463
     464                /*
     465                 * Add all following parameters after this one to our guest process
     466                 * argument vector.
     467                 */
     468                while (   (ch = RTGetOpt(&GetState, &ValueUnion))
     469                       && RT_SUCCESS(vrc))
     470                {
     471                    /*
     472                     * Is this option unknown or not recognized an option? Then just
     473                     * add the raw string value to our argument vector.
     474                     */
     475                    if (   ch == VINF_GETOPT_NOT_OPTION
     476                        || ch == VERR_GETOPT_UNKNOWN_OPTION)
     477                        args.push_back(Bstr(ValueUnion.psz).raw());
     478                    /*
     479                     * If this is an option/parameter we already defined for our actual
     480                     * execution command we need to take the pDef->pszLong value instead.
     481                     */
     482                    else if (ValueUnion.pDef)
     483                        args.push_back(Bstr(ValueUnion.pDef->pszLong).raw());
     484                    else
     485                        AssertMsgFailed(("Unknown parameter type detected!\n"));
     486                }
     487                break;
     488            }
     489
     490            case VINF_GETOPT_NOT_OPTION:
     491                if (Utf8Cmd.isEmpty())
     492                    Utf8Cmd = ValueUnion.psz;
    413493                else
    414                     fUsageOK = false;
    415                 break;
    416             }
    417 
    418             case VINF_GETOPT_NOT_OPTION:
    419             {
    420                 /** @todo r=bird: Guess what the following does:
    421                  * VBoxManage guestcontrol exec myvm /bin/ls 1 2 3 4;
    422                  *
    423                  * In 4.1 this SHALL be changed to treat 1 2 3 4 as arguments
    424                  * to /bin/ls.  Drop --arguments and replace it with --image
    425                  * <guest-file>.  The --image defaults to the first argument if
    426                  * not specified.  Users should be encouraged to use '--' to
    427                  * separate 'exec' options from options to the guest
    428                  * program:
    429                  *      VBoxManage guestcontrol myvm exec --image /bin/busybox -- ln -s /foo /bar
    430                  */
    431                 /* The actual command we want to execute on the guest. */
    432                 Utf8Cmd = ValueUnion.psz;
    433                 break;
    434             }
     494                    return RTGetOptPrintError(ch, &ValueUnion);
     495                break;
    435496
    436497            default:
     
    439500    }
    440501
    441     if (!fUsageOK) /** @todo r=bird: there is no clean up, so just return directly on failure with a specific message. */
    442         return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
     502    if (RT_FAILURE(vrc))
     503    {
     504        RTMsgError("Failed to parse argument '%c', rc=%Rrc", ch, vrc);
     505        return RTEXITCODE_FAILURE;
     506    }
    443507
    444508    if (Utf8Cmd.isEmpty())
     
    448512        return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!");
    449513
    450     /** @todo r=bird: You don't check vrc here, so if RTGetOptArgvFromString
    451      *  or RTGetOptArgvFromString failed above, you'll ignore it.
    452      *
    453      *  Just simplify the argument parsing to not use unnecessary state
    454      *  variables and instead return directly on error with a specific error
    455      *  message. (See fUsageOk comment above.) */
    456 
    457     /*
    458      * <comment missing>
    459      */
    460514    HRESULT rc = S_OK;
    461     ComPtr<IGuest> guest;
    462     vrc = ctrlInitVM(a, a->argv[0] /* VM Name */, &guest);
    463     if (RT_SUCCESS(vrc))
     515    if (fVerbose)
     516    {
     517        if (u32TimeoutMS == 0)
     518            RTPrintf("Waiting for guest to start process ...\n");
     519        else
     520            RTPrintf("Waiting for guest to start process (within %ums)\n", u32TimeoutMS);
     521    }
     522
     523    /* Get current time stamp to later calculate rest of timeout left. */
     524    uint64_t u64StartMS = RTTimeMilliTS();
     525
     526    /* Execute the process. */
     527    int rcProc = RTEXITCODE_EXEC_SUCCESS;
     528    ComPtr<IProgress> progress;
     529    ULONG uPID = 0;
     530    rc = guest->ExecuteProcess(Bstr(Utf8Cmd).raw(),
     531                               fFlags,
     532                               ComSafeArrayAsInParam(args),
     533                               ComSafeArrayAsInParam(env),
     534                               Bstr(Utf8UserName).raw(),
     535                               Bstr(Utf8Password).raw(),
     536                               u32TimeoutMS,
     537                               &uPID,
     538                               progress.asOutParam());
     539    if (FAILED(rc))
     540        vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
     541    else
    464542    {
    465543        if (fVerbose)
    466         {
    467             if (u32TimeoutMS == 0)
    468                 RTPrintf("Waiting for guest to start process ...\n");
    469             else
    470                 RTPrintf("Waiting for guest to start process (within %ums)\n", u32TimeoutMS);
    471         }
    472 
    473         /* Get current time stamp to later calculate rest of timeout left. */
    474         uint64_t u64StartMS = RTTimeMilliTS();
    475 
    476         /* Execute the process. */
    477         ComPtr<IProgress> progress;
    478         ULONG uPID = 0;
    479         rc = guest->ExecuteProcess(Bstr(Utf8Cmd).raw(),
    480                                    fFlags,
    481                                    ComSafeArrayAsInParam(args),
    482                                    ComSafeArrayAsInParam(env),
    483                                    Bstr(Utf8UserName).raw(),
    484                                    Bstr(Utf8Password).raw(),
    485                                    u32TimeoutMS,
    486                                    &uPID,
    487                                    progress.asOutParam());
    488         if (FAILED(rc))
    489             vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
    490         else
    491         {
    492             if (fVerbose)
    493                 RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.c_str(), uPID);
    494             if (fWaitForExit)
     544            RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.c_str(), uPID);
     545        if (fWaitForExit)
     546        {
     547            if (u32TimeoutMS) /* Wait with a certain timeout. */
    495548            {
    496                 if (u32TimeoutMS) /* Wait with a certain timeout. */
    497                 {
    498                     /* Calculate timeout value left after process has been started.  */
    499                     uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS;
    500                     /* Is timeout still bigger than current difference? */
    501                     if (u32TimeoutMS > u64Elapsed)
     549                /* Calculate timeout value left after process has been started.  */
     550                uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS;
     551                /* Is timeout still bigger than current difference? */
     552                if (u32TimeoutMS > u64Elapsed)
     553                {
     554                    if (fVerbose)
     555                        RTPrintf("Waiting for process to exit (%ums left) ...\n", u32TimeoutMS - u64Elapsed);
     556                }
     557                else
     558                {
     559                    if (fVerbose)
     560                        RTPrintf("No time left to wait for process!\n");
     561                }
     562            }
     563            else if (fVerbose) /* Wait forever. */
     564                RTPrintf("Waiting for process to exit ...\n");
     565
     566            /* Setup signal handling if cancelable. */
     567            ASSERT(progress);
     568            bool fCanceledAlready = false;
     569            BOOL fCancelable;
     570            HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable);
     571            if (FAILED(hrc))
     572                fCancelable = FALSE;
     573            if (fCancelable)
     574                ctrlSignalHandlerInstall();
     575
     576            /* Wait for process to exit ... */
     577            BOOL fCompleted    = FALSE;
     578            BOOL fCanceled     = FALSE;
     579            int  cMilliesSleep = 0;
     580            while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
     581            {
     582                SafeArray<BYTE> aOutputData;
     583                ULONG cbOutputData = 0;
     584
     585                /*
     586                 * Some data left to output?
     587                 */
     588                if (   fWaitForStdOut
     589                    || fWaitForStdErr)
     590                {
     591                    rc = guest->GetProcessOutput(uPID, 0 /* aFlags */,
     592                                                 RT_MAX(0, u32TimeoutMS - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */,
     593                                                 _64K, ComSafeArrayAsOutParam(aOutputData));
     594                    if (FAILED(rc))
    502595                    {
    503                         if (fVerbose)
    504                             RTPrintf("Waiting for process to exit (%ums left) ...\n", u32TimeoutMS - u64Elapsed);
     596                        vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
     597                        cbOutputData = 0;
    505598                    }
    506599                    else
    507600                    {
    508                         if (fVerbose)
    509                             RTPrintf("No time left to wait for process!\n");
    510                     }
    511                 }
    512                 else if (fVerbose) /* Wait forever. */
    513                     RTPrintf("Waiting for process to exit ...\n");
    514 
    515                 /* Setup signal handling if cancelable. */
    516                 ASSERT(progress);
    517                 bool fCanceledAlready = false;
    518                 BOOL fCancelable;
    519                 HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable);
    520                 if (FAILED(hrc))
    521                     fCancelable = FALSE;
    522                 if (fCancelable)
    523                     ctrlSignalHandlerInstall();
    524 
    525                 /* Wait for process to exit ... */
    526                 BOOL fCompleted    = FALSE;
    527                 BOOL fCanceled     = FALSE;
    528                 int  cMilliesSleep = 0;
    529                 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted))))
    530                 {
    531                     SafeArray<BYTE> aOutputData;
    532                     ULONG cbOutputData = 0;
    533 
    534                     /*
    535                      * Some data left to output?
    536                      */
    537                     if (   fWaitForStdOut
    538                         || fWaitForStdErr)
    539                     {
    540                         rc = guest->GetProcessOutput(uPID, 0 /* aFlags */,
    541                                                      RT_MAX(0, u32TimeoutMS - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */,
    542                                                      _64K, ComSafeArrayAsOutParam(aOutputData));
    543                         if (FAILED(rc))
     601                        cbOutputData = aOutputData.size();
     602                        if (cbOutputData > 0)
    544603                        {
    545                             vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
    546                             cbOutputData = 0;
    547                         }
    548                         else
    549                         {
    550                             cbOutputData = aOutputData.size();
    551                             if (cbOutputData > 0)
     604/** @todo r=bird: cat'ing binary data from the guest is not going to work
     605*        reliably if we do conversions like this.  Should probably just
     606*        write the output as it is by default, but bypassing RTStrWrite and
     607*        it's automatic translation.  Adding exec options to convert unix2dos
     608*        and dos2unix.  Use a VFS I/O stream filter for doing this, it's a
     609*        generic problem and the new VFS APIs will handle it more
     610*        transparently. (requires writing dos2unix/unix2dos filters ofc) */
     611                            /* aOutputData has a platform dependent line ending, standardize on
     612                             * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
     613                             * Windows. Otherwise we end up with CR/CR/LF on Windows. */
     614                            ULONG cbOutputDataPrint = cbOutputData;
     615                            for (BYTE *s = aOutputData.raw(), *d = s;
     616                                 s - aOutputData.raw() < (ssize_t)cbOutputData;
     617                                 s++, d++)
    552618                            {
    553 /** @todo r=bird: cat'ing binary data from the guest is not going to work
    554  *        reliably if we do conversions like this.  Should probably just
    555  *        write the output as it is by default, but bypassing RTStrWrite and
    556  *        it's automatic translation.  Adding exec options to convert unix2dos
    557  *        and dos2unix.  Use a VFS I/O stream filter for doing this, it's a
    558  *        generic problem and the new VFS APIs will handle it more
    559  *        transparently. (requires writing dos2unix/unix2dos filters ofc) */
    560                                 /* aOutputData has a platform dependent line ending, standardize on
    561                                  * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on
    562                                  * Windows. Otherwise we end up with CR/CR/LF on Windows. */
    563                                 ULONG cbOutputDataPrint = cbOutputData;
    564                                 for (BYTE *s = aOutputData.raw(), *d = s;
    565                                      s - aOutputData.raw() < (ssize_t)cbOutputData;
    566                                      s++, d++)
     619                                if (*s == '\r')
    567620                                {
    568                                     if (*s == '\r')
    569                                     {
    570                                         /* skip over CR, adjust destination */
    571                                         d--;
    572                                         cbOutputDataPrint--;
    573                                     }
    574                                     else if (s != d)
    575                                         *d = *s;
     621                                    /* skip over CR, adjust destination */
     622                                    d--;
     623                                    cbOutputDataPrint--;
    576624                                }
    577                                 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
     625                                else if (s != d)
     626                                    *d = *s;
    578627                            }
     628                            RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint);
    579629                        }
    580630                    }
    581 
    582                     /* No more output data left? */
    583                     if (cbOutputData <= 0)
    584                     {
    585                         /* Only break out from process handling loop if we processed (displayed)
    586                          * all output data or if there simply never was output data and the process
    587                          * has been marked as complete. */
    588                         if (fCompleted)
    589                             break;
    590 
    591                         /* Then wait a little while ... */
    592                         progress->WaitForCompletion(1 /* ms */);
    593                     }
    594 
    595                     /* Process async cancelation */
    596                     if (g_fGuestCtrlCanceled && !fCanceledAlready)
    597                     {
    598                         hrc = progress->Cancel();
    599                         if (SUCCEEDED(hrc))
    600                             fCanceledAlready = TRUE;
    601                         else
    602                             g_fGuestCtrlCanceled = false;
    603                     }
    604 
    605                     /* Progress canceled by Main API? */
    606                     if (   SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled)))
    607                         && fCanceled)
     631                }
     632
     633                /* No more output data left? */
     634                if (cbOutputData <= 0)
     635                {
     636                    /* Only break out from process handling loop if we processed (displayed)
     637                     * all output data or if there simply never was output data and the process
     638                     * has been marked as complete. */
     639                    if (fCompleted)
    608640                        break;
    609641
    610                     /* Did we run out of time? */
    611                     if (   u32TimeoutMS
    612                         && RTTimeMilliTS() - u64StartMS > u32TimeoutMS)
    613                     {
    614                         progress->Cancel();
    615                         break;
    616                     }
    617 
    618                     /* Don't hog the CPU in a busy loop! */
     642                    /* Then wait a little while ... */
     643                    progress->WaitForCompletion(1 /* ms */);
     644                }
     645
     646                /* Process async cancelation */
     647                if (g_fGuestCtrlCanceled && !fCanceledAlready)
     648                {
     649                    hrc = progress->Cancel();
     650                    if (SUCCEEDED(hrc))
     651                        fCanceledAlready = TRUE;
     652                    else
     653                        g_fGuestCtrlCanceled = false;
     654                }
     655
     656                /* Progress canceled by Main API? */
     657                if (   SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled)))
     658                    && fCanceled)
     659                    break;
     660
     661                /* Did we run out of time? */
     662                if (   u32TimeoutMS
     663                    && RTTimeMilliTS() - u64StartMS > u32TimeoutMS)
     664                {
     665                    progress->Cancel();
     666                    break;
     667                }
     668
     669                /* Don't hog the CPU in a busy loop! */
    619670/** @todo r=bird: I believe I already mentioned that this problem is better
    620  * solved by using WaitForCompletion and GetProcessOutput with timeouts.  The
    621  * 1ms hack above is not what I had in mind.  This quick fix must go away.  */
    622                     if (cbOutputData <= 0)
    623                     {
    624                         if (cMilliesSleep < 100)
    625                             cMilliesSleep++;
    626                         RTThreadSleep(cMilliesSleep);
    627                     }
    628                     else
    629                         cMilliesSleep = 0;
    630                 } /* while */
    631 
    632                 /* Undo signal handling */
    633                 if (fCancelable)
    634                     ctrlSignalHandlerUninstall();
    635 
    636                 /* Report status back to the user. */
    637                 if (fCanceled)
    638                 {
    639                     if (fVerbose)
    640                         RTPrintf("Process execution canceled!\n");
    641                 }
    642                 else if (   fCompleted
    643                          && SUCCEEDED(rc))
    644                 {
    645                     LONG iRc;
    646                     CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
    647                     if (FAILED(iRc))
    648                         vrc = ctrlPrintProgressError(progress);
    649                     else if (fVerbose)
    650                     {
    651                         ULONG uRetStatus, uRetExitCode, uRetFlags;
    652                         rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);
    653                         if (SUCCEEDED(rc))
    654                             RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, uRetStatus, ctrlExecGetStatus(uRetStatus), uRetFlags);
    655                         /** @todo r=bird: The guest application exit code
    656                          *        should by default be returned by the command.
    657                          *        Non-normal exits and exit codes outside
    658                          *        the portable range (0..127) should be
    659                          *        translated to more portable values.
    660                          *
    661                          *  Alternatively, we could return say exit code 16 if
    662                          *  the guest command failed, 17 if it terminated on a
    663                          *  signal, 18 if it abended and exit code 19 if it
    664                          *  timed out.  This is possibly a better solution.
    665                          *
    666                          *  Think about people using VBoxManage for scripting. */
    667                     }
     671* solved by using WaitForCompletion and GetProcessOutput with timeouts.  The
     672* 1ms hack above is not what I had in mind.  This quick fix must go away.  */
     673                if (cbOutputData <= 0)
     674                {
     675                    if (cMilliesSleep < 100)
     676                        cMilliesSleep++;
     677                    RTThreadSleep(cMilliesSleep);
    668678                }
    669679                else
    670                 {
    671                     if (fVerbose)
    672                         RTPrintf("Process execution aborted!\n");
    673                 }
     680                    cMilliesSleep = 0;
     681            } /* while */
     682
     683            /* Undo signal handling */
     684            if (fCancelable)
     685                ctrlSignalHandlerUninstall();
     686
     687            /* Report status back to the user. */
     688            if (fCanceled)
     689            {
     690                if (fVerbose)
     691                    RTPrintf("Process execution canceled!\n");
     692                rcProc = RTEXITCODE_EXEC_CANCELED;
    674693            }
    675         }
    676         ctrlUninitVM(a);
     694            else if (   fCompleted
     695                     && SUCCEEDED(rc))
     696            {
     697                LONG iRc;
     698                CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc);
     699                if (FAILED(iRc))
     700                    vrc = ctrlPrintProgressError(progress);
     701                else
     702                {
     703                    ULONG uRetStatus, uRetExitCode, uRetFlags;
     704                    rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &uRetStatus);
     705                    if (SUCCEEDED(rc) && fVerbose)
     706                        RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, uRetStatus, ctrlExecProcessStatusToText(uRetStatus), uRetFlags);
     707                    rcProc = ctrlExecProcessStatusToExitCode(uRetStatus);
     708                }
     709            }
     710            else
     711            {
     712                if (fVerbose)
     713                    RTPrintf("Process execution aborted!\n");
     714            }
     715        }
    677716    }
    678717
    679718    if (RT_FAILURE(vrc) || FAILED(rc))
    680719        return RTEXITCODE_FAILURE;
    681     return RTEXITCODE_SUCCESS;
     720    return rcProc;
    682721}
    683722
     
    10471086                               uint32_t fFlags)
    10481087{
     1088    AssertPtrReturn(pGuest, VERR_INVALID_PARAMETER);
    10491089    AssertPtrReturn(pszSource, VERR_INVALID_PARAMETER);
    10501090    AssertPtrReturn(pszDest, VERR_INVALID_PARAMETER);
     
    10681108}
    10691109
    1070 static int handleCtrlCopyTo(HandlerArg *a)
    1071 {
     1110static int handleCtrlCopyTo(ComPtr<IGuest> guest, HandlerArg *pArg)
     1111{
     1112    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     1113
    10721114    /*
    10731115     * Check the syntax.  We can deduce the correct syntax from the number of
    10741116     * arguments.
    10751117     */
    1076     if (a->argc < 3) /* At least the source + destination should be present :-). */
     1118    if (pArg->argc < 2) /* At least the source + destination should be present :-). */
    10771119        return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
    10781120
     
    10901132    RTGETOPTUNION ValueUnion;
    10911133    RTGETOPTSTATE GetState;
    1092     RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
     1134    RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
    10931135
    10941136    Utf8Str Utf8Source;
     
    11791221                           "No user name specified!");
    11801222    HRESULT rc = S_OK;
    1181     ComPtr<IGuest> guest;
    1182     vrc = ctrlInitVM(a, a->argv[0] /* VM Name */, &guest);
    1183     if (RT_SUCCESS(vrc))
    1184     {
    1185         if (fVerbose)
    1186         {
    1187             if (fDryRun)
    1188                 RTPrintf("Dry run - no files copied!\n");
    1189             RTPrintf("Gathering file information ...\n");
    1190         }
    1191 
    1192         RTLISTNODE listToCopy;
    1193         uint32_t cObjects = 0;
    1194         vrc = ctrlCopyInit(Utf8Source.c_str(), Utf8Dest.c_str(), fFlags,
    1195                            &cObjects, &listToCopy);
    1196         if (RT_FAILURE(vrc))
    1197         {
    1198             switch (vrc)
     1223    if (fVerbose)
     1224    {
     1225        if (fDryRun)
     1226            RTPrintf("Dry run - no files copied!\n");
     1227        RTPrintf("Gathering file information ...\n");
     1228    }
     1229
     1230    RTLISTNODE listToCopy;
     1231    uint32_t cObjects = 0;
     1232    vrc = ctrlCopyInit(Utf8Source.c_str(), Utf8Dest.c_str(), fFlags,
     1233                       &cObjects, &listToCopy);
     1234    if (RT_FAILURE(vrc))
     1235    {
     1236        switch (vrc)
     1237        {
     1238            case VERR_NOT_FOUND:
     1239                RTMsgError("No files to copy found!\n");
     1240                break;
     1241
     1242            case VERR_FILE_NOT_FOUND:
     1243                RTMsgError("Source path \"%s\" not found!\n", Utf8Source.c_str());
     1244                break;
     1245
     1246            default:
     1247                RTMsgError("Failed to initialize, rc=%Rrc\n", vrc);
     1248                break;
     1249        }
     1250    }
     1251    else
     1252    {
     1253        PDIRECTORYENTRY pNode;
     1254        if (RT_SUCCESS(vrc))
     1255        {
     1256            if (fVerbose)
    11991257            {
    1200                 case VERR_NOT_FOUND:
    1201                     RTMsgError("No files to copy found!\n");
     1258                if (fCopyRecursive)
     1259                    RTPrintf("Recursively copying \"%s\" to \"%s\" (%u file(s)) ...\n",
     1260                             Utf8Source.c_str(), Utf8Dest.c_str(), cObjects);
     1261                else
     1262                    RTPrintf("Copying \"%s\" to \"%s\" (%u file(s)) ...\n",
     1263                             Utf8Source.c_str(), Utf8Dest.c_str(), cObjects);
     1264            }
     1265
     1266            uint32_t uCurObject = 1;
     1267            RTListForEach(&listToCopy, pNode, DIRECTORYENTRY, Node)
     1268            {
     1269                if (!fDryRun)
     1270                {
     1271                    if (fVerbose)
     1272                        RTPrintf("Copying \"%s\" to \"%s\" (%u/%u) ...\n",
     1273                                 pNode->pszSourcePath, pNode->pszDestPath, uCurObject, cObjects);
     1274                    /* Finally copy the desired file (if no dry run selected). */
     1275                    if (!fDryRun)
     1276                        vrc = ctrlCopyFileToGuest(guest, fVerbose, pNode->pszSourcePath, pNode->pszDestPath,
     1277                                                  Utf8UserName.c_str(), Utf8Password.c_str(), fFlags);
     1278                }
     1279                if (RT_FAILURE(vrc))
    12021280                    break;
    1203 
    1204                 case VERR_FILE_NOT_FOUND:
    1205                     RTMsgError("Source path \"%s\" not found!\n", Utf8Source.c_str());
    1206                     break;
    1207 
    1208                 default:
    1209                     RTMsgError("Failed to initialize, rc=%Rrc\n", vrc);
    1210                     break;
     1281                uCurObject++;
    12111282            }
    1212         }
    1213         else
    1214         {
    1215             PDIRECTORYENTRY pNode;
    1216             if (RT_SUCCESS(vrc))
    1217             {
    1218                 if (fVerbose)
    1219                 {
    1220                     if (fCopyRecursive)
    1221                         RTPrintf("Recursively copying \"%s\" to \"%s\" (%u file(s)) ...\n",
    1222                                  Utf8Source.c_str(), Utf8Dest.c_str(), cObjects);
    1223                     else
    1224                         RTPrintf("Copying \"%s\" to \"%s\" (%u file(s)) ...\n",
    1225                                  Utf8Source.c_str(), Utf8Dest.c_str(), cObjects);
    1226                 }
    1227 
    1228                 uint32_t uCurObject = 1;
    1229                 RTListForEach(&listToCopy, pNode, DIRECTORYENTRY, Node)
    1230                 {
    1231                     if (!fDryRun)
    1232                     {
    1233                         if (fVerbose)
    1234                             RTPrintf("Copying \"%s\" to \"%s\" (%u/%u) ...\n",
    1235                                      pNode->pszSourcePath, pNode->pszDestPath, uCurObject, cObjects);
    1236                         /* Finally copy the desired file (if no dry run selected). */
    1237                         if (!fDryRun)
    1238                             vrc = ctrlCopyFileToGuest(guest, fVerbose, pNode->pszSourcePath, pNode->pszDestPath,
    1239                                                       Utf8UserName.c_str(), Utf8Password.c_str(), fFlags);
    1240                     }
    1241                     if (RT_FAILURE(vrc))
    1242                         break;
    1243                     uCurObject++;
    1244                 }
    1245                 if (RT_SUCCESS(vrc) && fVerbose)
    1246                     RTPrintf("Copy operation successful!\n");
    1247             }
    1248             ctrlDirectoryListDestroy(&listToCopy);
    1249         }
    1250         ctrlUninitVM(a);
     1283            if (RT_SUCCESS(vrc) && fVerbose)
     1284                RTPrintf("Copy operation successful!\n");
     1285        }
     1286        ctrlDirectoryListDestroy(&listToCopy);
    12511287    }
    12521288
    12531289    if (RT_FAILURE(vrc))
    12541290        rc = VBOX_E_IPRT_ERROR;
    1255     return SUCCEEDED(rc) ? 0 : 1;
    1256 }
    1257 
    1258 static int handleCtrlCreateDirectory(HandlerArg *a)
    1259 {
     1291    return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
     1292}
     1293
     1294static int handleCtrlCreateDirectory(ComPtr<IGuest> guest, HandlerArg *pArg)
     1295{
     1296    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     1297
    12601298    /*
    12611299     * Check the syntax.  We can deduce the correct syntax from the number of
    12621300     * arguments.
    12631301     */
    1264     if (a->argc < 2) /* At least the directory we want to create should be present :-). */
     1302    if (pArg->argc < 2) /* At least the directory we want to create should be present :-). */
    12651303        return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
    12661304
     
    12771315    RTGETOPTUNION ValueUnion;
    12781316    RTGETOPTSTATE GetState;
    1279     RTGetOptInit(&GetState, a->argc, a->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0);
     1317    RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
    12801318
    12811319    Utf8Str Utf8UserName;
     
    13511389
    13521390    HRESULT rc = S_OK;
    1353     ComPtr<IGuest> guest;
    1354     vrc = ctrlInitVM(a, a->argv[0] /* VM Name */, &guest);
    1355     if (RT_SUCCESS(vrc))
    1356     {
    1357         if (fVerbose && uNumDirs > 1)
    1358             RTPrintf("Creating %ld directories ...\n", uNumDirs);
    1359 
    1360         PDIRECTORYENTRY pNode;
    1361         RTListForEach(&listDirs, pNode, DIRECTORYENTRY, Node)
    1362         {
    1363             if (fVerbose)
    1364                 RTPrintf("Creating directory \"%s\" ...\n", pNode->pszDestPath);
    1365 
    1366             ComPtr<IProgress> progress;
    1367             rc = guest->CreateDirectory(Bstr(pNode->pszDestPath).raw(),
    1368                                         Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
    1369                                         uMode, fFlags, progress.asOutParam());
    1370             if (FAILED(rc))
    1371             {
    1372                 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
    1373                 break;
    1374             }
    1375         }
    1376         ctrlUninitVM(a);
     1391    if (fVerbose && uNumDirs > 1)
     1392        RTPrintf("Creating %u directories ...\n", uNumDirs);
     1393
     1394    PDIRECTORYENTRY pNode;
     1395    RTListForEach(&listDirs, pNode, DIRECTORYENTRY, Node)
     1396    {
     1397        if (fVerbose)
     1398            RTPrintf("Creating directory \"%s\" ...\n", pNode->pszDestPath);
     1399
     1400        ComPtr<IProgress> progress;
     1401        rc = guest->CreateDirectory(Bstr(pNode->pszDestPath).raw(),
     1402                                    Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(),
     1403                                    uMode, fFlags, progress.asOutParam());
     1404        if (FAILED(rc))
     1405        {
     1406            /* rc ignored */ ctrlPrintError(guest, COM_IIDOF(IGuest));
     1407            break;
     1408        }
    13771409    }
    13781410    ctrlDirectoryListDestroy(&listDirs);
    1379 
    1380     if (RT_FAILURE(vrc))
    1381         rc = VBOX_E_IPRT_ERROR;
    1382     return SUCCEEDED(rc) ? 0 : 1;
    1383 }
    1384 
    1385 static int handleCtrlUpdateAdditions(HandlerArg *a)
    1386 {
     1411    return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
     1412}
     1413
     1414static int handleCtrlUpdateAdditions(ComPtr<IGuest> guest, HandlerArg *pArg)
     1415{
     1416    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     1417
    13871418    /*
    13881419     * Check the syntax.  We can deduce the correct syntax from the number of
    13891420     * arguments.
    13901421     */
    1391     if (a->argc < 1) /* At least the VM name should be present :-). */
     1422    if (pArg->argc < 1) /* At least the VM name should be present :-). */
    13921423        return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
    13931424
     
    13951426    bool fVerbose = false;
    13961427
    1397 /** @todo r=bird: Use RTGetOpt here, no new code using strcmp-if-switching!  */
    1398     /* Iterate through all possible commands (if available). */
    1399     bool usageOK = true;
    1400     for (int i = 1; usageOK && i < a->argc; i++)
    1401     {
    1402         if (!strcmp(a->argv[i], "--source"))
    1403         {
    1404             if (i + 1 >= a->argc)
    1405                 usageOK = false;
    1406             else
    1407             {
    1408                 Utf8Source = a->argv[i + 1];
    1409                 ++i;
    1410             }
    1411         }
    1412         else if (!strcmp(a->argv[i], "--verbose"))
    1413             fVerbose = true;
     1428    static const RTGETOPTDEF s_aOptions[] =
     1429    {
     1430        { "--source",              's',         RTGETOPT_REQ_STRING  },
     1431        { "--verbose",             'v',         RTGETOPT_REQ_NOTHING }
     1432    };
     1433
     1434    int ch;
     1435    RTGETOPTUNION ValueUnion;
     1436    RTGETOPTSTATE GetState;
     1437    RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 0, 0);
     1438
     1439    int vrc = VINF_SUCCESS;
     1440    while (   (ch = RTGetOpt(&GetState, &ValueUnion))
     1441           && RT_SUCCESS(vrc))
     1442    {
     1443        switch (ch)
     1444        {
     1445            case 's':
     1446                Utf8Source = ValueUnion.psz;
     1447                break;
     1448
     1449            case 'v':
     1450                fVerbose = true;
     1451                break;
     1452
     1453            default:
     1454                return RTGetOptPrintError(ch, &ValueUnion);
     1455        }
     1456    }
     1457
     1458    if (fVerbose)
     1459        RTPrintf("Updating Guest Additions ...\n");
     1460
     1461#ifdef DEBUG_andy
     1462    if (Utf8Source.isEmpty())
     1463        Utf8Source = "c:\\Downloads\\VBoxGuestAdditions-r67158.iso";
     1464#endif
     1465
     1466    /* Determine source if not set yet. */
     1467    if (Utf8Source.isEmpty())
     1468    {
     1469        char strTemp[RTPATH_MAX];
     1470        vrc = RTPathAppPrivateNoArch(strTemp, sizeof(strTemp));
     1471        AssertRC(vrc);
     1472        Utf8Str Utf8Src1 = Utf8Str(strTemp).append("/VBoxGuestAdditions.iso");
     1473
     1474        vrc = RTPathExecDir(strTemp, sizeof(strTemp));
     1475        AssertRC(vrc);
     1476        Utf8Str Utf8Src2 = Utf8Str(strTemp).append("/additions/VBoxGuestAdditions.iso");
     1477
     1478        /* Check the standard image locations */
     1479        if (RTFileExists(Utf8Src1.c_str()))
     1480            Utf8Source = Utf8Src1;
     1481        else if (RTFileExists(Utf8Src2.c_str()))
     1482            Utf8Source = Utf8Src2;
    14141483        else
    1415             return errorSyntax(USAGE_GUESTCONTROL,
    1416                                "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
    1417     }
    1418 
    1419     if (!usageOK)
    1420         return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
    1421 
    1422     HRESULT rc = S_OK;
    1423     ComPtr<IGuest> guest;
    1424     int vrc = ctrlInitVM(a, a->argv[0] /* VM Name */, &guest);
     1484        {
     1485            RTMsgError("Source could not be determined! Please use --source to specify a valid source.\n");
     1486            vrc = VERR_FILE_NOT_FOUND;
     1487        }
     1488    }
     1489    else if (!RTFileExists(Utf8Source.c_str()))
     1490    {
     1491        RTMsgError("Source \"%s\" does not exist!\n", Utf8Source.c_str());
     1492        vrc = VERR_FILE_NOT_FOUND;
     1493    }
     1494
    14251495    if (RT_SUCCESS(vrc))
    14261496    {
    14271497        if (fVerbose)
    1428             RTPrintf("Updating Guest Additions of machine \"%s\" ...\n", a->argv[0]);
    1429 
    1430 #ifdef DEBUG_andy
    1431         if (Utf8Source.isEmpty())
    1432             Utf8Source = "c:\\Downloads\\VBoxGuestAdditions-r67158.iso";
    1433 #endif
    1434         /* Determine source if not set yet. */
    1435         if (Utf8Source.isEmpty())
    1436         {
    1437             char strTemp[RTPATH_MAX];
    1438             vrc = RTPathAppPrivateNoArch(strTemp, sizeof(strTemp));
    1439             AssertRC(vrc);
    1440             Utf8Str Utf8Src1 = Utf8Str(strTemp).append("/VBoxGuestAdditions.iso");
    1441 
    1442             vrc = RTPathExecDir(strTemp, sizeof(strTemp));
    1443             AssertRC(vrc);
    1444             Utf8Str Utf8Src2 = Utf8Str(strTemp).append("/additions/VBoxGuestAdditions.iso");
    1445 
    1446             /* Check the standard image locations */
    1447             if (RTFileExists(Utf8Src1.c_str()))
    1448                 Utf8Source = Utf8Src1;
    1449             else if (RTFileExists(Utf8Src2.c_str()))
    1450                 Utf8Source = Utf8Src2;
    1451             else
    1452             {
    1453                 RTMsgError("Source could not be determined! Please use --source to specify a valid source.\n");
    1454                 vrc = VERR_FILE_NOT_FOUND;
    1455             }
    1456         }
    1457         else if (!RTFileExists(Utf8Source.c_str()))
    1458         {
    1459             RTMsgError("Source \"%s\" does not exist!\n", Utf8Source.c_str());
    1460             vrc = VERR_FILE_NOT_FOUND;
    1461         }
    1462 
    1463         if (RT_SUCCESS(vrc))
    1464         {
    1465             if (fVerbose)
    1466                 RTPrintf("Using source: %s\n", Utf8Source.c_str());
    1467 
    1468             ComPtr<IProgress> progress;
    1469             CHECK_ERROR(guest, UpdateGuestAdditions(Bstr(Utf8Source).raw(),
    1470                                                     /* Wait for whole update process to complete. */
    1471                                                     AdditionsUpdateFlag_None,
    1472                                                     progress.asOutParam()));
     1498            RTPrintf("Using source: %s\n", Utf8Source.c_str());
     1499
     1500        HRESULT rc = S_OK;
     1501        ComPtr<IProgress> progress;
     1502        CHECK_ERROR(guest, UpdateGuestAdditions(Bstr(Utf8Source).raw(),
     1503                                                /* Wait for whole update process to complete. */
     1504                                                AdditionsUpdateFlag_None,
     1505                                                progress.asOutParam()));
     1506        if (FAILED(rc))
     1507            vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
     1508        else
     1509        {
     1510            rc = showProgress(progress);
    14731511            if (FAILED(rc))
    1474                 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest));
    1475             else
    1476             {
    1477                 rc = showProgress(progress);
    1478                 if (FAILED(rc))
    1479                     vrc = ctrlPrintProgressError(progress);
    1480                 else if (fVerbose)
    1481                     RTPrintf("Guest Additions update successful.\n");
    1482             }
    1483         }
    1484         ctrlUninitVM(a);
    1485     }
    1486 
    1487     if (RT_FAILURE(vrc))
    1488         rc = VBOX_E_IPRT_ERROR;
    1489     return SUCCEEDED(rc) ? 0 : 1;
     1512                vrc = ctrlPrintProgressError(progress);
     1513            else if (fVerbose)
     1514                RTPrintf("Guest Additions update successful.\n");
     1515        }
     1516    }
     1517
     1518    return RT_SUCCESS(vrc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
    14901519}
    14911520
     
    14961525 * @note see the command line API description for parameters
    14971526 */
    1498 int handleGuestControl(HandlerArg *a)
    1499 {
    1500     /** @todo This command does not follow the syntax where the <uuid|vmname>
    1501      * comes between the command and subcommand.  The commands controlvm,
    1502      * snapshot and debugvm puts it between.  The commands guestproperty,
    1503      * guestcontrol and sharedfolder puts it after (the latter is
    1504      * questionable).
    1505      *
    1506      * I would propose changing guestcontrol and guestproperty to the controlvm
    1507      * syntax in 4.1.  Whether sharedfolder should be changed as well is a bit
    1508      * more open.
    1509      */
    1510 
    1511     HandlerArg arg = *a;
    1512     arg.argc = a->argc - 1;
    1513     arg.argv = a->argv + 1;
    1514 
    1515     if (a->argc <= 0)
     1527int handleGuestControl(HandlerArg *pArg)
     1528{
     1529    AssertPtrReturn(pArg, VERR_INVALID_PARAMETER);
     1530
     1531    /* At least the VM name + sub command needs to be specified. */
     1532    if (pArg->argc <= 2)
    15161533        return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
    15171534
    1518     /* switch (cmd) */
    1519     /** @todo r=bird: Subcommands are not case insensitive for other commands
    1520      *        like controlvm.  Drop it. */
    1521     if (   !RTStrICmp(a->argv[0], "exec")
    1522         || !RTStrICmp(a->argv[0], "execute"))
    1523         return handleCtrlExecProgram(&arg);
    1524 
    1525     if (   !RTStrICmp(a->argv[0], "copyto")
    1526         || !RTStrICmp(a->argv[0], "cp"))
    1527         return handleCtrlCopyTo(&arg);
    1528 
    1529     if (   !RTStrICmp(a->argv[0], "createdirectory")
    1530         || !RTStrICmp(a->argv[0], "createdir")
    1531         || !RTStrICmp(a->argv[0], "mkdir")
    1532         || !RTStrICmp(a->argv[0], "md"))
    1533         return handleCtrlCreateDirectory(&arg);
    1534 
    1535     if (   !RTStrICmp(a->argv[0], "updateadditions")
    1536         || !RTStrICmp(a->argv[0], "updateadds"))
    1537         return handleCtrlUpdateAdditions(&arg);
    1538 
    1539     /* default: */
    1540     return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters");
     1535    HandlerArg arg = *pArg;
     1536    arg.argc = pArg->argc - 2; /* Skip VM name and sub command. */
     1537    arg.argv = pArg->argv + 2; /* Same here. */
     1538
     1539    ComPtr<IGuest> guest;
     1540    int vrc = ctrlInitVM(pArg, pArg->argv[0] /* VM Name */, &guest);
     1541    if (RT_SUCCESS(vrc))
     1542    {
     1543        int rcExit;
     1544        if (   !strcmp(pArg->argv[1], "exec")
     1545            || !strcmp(pArg->argv[1], "execute"))
     1546        {
     1547            rcExit = handleCtrlExecProgram(guest, &arg);
     1548        }
     1549        else if (   !strcmp(pArg->argv[1], "copyto")
     1550                 || !strcmp(pArg->argv[1], "cp"))
     1551        {
     1552            rcExit = handleCtrlCopyTo(guest, &arg);
     1553        }
     1554        else if (   !strcmp(pArg->argv[1], "createdirectory")
     1555                 || !strcmp(pArg->argv[1], "createdir")
     1556                 || !strcmp(pArg->argv[1], "mkdir")
     1557                 || !strcmp(pArg->argv[1], "md"))
     1558        {
     1559            rcExit = handleCtrlCreateDirectory(guest, &arg);
     1560        }
     1561        else if (   !strcmp(pArg->argv[1], "updateadditions")
     1562                 || !strcmp(pArg->argv[1], "updateadds"))
     1563        {
     1564            rcExit = handleCtrlUpdateAdditions(guest, &arg);
     1565        }
     1566        else
     1567            rcExit = errorSyntax(USAGE_GUESTCONTROL, "No sub command specified!");
     1568
     1569        ctrlUninitVM(pArg);
     1570        return rcExit;
     1571    }
     1572    return RTEXITCODE_FAILURE;
    15411573}
    15421574
    15431575#endif /* !VBOX_ONLY_DOCS */
     1576
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