VirtualBox

Changeset 55609 in vbox


Ignore:
Timestamp:
May 3, 2015 1:00:22 AM (10 years ago)
Author:
vboxsync
Message:

VBoxManageGuestCtrl.cpp: Implemented parsing vm and sub-command name along with common options, dialing back the pedanticness when using --username and the rest with commands requiring no guest session. Also replaced several C++ maps/vectors/whatever inefficient stuff that threatens to throw bad-alloc with processing non-options in the VINF_GETOPT_NOT_OPTION case (argv could also be worked directly from the first occurence of VINF_GETOPT_NOT_OPTION if we liked). These changes concerns mkdir, rmdir rm and mv so far - haven't had time to test these. Added '-f' to the 'rm' command. Updated usage info.

File:
1 edited

Legend:

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

    r55607 r55609  
    7373/** Common option definitions. */
    7474#define GCTLCMD_COMMON_OPTION_DEFS() \
    75     { "--username",            GCTLCMD_COMMON_OPT_USER,         RTGETOPT_REQ_STRING  }, \
    76     { "--passwordfile",        GCTLCMD_COMMON_OPT_PASSWORD_FILE,RTGETOPT_REQ_STRING  }, \
    77     { "--password",            GCTLCMD_COMMON_OPT_PASSWORD,     RTGETOPT_REQ_STRING  }, \
    78     { "--domain",              GCTLCMD_COMMON_OPT_DOMAIN,       RTGETOPT_REQ_STRING  }, \
    79     { "--quiet",               'q',                             RTGETOPT_REQ_NOTHING }, \
    80     { "--verbose",             'v',                             RTGETOPT_REQ_NOTHING },
     75        { "--username",             GCTLCMD_COMMON_OPT_USER,            RTGETOPT_REQ_STRING  }, \
     76        { "--passwordfile",         GCTLCMD_COMMON_OPT_PASSWORD_FILE,   RTGETOPT_REQ_STRING  }, \
     77        { "--password",             GCTLCMD_COMMON_OPT_PASSWORD,        RTGETOPT_REQ_STRING  }, \
     78        { "--domain",               GCTLCMD_COMMON_OPT_DOMAIN,          RTGETOPT_REQ_STRING  }, \
     79        { "--quiet",                'q',                                RTGETOPT_REQ_NOTHING }, \
     80        { "--verbose",              'v',                                RTGETOPT_REQ_NOTHING },
    8181
    8282/** Handles common options in the typical option parsing switch. */
     
    128128     * @param   pCtx            Pointer to command context to use.
    129129     */
    130     DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (struct GCTLCMDCTX *pCtx));
     130    DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (struct GCTLCMDCTX *pCtx, int argc, char **argv));
    131131
    132132    /** The command usage flags. */
     
    161161    const char *pszVmNameOrUuid;
    162162
     163    /** Whether we've done the post option parsing init already. */
     164    bool fPostOptionParsingInited;
    163165    /** Whether we've locked the VM session. */
    164166    bool fLockedVmSession;
     
    289291};
    290292
    291 enum GETOPTDEF_COPY
    292 {
    293     GETOPTDEF_COPY_DRYRUN = 1000,
    294     GETOPTDEF_COPY_FOLLOW,
    295     GETOPTDEF_COPY_TARGETDIR
    296 };
    297 
    298 
    299293enum kStreamTransform
    300294{
     
    316310void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2, uint32_t uSubCmd)
    317311{
    318     RTStrmPrintf(pStrm,
    319                        "%s guestcontrol %s    <uuid|vmname>\n%s",
    320                  pcszSep1, pcszSep2,
    321                  uSubCmd == ~0U ? "\n" : "");
     312    const uint32_t fAnonSubCmds = USAGE_GSTCTRL_CLOSESESSION
     313                                | USAGE_GSTCTRL_LIST
     314                                | USAGE_GSTCTRL_CLOSEPROCESS
     315                                | USAGE_GSTCTRL_CLOSESESSION
     316                                | USAGE_GSTCTRL_UPDATEGA
     317                                | USAGE_GSTCTRL_WATCH;
     318
    322319    /*                0         1         2         3         4         5         6         7         8XXXXXXXXXX */
    323320    /*                0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
    324 #define COMMON_OPTION_HELP \
    325                      "                              [--username <name>] [--domain <domain>]\n" \
    326                      "                              [--passwordfile <file> | --password <password>]\n" \
    327                      "                              [--verbose|-v] [--quiet|-q]\n"
    328 #define COMMON_OPTION_HELP_ANON \
    329                      "                              [--verbose|-v] [--quiet|-q]\n"
     321    if (~fAnonSubCmds & uSubCmd)
     322        RTStrmPrintf(pStrm,
     323                     "%s guestcontrol %s    <uuid|vmname> [--verbose|-v] [--quiet|-q]\n"
     324                     "                              [--username <name>] [--domain <domain>]\n"
     325                     "                              [--passwordfile <file> | --password <password>]\n%s",
     326                     pcszSep1, pcszSep2, uSubCmd == ~0U ? "\n" : "");
    330327    if (uSubCmd & USAGE_GSTCTRL_RUN)
    331328        RTStrmPrintf(pStrm,
    332                      "                              run\n" COMMON_OPTION_HELP
    333                      "                              [--image <path to executable>] [--timeout <msec>]\n"
    334                      "                              [--putenv <NAME>[=<VALUE>]] [--unquoted-args]\n"
     329                     "                              run [common-options]\n"
     330                     "                              [--exe <path to executable>] [--timeout <msec>]\n"
     331                     "                              [-E|--putenv <NAME>[=<VALUE>]] [--unquoted-args]\n"
     332                     "                              [--ignore-operhaned-processes] [--no-profile]\n"
    335333                     "                              [--no-wait-stdout|--wait-stdout]\n"
    336334                     "                              [--no-wait-stderr|--wait-stderr]\n"
     
    340338    if (uSubCmd & USAGE_GSTCTRL_START)
    341339        RTStrmPrintf(pStrm,
    342                      "                              start\n" COMMON_OPTION_HELP
    343                      "                              [--image <path to executable>] [--timeout <msec>]\n"
    344                      "                              [--putenv <NAME>[=<VALUE>]] [--unquoted-args]\n"
     340                     "                              start [common-options]\n"
     341                     "                              [--exe <path to executable>] [--timeout <msec>]\n"
     342                     "                              [-E|--putenv <NAME>[=<VALUE>]] [--unquoted-args]\n"
     343                     "                              [--ignore-operhaned-processes] [--no-profile]\n"
    345344                     "                              -- <program/arg0> [argument1] ... [argumentN]]\n"
    346                  "\n");
    347     /*            0         1         2         3         4         5         6         7         8XXXXXXXXXX */
    348     /*            0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
     345                     "\n");
    349346    if (uSubCmd & USAGE_GSTCTRL_COPYFROM)
    350347        RTStrmPrintf(pStrm,
    351                  "                              copyfrom\n" COMMON_OPTION_HELP
    352                  "                              [--dryrun] [--follow] [--recursive]\n"
    353                  "                              <guest source> <host dest>\n"
    354                  "\n");
     348                     "                              copyfrom [common-options]\n"
     349                     "                              [--dryrun] [--follow] [-R|--recursive]\n"
     350                     "                              <guest-src0> [guest-src1 [...]] <host-dst>\n"
     351                     "\n"
     352                     "                              copyfrom [common-options]\n"
     353                     "                              [--dryrun] [--follow] [-R|--recursive]\n"
     354                     "                              [--target-directory <host-dst-dir>]\n"
     355                     "                              <guest-src0> [guest-src1 [...]]\n"
     356                     "\n");
    355357    if (uSubCmd & USAGE_GSTCTRL_COPYTO)
    356358        RTStrmPrintf(pStrm,
    357                  "                              copyto\n" COMMON_OPTION_HELP
    358                  "                              [--dryrun] [--follow] [--recursive]\n"
    359                  "                              <host source> <guest dest> "
    360                  "\n");
     359                     "                              copyto [common-options]\n"
     360                     "                              [--dryrun] [--follow] [-R|--recursive]\n"
     361                     "                              <host-src0> [host-src1 [...]] <guest-dst>\n"
     362                     "\n"
     363                     "                              copyto [common-options]\n"
     364                     "                              [--dryrun] [--follow] [-R|--recursive]\n"
     365                     "                              [--target-directory <guest-dst>]\n"
     366                     "                              <host-src0> [host-src1 [...]]\n"
     367                     "\n");
    361368    if (uSubCmd & USAGE_GSTCTRL_MKDIR)
    362369        RTStrmPrintf(pStrm,
    363                  "                              mkdir|md|createdir[ectory]\n" COMMON_OPTION_HELP
    364                  "                              [--parents] [--mode <mode>]\n"
    365                  "                              <guest directory> [...]\n"
    366                  "\n");
     370                     "                              mkdir|createdir[ectory] [common-options]\n"
     371                     "                              [--parents] [--mode <mode>]\n"
     372                     "                              <guest directory> [...]\n"
     373                     "\n");
    367374    if (uSubCmd & USAGE_GSTCTRL_RMDIR)
    368375        RTStrmPrintf(pStrm,
    369                  "                              rmdir|removedir[ectory]\n" COMMON_OPTION_HELP
    370                  "                              [--recursive|-R|-r]\n"
    371                  "                              <guest directory> [...]\n"
    372                  "\n");
     376                     "                              rmdir|removedir[ectory] [common-options]\n"
     377                     "                              [-R|--recursive]\n"
     378                     "                              <guest directory> [...]\n"
     379                     "\n");
    373380    if (uSubCmd & USAGE_GSTCTRL_RM)
    374381        RTStrmPrintf(pStrm,
    375                  "                              removefile|rm\n" COMMON_OPTION_HELP
    376                  "                              <guest file> [...]\n"
    377                  "\n");
     382                     "                              removefile|rm [common-options] [-f|--force]\n"
     383                     "                              <guest file> [...]\n"
     384                     "\n");
    378385    if (uSubCmd & USAGE_GSTCTRL_MV)
    379386        RTStrmPrintf(pStrm,
    380                  "                              mv|move|ren[ame]\n" COMMON_OPTION_HELP
    381                  "                              <source> [source1 [...]] <dest>\n"
    382                  "\n");
     387                     "                              mv|move|ren[ame] [common-options]\n"
     388                     "                              <source> [source1 [...]] <dest>\n"
     389                     "\n");
    383390    if (uSubCmd & USAGE_GSTCTRL_MKTEMP)
    384391        RTStrmPrintf(pStrm,
    385                  "                              mktemp|createtemp[orary]\n" COMMON_OPTION_HELP
    386                  "                              [--secure] [--mode <mode>] [--tmpdir <directory>]\n"
    387                  "                              <template>\n"
    388                  "\n");
     392                     "                              mktemp|createtemp[orary] [common-options]\n"
     393                     "                              [--secure] [--mode <mode>] [--tmpdir <directory>]\n"
     394                     "                              <template>\n"
     395                     "\n");
    389396    if (uSubCmd & USAGE_GSTCTRL_STAT)
    390397        RTStrmPrintf(pStrm,
    391                  "                              stat\n" COMMON_OPTION_HELP
    392                  "                              <file> [...]\n"
    393                  "\n");
    394 
    395     /* Command not requiring authentication. */
    396     if (uSubCmd & USAGE_GSTCTRL_LIST)
     398                     "                              stat [common-options]\n"
     399                     "                              <file> [...]\n"
     400                     "\n");
     401
     402    /*
     403     * Command not requiring authentication.
     404     */
     405    if (fAnonSubCmds & uSubCmd)
     406    {
     407        /*                0         1         2         3         4         5         6         7         8XXXXXXXXXX */
     408        /*                0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */
    397409        RTStrmPrintf(pStrm,
    398                  "                              list <all|sessions|processes|files>\n" COMMON_OPTION_HELP_ANON
    399                  "\n");
    400     if (uSubCmd & USAGE_GSTCTRL_CLOSEPROCESS)
    401         RTStrmPrintf(pStrm,
    402                  "                              closeprocess\n" COMMON_OPTION_HELP_ANON
    403                  "                              <   --session-id <ID>\n"
    404                  "                                | --session-name <name or pattern>\n"
    405                  "                              <PID1> [PID1 [...]]\n"
    406                  "\n");
    407     if (uSubCmd & USAGE_GSTCTRL_CLOSESESSION)
    408         RTStrmPrintf(pStrm,
    409                  "                              closesession\n" COMMON_OPTION_HELP_ANON
    410                  "                              <  --all | --session-id <ID>\n"
    411                  "                                | --session-name <name or pattern> >\n"
    412                  "\n");
    413     if (uSubCmd & USAGE_GSTCTRL_UPDATEGA)
    414         RTStrmPrintf(pStrm,
    415                  "                              updatega|updateguestadditions|updateadditions\n" COMMON_OPTION_HELP_ANON
    416                  "                              [--source <guest additions .ISO>]\n"
    417                  "                              [--wait-start]\n"
    418                  "                              [-- [<argument1>] ... [<argumentN>]]\n"
    419                  "\n");
    420     if (uSubCmd & USAGE_GSTCTRL_WATCH)
    421         RTStrmPrintf(pStrm,
    422                  "                              watch\n" COMMON_OPTION_HELP_ANON
    423                  "\n");
     410                     "%s guestcontrol %s    <uuid|vmname> [--verbose|-v] [--quiet|-q]\n%s",
     411                     pcszSep1, pcszSep2, uSubCmd == ~0U ? "\n" : "");
     412        if (uSubCmd & USAGE_GSTCTRL_LIST)
     413            RTStrmPrintf(pStrm,
     414                     "                              list <all|sessions|processes|files> [common-opts]\n"
     415                     "\n");
     416        if (uSubCmd & USAGE_GSTCTRL_CLOSEPROCESS)
     417            RTStrmPrintf(pStrm,
     418                     "                              closeprocess [common-options]\n"
     419                     "                              <   --session-id <ID>\n"
     420                     "                                | --session-name <name or pattern>\n"
     421                     "                              <PID1> [PID1 [...]]\n"
     422                     "\n");
     423        if (uSubCmd & USAGE_GSTCTRL_CLOSESESSION)
     424            RTStrmPrintf(pStrm,
     425                     "                              closesession [common-options]\n"
     426                     "                              <  --all | --session-id <ID>\n"
     427                     "                                | --session-name <name or pattern> >\n"
     428                     "\n");
     429        if (uSubCmd & USAGE_GSTCTRL_UPDATEGA)
     430            RTStrmPrintf(pStrm,
     431                     "                              updatega|updateguestadditions|updateadditions\n"
     432                     "                              [--source <guest additions .ISO>]\n"
     433                     "                              [--wait-start] [common-options]\n"
     434                     "                              [-- [<argument1>] ... [<argumentN>]]\n"
     435                     "\n");
     436        if (uSubCmd & USAGE_GSTCTRL_WATCH)
     437            RTStrmPrintf(pStrm,
     438                     "                              watch [common-options]\n"
     439                     "\n");
     440    }
    424441}
    425442
     
    708725 * @param   pCtx                The command context to init.
    709726 * @param   pArg                The handle argument package.
    710  * @param   pCmdDef             The command definition.
    711  */
    712 static RTEXITCODE gctrCmdCtxInit(PGCTLCMDCTX pCtx, HandlerArg *pArg, PCGCTLCMDDEF pCmdDef)
    713 {
    714     Assert(pArg->argc >= 2);
     727 */
     728static RTEXITCODE gctrCmdCtxInit(PGCTLCMDCTX pCtx, HandlerArg *pArg)
     729{
    715730    RT_ZERO(*pCtx);
    716731    pCtx->pArg = pArg;
    717     pCtx->pCmdDef = pCmdDef;
    718     pCtx->pszVmNameOrUuid = pArg->argv[0];
    719732
    720733    /*
     
    756769    switch (ch)
    757770    {
    758         case 'u': /* User name */
    759             if (!(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
     771        case GCTLCMD_COMMON_OPT_USER: /* User name */
     772            if (!pCtx->pCmdDef || !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
    760773                pCtx->strUsername = pValueUnion->psz;
    761774            else
    762                 return errorSyntaxEx(USAGE_GUESTCONTROL, pCtx->pCmdDef->fCmdUsage,
    763                                      "The --username|-u option is not valid with this command");
     775                RTMsgWarning("The --username|-u option is ignored by '%s'", pCtx->pCmdDef->pszName);
    764776            break;
    765777
    766778        case GCTLCMD_COMMON_OPT_PASSWORD: /* Password */
    767             if (!(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
     779            if (!pCtx->pCmdDef || !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
    768780            {
    769781                if (pCtx->strPassword.isNotEmpty())
     
    772784            }
    773785            else
    774                 return errorSyntaxEx(USAGE_GUESTCONTROL, pCtx->pCmdDef->fCmdUsage,
    775                                      "The --password option is not valid with this command");
     786                RTMsgWarning("The --password option is ignored by '%s'", pCtx->pCmdDef->pszName);
    776787            break;
    777788
    778         case 'p': /* Password file */
    779             if (!(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
     789        case GCTLCMD_COMMON_OPT_PASSWORD_FILE: /* Password file */
     790            if (!pCtx->pCmdDef || !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
    780791                rcExit = readPasswordFile(pValueUnion->psz, &pCtx->strPassword);
    781792            else
    782                 return errorSyntaxEx(USAGE_GUESTCONTROL, pCtx->pCmdDef->fCmdUsage,
    783                                      "The --password-file|-p option is not valid with this command");
     793                RTMsgWarning("The --password-file|-p option is ignored by '%s'", pCtx->pCmdDef->pszName);
    784794            break;
    785795
    786         case 'd': /* domain */
    787             if (!(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
     796        case GCTLCMD_COMMON_OPT_DOMAIN: /* domain */
     797            if (!pCtx->pCmdDef || !(pCtx->pCmdDef->fCmdCtx & GCTLCMDCTX_F_SESSION_ANONYMOUS))
    788798                pCtx->strDomain = pValueUnion->psz;
    789799            else
    790                 return errorSyntaxEx(USAGE_GUESTCONTROL, pCtx->pCmdDef->fCmdUsage,
    791                                      "The --domain option is not valid with this command");
     800                RTMsgWarning("The --domain option is ignored by '%s'", pCtx->pCmdDef->pszName);
    792801            break;
    793802
     
    974983 *                          GCTCMDCTX::pGuestSession and GCTLCMDCTX::uSessionID
    975984 *                          will be set.
    976  */
    977 static RTEXITCODE gctlCtxPostArgParsingInit(PGCTLCMDCTX pCtx)
    978 {
     985 * @remarks Can safely be called multiple times, will only do work once.
     986 */
     987static RTEXITCODE gctlCtxPostOptionParsingInit(PGCTLCMDCTX pCtx)
     988{
     989    if (pCtx->fPostOptionParsingInited)
     990        return RTEXITCODE_SUCCESS;
     991
    979992    /*
    980993     * Check that the user name isn't empty when we need it.
     
    10051018    else
    10061019        rcExit = errorSyntaxEx(USAGE_GUESTCONTROL, pCtx->pCmdDef->fCmdUsage, "No user name specified!");
     1020
     1021    pCtx->fPostOptionParsingInited = rcExit == RTEXITCODE_SUCCESS;
    10071022    return rcExit;
    10081023}
     
    12751290 * @returns Command exit code.
    12761291 * @param   pCtx        Guest session context.
     1292 * @param   argc        The argument count.
     1293 * @param   argv        The argument vector for this command.
    12771294 * @param   fRunCmd     Set if it's 'run' clear if 'start'.
    12781295 * @param   fHelp       The help flag for the command.
    12791296 */
    1280 static RTEXITCODE gctlHandleRunCommon(PGCTLCMDCTX pCtx, bool fRunCmd, uint32_t fHelp)
     1297static RTEXITCODE gctlHandleRunCommon(PGCTLCMDCTX pCtx, int argc, char **argv, bool fRunCmd, uint32_t fHelp)
    12811298{
    12821299    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    13001317        GCTLCMD_COMMON_OPTION_DEFS()
    13011318        { "--putenv",                       'E',                                      RTGETOPT_REQ_STRING  },
    1302         { "--executable",                   'e',                                      RTGETOPT_REQ_STRING  },
     1319        { "--exe",                          'e',                                      RTGETOPT_REQ_STRING  },
    13031320        { "--timeout",                      't',                                      RTGETOPT_REQ_UINT32  },
    13041321        { "--unquoted-args",                'u',                                      RTGETOPT_REQ_NOTHING },
    13051322        { "--ignore-operhaned-processes",   kGstCtrlRunOpt_IgnoreOrphanedProcesses,   RTGETOPT_REQ_NOTHING },
    13061323        { "--no-profile",                   kGstCtrlRunOpt_NoProfile,                 RTGETOPT_REQ_NOTHING },
     1324        /* run only: 6 - options */
    13071325        { "--dos2unix",                     kGstCtrlRunOpt_Dos2Unix,                  RTGETOPT_REQ_NOTHING },
    13081326        { "--unix2dos",                     kGstCtrlRunOpt_Unix2Dos,                  RTGETOPT_REQ_NOTHING },
     
    13131331    };
    13141332
     1333    /** @todo stdin handling.   */
     1334
    13151335    int                     ch;
    13161336    RTGETOPTUNION           ValueUnion;
    13171337    RTGETOPTSTATE           GetState;
    1318     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    1319                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     1338    size_t                  cOptions =
     1339    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions) - (fRunCmd ? 0 : 6),
     1340                 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    13201341
    13211342    com::SafeArray<ProcessCreateFlag_T>     aCreateFlags;
     
    13751396                    break;
    13761397
     1398                /* run only options: */
    13771399                case kGstCtrlRunOpt_Dos2Unix:
     1400                    Assert(fRunCmd);
    13781401                    enmStdErrTransform = enmStdOutTransform = kStreamTransform_Dos2Unix;
    13791402                    break;
    13801403                case kGstCtrlRunOpt_Unix2Dos:
     1404                    Assert(fRunCmd);
    13811405                    enmStdErrTransform = enmStdOutTransform = kStreamTransform_Unix2Dos;
    13821406                    break;
    13831407
    13841408                case kGstCtrlRunOpt_WaitForStdOut:
    1385                     if (!fRunCmd)
    1386                         return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stdout");
     1409                    Assert(fRunCmd);
    13871410                    fWaitForStdOut = true;
    13881411                    break;
    13891412                case kGstCtrlRunOpt_NoWaitForStdOut:
    1390                     if (!fRunCmd)
    1391                         return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --no-wait-for-stdout");
     1413                    Assert(fRunCmd);
    13921414                    fWaitForStdOut = false;
    13931415                    break;
    13941416
    13951417                case kGstCtrlRunOpt_WaitForStdErr:
    1396                     if (!fRunCmd)
    1397                         return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stderr");
     1418                    Assert(fRunCmd);
    13981419                    fWaitForStdErr = true;
    13991420                    break;
    14001421                case kGstCtrlRunOpt_NoWaitForStdErr:
    1401                     if (!fRunCmd)
    1402                         return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stderr");
     1422                    Assert(fRunCmd);
    14031423                    fWaitForStdErr = false;
    14041424                    break;
     
    14541474    }
    14551475
    1456     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     1476    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    14571477    if (rcExit != RTEXITCODE_SUCCESS)
    14581478        return rcExit;
     
    16901710
    16911711
    1692 static DECLCALLBACK(RTEXITCODE) gctlHandleRun(PGCTLCMDCTX pCtx)
    1693 {
    1694     return gctlHandleRunCommon(pCtx, true /*fRunCmd*/, USAGE_GSTCTRL_RUN);
    1695 }
    1696 
    1697 
    1698 static DECLCALLBACK(RTEXITCODE) gctlHandleStart(PGCTLCMDCTX pCtx)
    1699 {
    1700     return gctlHandleRunCommon(pCtx, false /*fRunCmd*/, USAGE_GSTCTRL_START);
     1712static DECLCALLBACK(RTEXITCODE) gctlHandleRun(PGCTLCMDCTX pCtx, int argc, char **argv)
     1713{
     1714    return gctlHandleRunCommon(pCtx, argc, argv, true /*fRunCmd*/, USAGE_GSTCTRL_RUN);
     1715}
     1716
     1717
     1718static DECLCALLBACK(RTEXITCODE) gctlHandleStart(PGCTLCMDCTX pCtx, int argc, char **argv)
     1719{
     1720    return gctlHandleRunCommon(pCtx, argc, argv, false /*fRunCmd*/, USAGE_GSTCTRL_START);
    17011721}
    17021722
     
    25832603}
    25842604
    2585 static RTEXITCODE gctlHandleCopy(PGCTLCMDCTX pCtx, bool fHostToGuest)
     2605static RTEXITCODE gctlHandleCopy(PGCTLCMDCTX pCtx, int argc, char **argv, bool fHostToGuest)
    25862606{
    25872607    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    26012621     * does in here.
    26022622     */
     2623    enum GETOPTDEF_COPY
     2624    {
     2625        GETOPTDEF_COPY_DRYRUN = 1000,
     2626        GETOPTDEF_COPY_FOLLOW,
     2627        GETOPTDEF_COPY_TARGETDIR
     2628    };
    26032629    static const RTGETOPTDEF s_aOptions[] =
    26042630    {
     
    26132639    RTGETOPTUNION ValueUnion;
    26142640    RTGETOPTSTATE GetState;
    2615     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    2616                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2641    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    26172642
    26182643    Utf8Str strSource;
    2619     Utf8Str strDest;
     2644    const char *pszDst = NULL;
    26202645    uint32_t fFlags = CopyFileFlag_None;
    26212646    bool fCopyRecursive = false;
     
    26262651
    26272652    int vrc = VINF_SUCCESS;
    2628     while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     2653    while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
    26292654    {
    26302655        /* For options that require an argument, ValueUnion has received the value. */
     
    26462671
    26472672            case GETOPTDEF_COPY_TARGETDIR:
    2648                 strDest = ValueUnion.psz;
     2673                pszDst = ValueUnion.psz;
    26492674                break;
    26502675
    26512676            case VINF_GETOPT_NOT_OPTION:
    2652             {
    26532677                /* Last argument and no destination specified with
    26542678                 * --target-directory yet? Then use the current
    26552679                 * (= last) argument as destination. */
    26562680                if (   pCtx->pArg->argc == GetState.iNext
    2657                     && strDest.isEmpty())
    2658                 {
    2659                     strDest = ValueUnion.psz;
    2660                 }
     2681                    && pszDst == NULL)
     2682                    pszDst = ValueUnion.psz;
    26612683                else
    26622684                {
    2663                     /* Save the source directory. */
    2664                     vecSources.push_back(SOURCEFILEENTRY(ValueUnion.psz));
     2685                    try
     2686                    {   /* Save the source directory. */
     2687                        vecSources.push_back(SOURCEFILEENTRY(ValueUnion.psz));
     2688                    }
     2689                    catch (std::bad_alloc &)
     2690                    {
     2691                        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory");
     2692                    }
    26652693                }
    26662694                break;
    2667             }
    26682695
    26692696            default:
     
    26732700
    26742701    if (!vecSources.size())
    2675         return errorSyntaxEx(USAGE_GUESTCONTROL, uUsage,
    2676                              "No source(s) specified!");
    2677 
    2678     if (strDest.isEmpty())
    2679         return errorSyntaxEx(USAGE_GUESTCONTROL, uUsage,
    2680                              "No destination specified!");
    2681 
    2682     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     2702        return errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, "No source(s) specified!");
     2703
     2704    if (pszDst == NULL)
     2705        return errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, "No destination specified!");
     2706
     2707    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    26832708    if (rcExit != RTEXITCODE_SUCCESS)
    26842709        return rcExit;
     
    27102735    }
    27112736
    2712     /* If the destination is a path, (try to) create it. */
    2713     const char *pszDest = strDest.c_str();
    27142737/** @todo r=bird: RTPathFilename and RTPathStripFilename won't work
    27152738 * correctly on non-windows hosts when the guest is from the DOS world (Windows,
     
    27232746 * @bugref{6344}
    27242747 */
    2725     if (!RTPathFilename(pszDest))
    2726     {
    2727         vrc = gctlCopyDirCreate(pContext, pszDest);
     2748    if (!RTPathFilename(pszDst))
     2749    {
     2750        vrc = gctlCopyDirCreate(pContext, pszDst);
    27282751    }
    27292752    else
     
    27322755         * the actual file name and make sure the appropriate
    27332756         * directories get created. */
    2734         char *pszDestDir = RTStrDup(pszDest);
    2735         AssertPtr(pszDestDir);
    2736         RTPathStripFilename(pszDestDir);
    2737         vrc = gctlCopyDirCreate(pContext, pszDestDir);
    2738         RTStrFree(pszDestDir);
     2757        char *pszDstDir = RTStrDup(pszDst);
     2758        AssertPtr(pszDstDir);
     2759        RTPathStripFilename(pszDstDir);
     2760        vrc = gctlCopyDirCreate(pContext, pszDstDir);
     2761        RTStrFree(pszDstDir);
    27392762    }
    27402763
     
    28002823                {
    28012824                    /* Single file. */
    2802                     char *pszDestFile;
    2803                     vrc = gctlCopyTranslatePath(pszSourceRoot, pszSource,
    2804                                                 strDest.c_str(), &pszDestFile);
     2825                    char *pszDstFile;
     2826                    vrc = gctlCopyTranslatePath(pszSourceRoot, pszSource, pszDst, &pszDstFile);
    28052827                    if (RT_SUCCESS(vrc))
    28062828                    {
    2807                         vrc = gctlCopyFileToDest(pContext, pszSource,
    2808                                                  pszDestFile, 0 /* Flags */);
    2809                         RTStrFree(pszDestFile);
     2829                        vrc = gctlCopyFileToDest(pContext, pszSource, pszDstFile, 0 /* Flags */);
     2830                        RTStrFree(pszDstFile);
    28102831                    }
    28112832                    else
    2812                         RTMsgError("Unable to translate path for \"%s\", rc=%Rrc\n",
    2813                                    pszSource, vrc);
     2833                        RTMsgError("Unable to translate path for \"%s\", rc=%Rrc\n", pszSource, vrc);
    28142834                }
    28152835                else
    28162836                {
    28172837                    /* Directory (with filter?). */
    2818                     vrc = gctlCopyDirToDest(pContext, pszSource, pszFilter,
    2819                                             strDest.c_str(), fFlags);
     2838                    vrc = gctlCopyDirToDest(pContext, pszSource, pszFilter, pszDst, fFlags);
    28202839                }
    28212840            }
     
    28482867}
    28492868
    2850 static DECLCALLBACK(RTEXITCODE) gctlHandleCopyFrom(PGCTLCMDCTX pCtx)
    2851 {
    2852     return gctlHandleCopy(pCtx, false /* Guest to host */);
    2853 }
    2854 
    2855 static DECLCALLBACK(RTEXITCODE) gctlHandleCopyTo(PGCTLCMDCTX pCtx)
    2856 {
    2857     return gctlHandleCopy(pCtx, true /* Host to guest */);
    2858 }
    2859 
    2860 static DECLCALLBACK(RTEXITCODE) handleCtrtMkDir(PGCTLCMDCTX pCtx)
     2869static DECLCALLBACK(RTEXITCODE) gctlHandleCopyFrom(PGCTLCMDCTX pCtx, int argc, char **argv)
     2870{
     2871    return gctlHandleCopy(pCtx, argc, argv, false /* Guest to host */);
     2872}
     2873
     2874static DECLCALLBACK(RTEXITCODE) gctlHandleCopyTo(PGCTLCMDCTX pCtx, int argc, char **argv)
     2875{
     2876    return gctlHandleCopy(pCtx, argc, argv, true /* Host to guest */);
     2877}
     2878
     2879static DECLCALLBACK(RTEXITCODE) handleCtrtMkDir(PGCTLCMDCTX pCtx, int argc, char **argv)
    28612880{
    28622881    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    28722891    RTGETOPTUNION ValueUnion;
    28732892    RTGETOPTSTATE GetState;
    2874     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2,
    2875                  RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2893    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    28762894
    28772895    SafeArray<DirectoryCreateFlag_T> dirCreateFlags;
    2878     uint32_t fDirMode = 0; /* Default mode. */
    2879     DESTDIRMAP mapDirs;
    2880 
    2881     while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     2896    uint32_t    fDirMode     = 0; /* Default mode. */
     2897    uint32_t    cDirsCreated = 0;
     2898    RTEXITCODE  rcExit       = RTEXITCODE_SUCCESS;
     2899
     2900    while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
    28822901    {
    28832902        /* For options that require an argument, ValueUnion has received the value. */
     
    28952914
    28962915            case VINF_GETOPT_NOT_OPTION:
    2897                 mapDirs[ValueUnion.psz]; /* Add destination directory to map. */
     2916                if (cDirsCreated == 0)
     2917                {
     2918                    /*
     2919                     * First non-option - no more options now.
     2920                     */
     2921                    rcExit = gctlCtxPostOptionParsingInit(pCtx);
     2922                    if (rcExit != RTEXITCODE_SUCCESS)
     2923                        return rcExit;
     2924                    if (pCtx->cVerbose > 1)
     2925                        RTPrintf("Creating %RU32 directories...\n", argc - GetState.iNext + 1);
     2926                }
     2927                if (g_fGuestCtrlCanceled)
     2928                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "mkdir was interrupted by Ctrl-C (%u left)\n",
     2929                                          argc - GetState.iNext + 1);
     2930
     2931                /*
     2932                 * Create the specified directory.
     2933                 *
     2934                 * On failure we'll change the exit status to failure and
     2935                 * continue with the next directory that needs creating. We do
     2936                 * this because we only create new things, and because this is
     2937                 * how /bin/mkdir works on unix.
     2938                 */
     2939                cDirsCreated++;
     2940                if (pCtx->cVerbose > 1)
     2941                    RTPrintf("Creating directory \"%s\" ...\n", ValueUnion.psz);
     2942                try
     2943                {
     2944                    HRESULT rc;
     2945                    CHECK_ERROR(pCtx->pGuestSession, DirectoryCreate(Bstr(ValueUnion.psz).raw(),
     2946                                                                     fDirMode, ComSafeArrayAsInParam(dirCreateFlags)));
     2947                    if (FAILED(rc))
     2948                        rcExit = RTEXITCODE_FAILURE;
     2949                }
     2950                catch (std::bad_alloc &)
     2951                {
     2952                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory\n");
     2953                }
    28982954                break;
    28992955
     
    29032959    }
    29042960
    2905     size_t cDirs = mapDirs.size();
    2906     if (!cDirs)
    2907         return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MKDIR,
    2908                              "No directory to create specified!");
    2909 
    2910     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
    2911     if (rcExit != RTEXITCODE_SUCCESS)
    2912         return rcExit;
    2913 
    2914     /*
    2915      * Create the directories.
    2916      */
    2917     HRESULT rc = S_OK;
    2918     if (pCtx->cVerbose > 1)
    2919         RTPrintf("Creating %RU32 directories ...\n", cDirs);
    2920 
    2921     DESTDIRMAPITER it = mapDirs.begin();
    2922     while (   (it != mapDirs.end())
    2923            && !g_fGuestCtrlCanceled)
    2924     {
    2925         if (pCtx->cVerbose > 1)
    2926             RTPrintf("Creating directory \"%s\" ...\n", it->first.c_str());
    2927 
    2928         CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryCreate(Bstr(it->first).raw(),
    2929                                                fDirMode, ComSafeArrayAsInParam(dirCreateFlags)));
    2930         it++;
    2931     }
    2932 
    2933     return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
    2934 }
    2935 
    2936 static DECLCALLBACK(RTEXITCODE) gctlHandleRmDir(PGCTLCMDCTX pCtx)
     2961    if (!cDirsCreated)
     2962        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_MKDIR, "No directory to create specified!");
     2963    return rcExit;
     2964}
     2965
     2966
     2967static DECLCALLBACK(RTEXITCODE) gctlHandleRmDir(PGCTLCMDCTX pCtx, int argc, char **argv)
    29372968{
    29382969    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    29412972    {
    29422973        GCTLCMD_COMMON_OPTION_DEFS()
    2943         { "--recursive",           'R',                             RTGETOPT_REQ_NOTHING }
     2974        { "--recursive",           'R',                             RTGETOPT_REQ_NOTHING },
    29442975    };
    29452976
     
    29472978    RTGETOPTUNION ValueUnion;
    29482979    RTGETOPTSTATE GetState;
    2949     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    2950                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    2951 
    2952     bool fRecursive = false;
    2953     DESTDIRMAP mapDirs;
    2954 
    2955     while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     2980    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     2981
     2982    bool        fRecursive  = false;
     2983    uint32_t    cDirRemoved = 0;
     2984    RTEXITCODE  rcExit      = RTEXITCODE_SUCCESS;
     2985
     2986    while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
    29562987    {
    29572988        /* For options that require an argument, ValueUnion has received the value. */
     
    29652996
    29662997            case VINF_GETOPT_NOT_OPTION:
    2967                 mapDirs[ValueUnion.psz]; /* Add destination directory to map. */
     2998            {
     2999                if (cDirRemoved == 0)
     3000                {
     3001                    /*
     3002                     * First non-option - no more options now.
     3003                     */
     3004                    rcExit = gctlCtxPostOptionParsingInit(pCtx);
     3005                    if (rcExit != RTEXITCODE_SUCCESS)
     3006                        return rcExit;
     3007                    if (pCtx->cVerbose > 1)
     3008                        RTPrintf("Removing %RU32 directorie%ss...\n", argc - GetState.iNext + 1, fRecursive ? "trees" : "");
     3009                }
     3010                if (g_fGuestCtrlCanceled)
     3011                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "rmdir was interrupted by Ctrl-C (%u left)\n",
     3012                                          argc - GetState.iNext + 1);
     3013
     3014                cDirRemoved++;
     3015                HRESULT rc;
     3016                if (!fRecursive)
     3017                {
     3018                    /*
     3019                     * Remove exactly one directory.
     3020                     */
     3021                    if (pCtx->cVerbose > 1)
     3022                        RTPrintf("Removing directory \"%s\" ...\n", ValueUnion.psz);
     3023                    try
     3024                    {
     3025                        CHECK_ERROR(pCtx->pGuestSession, DirectoryRemove(Bstr(ValueUnion.psz).raw()));
     3026                    }
     3027                    catch (std::bad_alloc &)
     3028                    {
     3029                        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory\n");
     3030                    }
     3031                }
     3032                else
     3033                {
     3034                    /*
     3035                     * Remove the directory and anything under it, that means files
     3036                     * and everything.  This is in the tradition of the Windows NT
     3037                     * CMD.EXE "rmdir /s" operation, a tradition which jpsoft's TCC
     3038                     * strongly warns against (and half-ways questions the sense of).
     3039                     */
     3040                    if (pCtx->cVerbose > 1)
     3041                        RTPrintf("Recursively removing directory \"%s\" ...\n", ValueUnion.psz);
     3042                    try
     3043                    {
     3044                        /** @todo Make flags configurable. */
     3045                        com::SafeArray<DirectoryRemoveRecFlag_T> aRemRecFlags;
     3046                        aRemRecFlags.push_back(DirectoryRemoveRecFlag_ContentAndDir);
     3047
     3048                        ComPtr<IProgress> ptrProgress;
     3049                        CHECK_ERROR(pCtx->pGuestSession, DirectoryRemoveRecursive(Bstr(ValueUnion.psz).raw(),
     3050                                                                                  ComSafeArrayAsInParam(aRemRecFlags),
     3051                                                                                  ptrProgress.asOutParam()));
     3052                        if (SUCCEEDED(rc))
     3053                        {
     3054                            if (pCtx->cVerbose > 1)
     3055                                rc = showProgress(ptrProgress);
     3056                            else
     3057                                rc = ptrProgress->WaitForCompletion(-1 /* indefinitely */);
     3058                            if (SUCCEEDED(rc))
     3059                                CHECK_PROGRESS_ERROR(ptrProgress, ("Directory deletion failed"));
     3060                            ptrProgress.setNull();
     3061                        }
     3062                    }
     3063                    catch (std::bad_alloc &)
     3064                    {
     3065                        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory during recursive rmdir\n");
     3066                    }
     3067                }
     3068
     3069                /*
     3070                 * This command returns immediately on failure since it's destructive in nature.
     3071                 */
     3072                if (FAILED(rc))
     3073                    return RTEXITCODE_FAILURE;
    29683074                break;
     3075            }
    29693076
    29703077            default:
     
    29733080    }
    29743081
    2975     size_t cDirs = mapDirs.size();
    2976     if (!cDirs)
    2977         return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RMDIR,
    2978                              "No directory to remove specified!");
    2979 
    2980     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
    2981     if (rcExit != RTEXITCODE_SUCCESS)
    2982         return rcExit;
    2983 
    2984     /*
    2985      * Remove the directories.
    2986      */
    2987     HRESULT rc = S_OK;
    2988     if (pCtx->cVerbose > 1)
    2989         RTPrintf("Removing %RU32 directories ...\n", cDirs);
    2990 
    2991     DESTDIRMAPITER it = mapDirs.begin();
    2992     while (   (it != mapDirs.end())
    2993            && !g_fGuestCtrlCanceled)
    2994     {
    2995         if (pCtx->cVerbose > 1)
    2996             RTPrintf("%s directory \"%s\" ...\n",
    2997                      fRecursive ? "Recursively removing" : "Removing",
    2998                      it->first.c_str());
    2999         try
    3000         {
    3001             if (fRecursive)
    3002             {
    3003                 com::SafeArray<DirectoryRemoveRecFlag_T> aRemRecFlags;
    3004                 /** @todo Make flags configurable. */
    3005                 aRemRecFlags.push_back(DirectoryRemoveRecFlag_ContentAndDir);
    3006 
    3007                 ComPtr<IProgress> pProgress;
    3008                 CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRemoveRecursive(Bstr(it->first).raw(),
    3009                                                                                 ComSafeArrayAsInParam(aRemRecFlags),
    3010                                                                                 pProgress.asOutParam()));
    3011                 if (pCtx->cVerbose > 1)
    3012                     rc = showProgress(pProgress);
    3013                 else
    3014                     rc = pProgress->WaitForCompletion(-1 /* No timeout */);
    3015                 if (SUCCEEDED(rc))
    3016                     CHECK_PROGRESS_ERROR(pProgress, ("Directory deletion failed"));
    3017 
    3018                 pProgress.setNull();
    3019             }
    3020             else
    3021                 CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRemove(Bstr(it->first).raw()));
    3022         }
    3023         catch (std::bad_alloc)
    3024         {
    3025             rc = E_OUTOFMEMORY;
    3026             break;
    3027         }
    3028 
    3029         it++;
    3030     }
    3031 
    3032     return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
    3033 }
    3034 
    3035 static DECLCALLBACK(RTEXITCODE) gctlHandleRm(PGCTLCMDCTX pCtx)
     3082    if (!cDirRemoved)
     3083        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RMDIR, "No directory to remove specified!");
     3084    return rcExit;
     3085}
     3086
     3087static DECLCALLBACK(RTEXITCODE) gctlHandleRm(PGCTLCMDCTX pCtx, int argc, char **argv)
    30363088{
    30373089    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    30403092    {
    30413093        GCTLCMD_COMMON_OPTION_DEFS()
     3094        { "--force",                         'f',                                       RTGETOPT_REQ_NOTHING, },
    30423095    };
    30433096
     
    30453098    RTGETOPTUNION ValueUnion;
    30463099    RTGETOPTSTATE GetState;
    3047     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    3048                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    3049 
    3050     DESTDIRMAP mapDirs;
    3051 
    3052     while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     3100    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3101
     3102    uint32_t    cFilesDeleted   = 0;
     3103    RTEXITCODE  rcExit          = RTEXITCODE_SUCCESS;
     3104    bool        fForce         = true;
     3105
     3106    while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
    30533107    {
    30543108        /* For options that require an argument, ValueUnion has received the value. */
     
    30583112
    30593113            case VINF_GETOPT_NOT_OPTION:
    3060                 mapDirs[ValueUnion.psz]; /* Add destination directory to map. */
     3114                if (cFilesDeleted == 0)
     3115                {
     3116                    /*
     3117                     * First non-option - no more options now.
     3118                     */
     3119                    rcExit = gctlCtxPostOptionParsingInit(pCtx);
     3120                    if (rcExit != RTEXITCODE_SUCCESS)
     3121                        return rcExit;
     3122                    if (pCtx->cVerbose > 1)
     3123                        RTPrintf("Removing %RU32 file(s)...\n", argc - GetState.iNext + 1);
     3124                }
     3125                if (g_fGuestCtrlCanceled)
     3126                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "rm was interrupted by Ctrl-C (%u left)\n",
     3127                                          argc - GetState.iNext + 1);
     3128
     3129                /*
     3130                 * Remove the specified file.
     3131                 *
     3132                 * On failure we will by default stop, however, the force option will
     3133                 * by unix traditions force us to ignore errors and continue.
     3134                 */
     3135                cFilesDeleted++;
     3136                if (pCtx->cVerbose > 1)
     3137                    RTPrintf("Removing file \"%s\" ...\n", ValueUnion.psz);
     3138                try
     3139                {
     3140                    /** @todo How does IGuestSession::FileRemove work with read-only files? Do we
     3141                     *        need to do some chmod or whatever to better emulate the --force flag? */
     3142                    HRESULT rc;
     3143                    CHECK_ERROR(pCtx->pGuestSession, FileRemove(Bstr(ValueUnion.psz).raw()));
     3144                    if (FAILED(rc) && !fForce)
     3145                        return RTEXITCODE_FAILURE;
     3146                }
     3147                catch (std::bad_alloc &)
     3148                {
     3149                    return RTMsgErrorExit(RTEXITCODE_FAILURE, "Out of memory\n");
     3150                }
    30613151                break;
    30623152
     
    30663156    }
    30673157
    3068     size_t cFiles = mapDirs.size();
    3069     if (!cFiles)
     3158    if (!cFilesDeleted && !fForce)
    30703159        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RM, "No file to remove specified!");
    3071 
    3072     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
    3073     if (rcExit != RTEXITCODE_SUCCESS)
    3074         return rcExit;
    3075 
    3076     /*
    3077      * Create the directories.
    3078      */
    3079     HRESULT rc = S_OK;
    3080     if (pCtx->cVerbose > 1)
    3081         RTPrintf("Removing %RU32 file(s) ...\n", cFiles);
    3082 
    3083     DESTDIRMAPITER it = mapDirs.begin();
    3084     while (   (it != mapDirs.end())
    3085            && !g_fGuestCtrlCanceled)
    3086     {
    3087         if (pCtx->cVerbose > 1)
    3088             RTPrintf("Removing file \"%s\" ...\n", it->first.c_str());
    3089 
    3090         CHECK_ERROR_BREAK(pCtx->pGuestSession, FileRemove(Bstr(it->first).raw()));
    3091 
    3092         it++;
    3093     }
    3094 
    3095     return FAILED(rc) ? RTEXITCODE_FAILURE : RTEXITCODE_SUCCESS;
    3096 }
    3097 
    3098 static DECLCALLBACK(RTEXITCODE) gctlHandleMv(PGCTLCMDCTX pCtx)
     3160    return rcExit;
     3161}
     3162
     3163static DECLCALLBACK(RTEXITCODE) gctlHandleMv(PGCTLCMDCTX pCtx, int argc, char **argv)
    30993164{
    31003165    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    31083173    RTGETOPTUNION ValueUnion;
    31093174    RTGETOPTSTATE GetState;
    3110     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    3111                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3175    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    31123176
    31133177    int vrc = VINF_SUCCESS;
     
    31153179    bool fDryrun = false;
    31163180    std::vector< Utf8Str > vecSources;
    3117     Utf8Str strDest;
     3181    const char *pszDst = NULL;
    31183182    com::SafeArray<PathRenameFlag_T> aRenameFlags;
    31193183
     
    31363200                case VINF_GETOPT_NOT_OPTION:
    31373201                    vecSources.push_back(Utf8Str(ValueUnion.psz));
    3138                     strDest = ValueUnion.psz;
     3202                    pszDst = ValueUnion.psz;
    31393203                    break;
    31403204
     
    31603224                             "No destination specified!");
    31613225
    3162     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     3226    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    31633227    if (rcExit != RTEXITCODE_SUCCESS)
    31643228        return rcExit;
     
    31733237    {
    31743238        ComPtr<IGuestFsObjInfo> pFsObjInfo;
    3175         rc = pCtx->pGuestSession->DirectoryQueryInfo(Bstr(strDest).raw(), pFsObjInfo.asOutParam());
     3239        rc = pCtx->pGuestSession->DirectoryQueryInfo(Bstr(pszDst).raw(), pFsObjInfo.asOutParam());
    31763240        if (FAILED(rc))
    31773241            return RTMsgErrorExit(RTEXITCODE_FAILURE, "Destination must be a directory when specifying multiple sources\n");
     
    31903254        bool fSourceIsDirectory = false;
    31913255        Utf8Str strCurSource = (*it);
    3192         Utf8Str strCurDest = strDest;
    31933256
    31943257        /** @todo Slooooow, but works for now. */
     3258        /** @todo r=bird: Need an interface for querying info on a file system
     3259         *        object, not just files or directories exclusively.  You also may
     3260         *        want to take symbolic links into account somewhere along the line,
     3261         *        though preferrably on the guest end of things... */
    31953262        ComPtr<IGuestFsObjInfo> pFsObjInfo;
    31963263        rc = pCtx->pGuestSession->FileQueryInfo(Bstr(strCurSource).raw(), pFsObjInfo.asOutParam());
     
    32123279            RTPrintf("Renaming %s \"%s\" to \"%s\" ...\n",
    32133280                     fSourceIsDirectory ? "directory" : "file",
    3214                      strCurSource.c_str(), strCurDest.c_str());
     3281                     strCurSource.c_str(), pszDst);
    32153282
    32163283        if (!fDryrun)
     
    32193286            {
    32203287                CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRename(Bstr(strCurSource).raw(),
    3221                                                                        Bstr(strCurDest).raw(),
     3288                                                                       Bstr(pszDst).raw(),
    32223289                                                                       ComSafeArrayAsInParam(aRenameFlags)));
    32233290
    32243291                /* Break here, since it makes no sense to rename mroe than one source to
    32253292                 * the same directory. */
     3293/** @todo r=bird: You are being kind of windowsy (or just DOSish) about the 'sense' part here,
     3294 * while being totaly buggy about the behavior. 'VBoxGuest guestcontrol ren dir1 dir2 dstdir' will
     3295 * stop after 'dir1' and SILENTLY ignore dir2.  If you tried this on Windows, you'd see an error
     3296 * being displayed.  If you 'man mv' on a nearby unixy system, you'd see that they've made perfect
     3297 * sense out of any situation with more than one source. */
    32263298                it = vecSources.end();
    32273299                break;
     
    32293301            else
    32303302                CHECK_ERROR_BREAK(pCtx->pGuestSession, FileRename(Bstr(strCurSource).raw(),
    3231                                                                   Bstr(strCurDest).raw(),
     3303                                                                  Bstr(pszDst).raw(),
    32323304                                                                  ComSafeArrayAsInParam(aRenameFlags)));
    32333305        }
     
    32453317}
    32463318
    3247 static DECLCALLBACK(RTEXITCODE) gctlHandleMkTemp(PGCTLCMDCTX pCtx)
     3319static DECLCALLBACK(RTEXITCODE) gctlHandleMkTemp(PGCTLCMDCTX pCtx, int argc, char **argv)
    32483320{
    32493321    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    32613333    RTGETOPTUNION ValueUnion;
    32623334    RTGETOPTSTATE GetState;
    3263     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    3264                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3335    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    32653336
    32663337    Utf8Str strTemplate;
     
    33183389                             "Creating temporary files is currently not supported!");
    33193390
    3320     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     3391    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    33213392    if (rcExit != RTEXITCODE_SUCCESS)
    33223393        return rcExit;
     
    33633434}
    33643435
    3365 static DECLCALLBACK(RTEXITCODE) gctlHandleStat(PGCTLCMDCTX pCtx)
     3436static DECLCALLBACK(RTEXITCODE) gctlHandleStat(PGCTLCMDCTX pCtx, int argc, char **argv)
    33663437{
    33673438    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    33793450    RTGETOPTUNION ValueUnion;
    33803451    RTGETOPTSTATE GetState;
    3381     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    3382                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3452    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    33833453
    33843454    DESTDIRMAP mapObjs;
    33853455
    3386     while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     3456    while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
    33873457    {
    33883458        /* For options that require an argument, ValueUnion has received the value. */
     
    34123482                             "No element(s) to check specified!");
    34133483
    3414     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     3484    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    34153485    if (rcExit != RTEXITCODE_SUCCESS)
    34163486        return rcExit;
     
    34733543}
    34743544
    3475 static DECLCALLBACK(RTEXITCODE) gctlHandleUpdateAdditions(PGCTLCMDCTX pCtx)
     3545static DECLCALLBACK(RTEXITCODE) gctlHandleUpdateAdditions(PGCTLCMDCTX pCtx, int argc, char **argv)
    34763546{
    34773547    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    34953565    RTGETOPTUNION ValueUnion;
    34963566    RTGETOPTSTATE GetState;
    3497     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    3498                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3567    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    34993568
    35003569    int vrc = VINF_SUCCESS;
     
    35583627
    35593628
    3560         RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     3629        RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    35613630        if (rcExit != RTEXITCODE_SUCCESS)
    35623631            return rcExit;
     
    36003669}
    36013670
    3602 static DECLCALLBACK(RTEXITCODE) gctlHandleList(PGCTLCMDCTX pCtx)
     3671static DECLCALLBACK(RTEXITCODE) gctlHandleList(PGCTLCMDCTX pCtx, int argc, char **argv)
    36033672{
    36043673    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    36123681    RTGETOPTUNION ValueUnion;
    36133682    RTGETOPTSTATE GetState;
    3614     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    3615                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3683    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    36163684
    36173685    bool fSeenListArg   = false;
     
    36553723    Assert(fListAll || fListSessions);
    36563724
    3657     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     3725    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    36583726    if (rcExit != RTEXITCODE_SUCCESS)
    36593727        return rcExit;
     
    37703838}
    37713839
    3772 static DECLCALLBACK(RTEXITCODE) gctlHandleCloseProcess(PGCTLCMDCTX pCtx)
     3840static DECLCALLBACK(RTEXITCODE) gctlHandleCloseProcess(PGCTLCMDCTX pCtx, int argc, char **argv)
    37733841{
    37743842    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    37843852    RTGETOPTUNION ValueUnion;
    37853853    RTGETOPTSTATE GetState;
    3786     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    3787                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     3854    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    37883855
    37893856    std::vector < uint32_t > vecPID;
     
    38543921                             "Either session ID or name (pattern) must be specified");
    38553922
    3856     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     3923    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    38573924    if (rcExit != RTEXITCODE_SUCCESS)
    38583925        return rcExit;
     
    39554022
    39564023
    3957 static DECLCALLBACK(RTEXITCODE) gctlHandleCloseSession(PGCTLCMDCTX pCtx)
     4024static DECLCALLBACK(RTEXITCODE) gctlHandleCloseSession(PGCTLCMDCTX pCtx, int argc, char **argv)
    39584025{
    39594026    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    39744041    RTGETOPTUNION ValueUnion;
    39754042    RTGETOPTSTATE GetState;
    3976     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    3977                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     4043    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    39784044
    39794045    ULONG ulSessionID = UINT32_MAX;
    39804046    Utf8Str strSessionName;
    39814047
    3982     while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     4048    while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
    39834049    {
    39844050        /* For options that require an argument, ValueUnion has received the value. */
     
    40174083                             "Either session ID or name (pattern) must be specified");
    40184084
    4019     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     4085    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    40204086    if (rcExit != RTEXITCODE_SUCCESS)
    40214087        return rcExit;
     
    40814147
    40824148
    4083 static DECLCALLBACK(RTEXITCODE) gctlHandleWatch(PGCTLCMDCTX pCtx)
     4149static DECLCALLBACK(RTEXITCODE) gctlHandleWatch(PGCTLCMDCTX pCtx, int argc, char **argv)
    40844150{
    40854151    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    40964162    RTGETOPTUNION ValueUnion;
    40974163    RTGETOPTSTATE GetState;
    4098     RTGetOptInit(&GetState, pCtx->pArg->argc, pCtx->pArg->argv, s_aOptions, RT_ELEMENTS(s_aOptions),
    4099                  2, RTGETOPTINIT_FLAGS_OPTS_FIRST);
    4100 
    4101     while ((ch = RTGetOpt(&GetState, &ValueUnion)))
     4164    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, RTGETOPTINIT_FLAGS_OPTS_FIRST);
     4165
     4166    while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
    41024167    {
    41034168        /* For options that require an argument, ValueUnion has received the value. */
     
    41154180    /** @todo Specify a --timeout for waiting only for a certain amount of time? */
    41164181
    4117     RTEXITCODE rcExit = gctlCtxPostArgParsingInit(pCtx);
     4182    RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx);
    41184183    if (rcExit != RTEXITCODE_SUCCESS)
    41194184        return rcExit;
     
    42354300
    42364301    /*
    4237      * Lookup the command and invoke the handler.
     4302     * VBoxManage guestcontrol [common-options] <VM> [common-options] <sub-command> ...
    42384303     *
    4239      * Our Argument expectations:
    4240      *      argv[0] is the VM name.
    4241      *      argv[1] is the guest control command.
     4304     * Parse common options and VM name until we find a sub-command.  Allowing
     4305     * the user  to put the user and password related options before the
     4306     * sub-command makes it easier to edit the command line when doing several
     4307     * operations with the same guest user account.  (Accidentally, it also
     4308     * makes the syntax diagram shorter and easier to read.)
    42424309     */
    4243     if (pArg->argc >= 2)
    4244     {
    4245         /** @todo bird: From a syntax diagram point of view, it's tempting to start
    4246          *        common option parsing here, stop on the first non-option and using
    4247          *        it as the command.  We will of course allow common options after
    4248          *        the command, no problems. */
    4249         const char *pszCmd = pArg->argv[1];
    4250         uint32_t    iCmd;
    4251         for (iCmd = 0; iCmd < RT_ELEMENTS(s_aCmdDefs); iCmd++)
    4252             if (strcmp(s_aCmdDefs[iCmd].pszName, pszCmd) == 0)
    4253             {
    4254                 GCTLCMDCTX CmdCtx;
    4255                 RTEXITCODE rcExit = gctrCmdCtxInit(&CmdCtx, pArg, &s_aCmdDefs[iCmd]);
    4256                 if (rcExit == RTEXITCODE_SUCCESS)
    4257                 {
    4258                     rcExit = s_aCmdDefs[iCmd].pfnHandler(&CmdCtx);
    4259 
    4260                     gctlCtxTerm(&CmdCtx);
    4261                 }
    4262                 return rcExit;
     4310    GCTLCMDCTX CmdCtx;
     4311    RTEXITCODE rcExit = gctrCmdCtxInit(&CmdCtx, pArg);
     4312    if (rcExit == RTEXITCODE_SUCCESS)
     4313    {
     4314        static const RTGETOPTDEF s_CommonOptions[] = { GCTLCMD_COMMON_OPTION_DEFS() };
     4315
     4316        int ch;
     4317        RTGETOPTUNION ValueUnion;
     4318        RTGETOPTSTATE GetState;
     4319        RTGetOptInit(&GetState, pArg->argc, pArg->argv, s_CommonOptions, RT_ELEMENTS(s_CommonOptions), 0, 0 /* No sorting! */);
     4320
     4321        while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0)
     4322        {
     4323            switch (ch)
     4324            {
     4325                GCTLCMD_COMMON_OPTION_CASES(&CmdCtx, ch, &ValueUnion);
     4326
     4327                case VINF_GETOPT_NOT_OPTION:
     4328                    /* First comes the VM name or UUID. */
     4329                    if (!CmdCtx.pszVmNameOrUuid)
     4330                        CmdCtx.pszVmNameOrUuid = ValueUnion.psz;
     4331                    /*
     4332                     * The sub-command is next.  Look it up and invoke it.
     4333                     * Note! Currently no warnings about user/password options (like we'll do later on)
     4334                     *       for GCTLCMDCTX_F_SESSION_ANONYMOUS commands. No reason to be too pedantic.
     4335                     */
     4336                    else
     4337                    {
     4338                        const char *pszCmd = ValueUnion.psz;
     4339                        uint32_t    iCmd;
     4340                        for (iCmd = 0; iCmd < RT_ELEMENTS(s_aCmdDefs); iCmd++)
     4341                            if (strcmp(s_aCmdDefs[iCmd].pszName, pszCmd) == 0)
     4342                            {
     4343                                CmdCtx.pCmdDef = &s_aCmdDefs[iCmd];
     4344
     4345                                rcExit = s_aCmdDefs[iCmd].pfnHandler(&CmdCtx, pArg->argc - GetState.iNext + 1,
     4346                                                                     &pArg->argv[GetState.iNext - 1]);
     4347
     4348                                gctlCtxTerm(&CmdCtx);
     4349                                return rcExit;
     4350                            }
     4351                        return errorSyntax(USAGE_GUESTCONTROL, "Unknown sub-command: '%s'", pszCmd);
     4352                    }
     4353                    break;
     4354
     4355                default:
     4356                    return errorGetOpt(USAGE_GUESTCONTROL, ch, &ValueUnion);
    42634357            }
    4264 
    4265         return errorSyntax(USAGE_GUESTCONTROL, "Unknown sub-command: '%s'", pszCmd);
    4266     }
    4267     return errorSyntax(USAGE_GUESTCONTROL, "Missing sub-command");
     4358        }
     4359        if (CmdCtx.pszVmNameOrUuid)
     4360            rcExit = errorSyntax(USAGE_GUESTCONTROL, "Missing sub-command");
     4361        else
     4362            rcExit = errorSyntax(USAGE_GUESTCONTROL, "Missing VM name and sub-command");
     4363    }
     4364    return rcExit;
    42684365}
    42694366#endif /* !VBOX_ONLY_DOCS */
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