Changeset 55609 in vbox
- Timestamp:
- May 3, 2015 1:00:22 AM (10 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp
r55607 r55609 73 73 /** Common option definitions. */ 74 74 #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 }, 81 81 82 82 /** Handles common options in the typical option parsing switch. */ … … 128 128 * @param pCtx Pointer to command context to use. 129 129 */ 130 DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (struct GCTLCMDCTX *pCtx ));130 DECLR3CALLBACKMEMBER(RTEXITCODE, pfnHandler, (struct GCTLCMDCTX *pCtx, int argc, char **argv)); 131 131 132 132 /** The command usage flags. */ … … 161 161 const char *pszVmNameOrUuid; 162 162 163 /** Whether we've done the post option parsing init already. */ 164 bool fPostOptionParsingInited; 163 165 /** Whether we've locked the VM session. */ 164 166 bool fLockedVmSession; … … 289 291 }; 290 292 291 enum GETOPTDEF_COPY292 {293 GETOPTDEF_COPY_DRYRUN = 1000,294 GETOPTDEF_COPY_FOLLOW,295 GETOPTDEF_COPY_TARGETDIR296 };297 298 299 293 enum kStreamTransform 300 294 { … … 316 310 void usageGuestControl(PRTSTREAM pStrm, const char *pcszSep1, const char *pcszSep2, uint32_t uSubCmd) 317 311 { 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 322 319 /* 0 1 2 3 4 5 6 7 8XXXXXXXXXX */ 323 320 /* 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" : ""); 330 327 if (uSubCmd & USAGE_GSTCTRL_RUN) 331 328 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" 335 333 " [--no-wait-stdout|--wait-stdout]\n" 336 334 " [--no-wait-stderr|--wait-stderr]\n" … … 340 338 if (uSubCmd & USAGE_GSTCTRL_START) 341 339 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" 345 344 " -- <program/arg0> [argument1] ... [argumentN]]\n" 346 "\n"); 347 /* 0 1 2 3 4 5 6 7 8XXXXXXXXXX */ 348 /* 0123456789012345678901234567890123456789012345678901234567890123456789012345678901234567890 */ 345 "\n"); 349 346 if (uSubCmd & USAGE_GSTCTRL_COPYFROM) 350 347 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"); 355 357 if (uSubCmd & USAGE_GSTCTRL_COPYTO) 356 358 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"); 361 368 if (uSubCmd & USAGE_GSTCTRL_MKDIR) 362 369 RTStrmPrintf(pStrm, 363 " mkdir|md|createdir[ectory]\n" COMMON_OPTION_HELP364 " [--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"); 367 374 if (uSubCmd & USAGE_GSTCTRL_RMDIR) 368 375 RTStrmPrintf(pStrm, 369 " rmdir|removedir[ectory]\n" COMMON_OPTION_HELP370 " [--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"); 373 380 if (uSubCmd & USAGE_GSTCTRL_RM) 374 381 RTStrmPrintf(pStrm, 375 " removefile|rm\n" COMMON_OPTION_HELP376 " <guest file> [...]\n"377 "\n");382 " removefile|rm [common-options] [-f|--force]\n" 383 " <guest file> [...]\n" 384 "\n"); 378 385 if (uSubCmd & USAGE_GSTCTRL_MV) 379 386 RTStrmPrintf(pStrm, 380 " mv|move|ren[ame]\n" COMMON_OPTION_HELP381 " <source> [source1 [...]] <dest>\n"382 "\n");387 " mv|move|ren[ame] [common-options]\n" 388 " <source> [source1 [...]] <dest>\n" 389 "\n"); 383 390 if (uSubCmd & USAGE_GSTCTRL_MKTEMP) 384 391 RTStrmPrintf(pStrm, 385 " mktemp|createtemp[orary]\n" COMMON_OPTION_HELP386 " [--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"); 389 396 if (uSubCmd & USAGE_GSTCTRL_STAT) 390 397 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 */ 397 409 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 } 424 441 } 425 442 … … 708 725 * @param pCtx The command context to init. 709 726 * @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 */ 728 static RTEXITCODE gctrCmdCtxInit(PGCTLCMDCTX pCtx, HandlerArg *pArg) 729 { 715 730 RT_ZERO(*pCtx); 716 731 pCtx->pArg = pArg; 717 pCtx->pCmdDef = pCmdDef;718 pCtx->pszVmNameOrUuid = pArg->argv[0];719 732 720 733 /* … … 756 769 switch (ch) 757 770 { 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)) 760 773 pCtx->strUsername = pValueUnion->psz; 761 774 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); 764 776 break; 765 777 766 778 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)) 768 780 { 769 781 if (pCtx->strPassword.isNotEmpty()) … … 772 784 } 773 785 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); 776 787 break; 777 788 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)) 780 791 rcExit = readPasswordFile(pValueUnion->psz, &pCtx->strPassword); 781 792 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); 784 794 break; 785 795 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)) 788 798 pCtx->strDomain = pValueUnion->psz; 789 799 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); 792 801 break; 793 802 … … 974 983 * GCTCMDCTX::pGuestSession and GCTLCMDCTX::uSessionID 975 984 * 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 */ 987 static RTEXITCODE gctlCtxPostOptionParsingInit(PGCTLCMDCTX pCtx) 988 { 989 if (pCtx->fPostOptionParsingInited) 990 return RTEXITCODE_SUCCESS; 991 979 992 /* 980 993 * Check that the user name isn't empty when we need it. … … 1005 1018 else 1006 1019 rcExit = errorSyntaxEx(USAGE_GUESTCONTROL, pCtx->pCmdDef->fCmdUsage, "No user name specified!"); 1020 1021 pCtx->fPostOptionParsingInited = rcExit == RTEXITCODE_SUCCESS; 1007 1022 return rcExit; 1008 1023 } … … 1275 1290 * @returns Command exit code. 1276 1291 * @param pCtx Guest session context. 1292 * @param argc The argument count. 1293 * @param argv The argument vector for this command. 1277 1294 * @param fRunCmd Set if it's 'run' clear if 'start'. 1278 1295 * @param fHelp The help flag for the command. 1279 1296 */ 1280 static RTEXITCODE gctlHandleRunCommon(PGCTLCMDCTX pCtx, bool fRunCmd, uint32_t fHelp)1297 static RTEXITCODE gctlHandleRunCommon(PGCTLCMDCTX pCtx, int argc, char **argv, bool fRunCmd, uint32_t fHelp) 1281 1298 { 1282 1299 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 1300 1317 GCTLCMD_COMMON_OPTION_DEFS() 1301 1318 { "--putenv", 'E', RTGETOPT_REQ_STRING }, 1302 { "--exe cutable",'e', RTGETOPT_REQ_STRING },1319 { "--exe", 'e', RTGETOPT_REQ_STRING }, 1303 1320 { "--timeout", 't', RTGETOPT_REQ_UINT32 }, 1304 1321 { "--unquoted-args", 'u', RTGETOPT_REQ_NOTHING }, 1305 1322 { "--ignore-operhaned-processes", kGstCtrlRunOpt_IgnoreOrphanedProcesses, RTGETOPT_REQ_NOTHING }, 1306 1323 { "--no-profile", kGstCtrlRunOpt_NoProfile, RTGETOPT_REQ_NOTHING }, 1324 /* run only: 6 - options */ 1307 1325 { "--dos2unix", kGstCtrlRunOpt_Dos2Unix, RTGETOPT_REQ_NOTHING }, 1308 1326 { "--unix2dos", kGstCtrlRunOpt_Unix2Dos, RTGETOPT_REQ_NOTHING }, … … 1313 1331 }; 1314 1332 1333 /** @todo stdin handling. */ 1334 1315 1335 int ch; 1316 1336 RTGETOPTUNION ValueUnion; 1317 1337 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); 1320 1341 1321 1342 com::SafeArray<ProcessCreateFlag_T> aCreateFlags; … … 1375 1396 break; 1376 1397 1398 /* run only options: */ 1377 1399 case kGstCtrlRunOpt_Dos2Unix: 1400 Assert(fRunCmd); 1378 1401 enmStdErrTransform = enmStdOutTransform = kStreamTransform_Dos2Unix; 1379 1402 break; 1380 1403 case kGstCtrlRunOpt_Unix2Dos: 1404 Assert(fRunCmd); 1381 1405 enmStdErrTransform = enmStdOutTransform = kStreamTransform_Unix2Dos; 1382 1406 break; 1383 1407 1384 1408 case kGstCtrlRunOpt_WaitForStdOut: 1385 if (!fRunCmd) 1386 return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stdout"); 1409 Assert(fRunCmd); 1387 1410 fWaitForStdOut = true; 1388 1411 break; 1389 1412 case kGstCtrlRunOpt_NoWaitForStdOut: 1390 if (!fRunCmd) 1391 return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --no-wait-for-stdout"); 1413 Assert(fRunCmd); 1392 1414 fWaitForStdOut = false; 1393 1415 break; 1394 1416 1395 1417 case kGstCtrlRunOpt_WaitForStdErr: 1396 if (!fRunCmd) 1397 return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stderr"); 1418 Assert(fRunCmd); 1398 1419 fWaitForStdErr = true; 1399 1420 break; 1400 1421 case kGstCtrlRunOpt_NoWaitForStdErr: 1401 if (!fRunCmd) 1402 return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stderr"); 1422 Assert(fRunCmd); 1403 1423 fWaitForStdErr = false; 1404 1424 break; … … 1454 1474 } 1455 1475 1456 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);1476 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 1457 1477 if (rcExit != RTEXITCODE_SUCCESS) 1458 1478 return rcExit; … … 1690 1710 1691 1711 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);1712 static DECLCALLBACK(RTEXITCODE) gctlHandleRun(PGCTLCMDCTX pCtx, int argc, char **argv) 1713 { 1714 return gctlHandleRunCommon(pCtx, argc, argv, true /*fRunCmd*/, USAGE_GSTCTRL_RUN); 1715 } 1716 1717 1718 static DECLCALLBACK(RTEXITCODE) gctlHandleStart(PGCTLCMDCTX pCtx, int argc, char **argv) 1719 { 1720 return gctlHandleRunCommon(pCtx, argc, argv, false /*fRunCmd*/, USAGE_GSTCTRL_START); 1701 1721 } 1702 1722 … … 2583 2603 } 2584 2604 2585 static RTEXITCODE gctlHandleCopy(PGCTLCMDCTX pCtx, bool fHostToGuest)2605 static RTEXITCODE gctlHandleCopy(PGCTLCMDCTX pCtx, int argc, char **argv, bool fHostToGuest) 2586 2606 { 2587 2607 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 2601 2621 * does in here. 2602 2622 */ 2623 enum GETOPTDEF_COPY 2624 { 2625 GETOPTDEF_COPY_DRYRUN = 1000, 2626 GETOPTDEF_COPY_FOLLOW, 2627 GETOPTDEF_COPY_TARGETDIR 2628 }; 2603 2629 static const RTGETOPTDEF s_aOptions[] = 2604 2630 { … … 2613 2639 RTGETOPTUNION ValueUnion; 2614 2640 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); 2617 2642 2618 2643 Utf8Str strSource; 2619 Utf8Str strDest;2644 const char *pszDst = NULL; 2620 2645 uint32_t fFlags = CopyFileFlag_None; 2621 2646 bool fCopyRecursive = false; … … 2626 2651 2627 2652 int vrc = VINF_SUCCESS; 2628 while ((ch = RTGetOpt(&GetState, &ValueUnion)) )2653 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0) 2629 2654 { 2630 2655 /* For options that require an argument, ValueUnion has received the value. */ … … 2646 2671 2647 2672 case GETOPTDEF_COPY_TARGETDIR: 2648 strDest = ValueUnion.psz;2673 pszDst = ValueUnion.psz; 2649 2674 break; 2650 2675 2651 2676 case VINF_GETOPT_NOT_OPTION: 2652 {2653 2677 /* Last argument and no destination specified with 2654 2678 * --target-directory yet? Then use the current 2655 2679 * (= last) argument as destination. */ 2656 2680 if ( pCtx->pArg->argc == GetState.iNext 2657 && strDest.isEmpty()) 2658 { 2659 strDest = ValueUnion.psz; 2660 } 2681 && pszDst == NULL) 2682 pszDst = ValueUnion.psz; 2661 2683 else 2662 2684 { 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 } 2665 2693 } 2666 2694 break; 2667 }2668 2695 2669 2696 default: … … 2673 2700 2674 2701 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); 2683 2708 if (rcExit != RTEXITCODE_SUCCESS) 2684 2709 return rcExit; … … 2710 2735 } 2711 2736 2712 /* If the destination is a path, (try to) create it. */2713 const char *pszDest = strDest.c_str();2714 2737 /** @todo r=bird: RTPathFilename and RTPathStripFilename won't work 2715 2738 * correctly on non-windows hosts when the guest is from the DOS world (Windows, … … 2723 2746 * @bugref{6344} 2724 2747 */ 2725 if (!RTPathFilename(pszD est))2726 { 2727 vrc = gctlCopyDirCreate(pContext, pszD est);2748 if (!RTPathFilename(pszDst)) 2749 { 2750 vrc = gctlCopyDirCreate(pContext, pszDst); 2728 2751 } 2729 2752 else … … 2732 2755 * the actual file name and make sure the appropriate 2733 2756 * directories get created. */ 2734 char *pszD estDir = RTStrDup(pszDest);2735 AssertPtr(pszD estDir);2736 RTPathStripFilename(pszD estDir);2737 vrc = gctlCopyDirCreate(pContext, pszD estDir);2738 RTStrFree(pszD estDir);2757 char *pszDstDir = RTStrDup(pszDst); 2758 AssertPtr(pszDstDir); 2759 RTPathStripFilename(pszDstDir); 2760 vrc = gctlCopyDirCreate(pContext, pszDstDir); 2761 RTStrFree(pszDstDir); 2739 2762 } 2740 2763 … … 2800 2823 { 2801 2824 /* 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); 2805 2827 if (RT_SUCCESS(vrc)) 2806 2828 { 2807 vrc = gctlCopyFileToDest(pContext, pszSource, 2808 pszDestFile, 0 /* Flags */); 2809 RTStrFree(pszDestFile); 2829 vrc = gctlCopyFileToDest(pContext, pszSource, pszDstFile, 0 /* Flags */); 2830 RTStrFree(pszDstFile); 2810 2831 } 2811 2832 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); 2814 2834 } 2815 2835 else 2816 2836 { 2817 2837 /* Directory (with filter?). */ 2818 vrc = gctlCopyDirToDest(pContext, pszSource, pszFilter, 2819 strDest.c_str(), fFlags); 2838 vrc = gctlCopyDirToDest(pContext, pszSource, pszFilter, pszDst, fFlags); 2820 2839 } 2821 2840 } … … 2848 2867 } 2849 2868 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 )2869 static DECLCALLBACK(RTEXITCODE) gctlHandleCopyFrom(PGCTLCMDCTX pCtx, int argc, char **argv) 2870 { 2871 return gctlHandleCopy(pCtx, argc, argv, false /* Guest to host */); 2872 } 2873 2874 static DECLCALLBACK(RTEXITCODE) gctlHandleCopyTo(PGCTLCMDCTX pCtx, int argc, char **argv) 2875 { 2876 return gctlHandleCopy(pCtx, argc, argv, true /* Host to guest */); 2877 } 2878 2879 static DECLCALLBACK(RTEXITCODE) handleCtrtMkDir(PGCTLCMDCTX pCtx, int argc, char **argv) 2861 2880 { 2862 2881 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 2872 2891 RTGETOPTUNION ValueUnion; 2873 2892 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); 2876 2894 2877 2895 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) 2882 2901 { 2883 2902 /* For options that require an argument, ValueUnion has received the value. */ … … 2895 2914 2896 2915 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 } 2898 2954 break; 2899 2955 … … 2903 2959 } 2904 2960 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 2967 static DECLCALLBACK(RTEXITCODE) gctlHandleRmDir(PGCTLCMDCTX pCtx, int argc, char **argv) 2937 2968 { 2938 2969 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 2941 2972 { 2942 2973 GCTLCMD_COMMON_OPTION_DEFS() 2943 { "--recursive", 'R', RTGETOPT_REQ_NOTHING } 2974 { "--recursive", 'R', RTGETOPT_REQ_NOTHING }, 2944 2975 }; 2945 2976 … … 2947 2978 RTGETOPTUNION ValueUnion; 2948 2979 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) 2956 2987 { 2957 2988 /* For options that require an argument, ValueUnion has received the value. */ … … 2965 2996 2966 2997 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; 2968 3074 break; 3075 } 2969 3076 2970 3077 default: … … 2973 3080 } 2974 3081 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 3087 static DECLCALLBACK(RTEXITCODE) gctlHandleRm(PGCTLCMDCTX pCtx, int argc, char **argv) 3036 3088 { 3037 3089 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 3040 3092 { 3041 3093 GCTLCMD_COMMON_OPTION_DEFS() 3094 { "--force", 'f', RTGETOPT_REQ_NOTHING, }, 3042 3095 }; 3043 3096 … … 3045 3098 RTGETOPTUNION ValueUnion; 3046 3099 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) 3053 3107 { 3054 3108 /* For options that require an argument, ValueUnion has received the value. */ … … 3058 3112 3059 3113 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 } 3061 3151 break; 3062 3152 … … 3066 3156 } 3067 3157 3068 size_t cFiles = mapDirs.size(); 3069 if (!cFiles) 3158 if (!cFilesDeleted && !fForce) 3070 3159 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 3163 static DECLCALLBACK(RTEXITCODE) gctlHandleMv(PGCTLCMDCTX pCtx, int argc, char **argv) 3099 3164 { 3100 3165 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 3108 3173 RTGETOPTUNION ValueUnion; 3109 3174 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); 3112 3176 3113 3177 int vrc = VINF_SUCCESS; … … 3115 3179 bool fDryrun = false; 3116 3180 std::vector< Utf8Str > vecSources; 3117 Utf8Str strDest;3181 const char *pszDst = NULL; 3118 3182 com::SafeArray<PathRenameFlag_T> aRenameFlags; 3119 3183 … … 3136 3200 case VINF_GETOPT_NOT_OPTION: 3137 3201 vecSources.push_back(Utf8Str(ValueUnion.psz)); 3138 strDest = ValueUnion.psz;3202 pszDst = ValueUnion.psz; 3139 3203 break; 3140 3204 … … 3160 3224 "No destination specified!"); 3161 3225 3162 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);3226 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 3163 3227 if (rcExit != RTEXITCODE_SUCCESS) 3164 3228 return rcExit; … … 3173 3237 { 3174 3238 ComPtr<IGuestFsObjInfo> pFsObjInfo; 3175 rc = pCtx->pGuestSession->DirectoryQueryInfo(Bstr( strDest).raw(), pFsObjInfo.asOutParam());3239 rc = pCtx->pGuestSession->DirectoryQueryInfo(Bstr(pszDst).raw(), pFsObjInfo.asOutParam()); 3176 3240 if (FAILED(rc)) 3177 3241 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Destination must be a directory when specifying multiple sources\n"); … … 3190 3254 bool fSourceIsDirectory = false; 3191 3255 Utf8Str strCurSource = (*it); 3192 Utf8Str strCurDest = strDest;3193 3256 3194 3257 /** @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... */ 3195 3262 ComPtr<IGuestFsObjInfo> pFsObjInfo; 3196 3263 rc = pCtx->pGuestSession->FileQueryInfo(Bstr(strCurSource).raw(), pFsObjInfo.asOutParam()); … … 3212 3279 RTPrintf("Renaming %s \"%s\" to \"%s\" ...\n", 3213 3280 fSourceIsDirectory ? "directory" : "file", 3214 strCurSource.c_str(), strCurDest.c_str());3281 strCurSource.c_str(), pszDst); 3215 3282 3216 3283 if (!fDryrun) … … 3219 3286 { 3220 3287 CHECK_ERROR_BREAK(pCtx->pGuestSession, DirectoryRename(Bstr(strCurSource).raw(), 3221 Bstr( strCurDest).raw(),3288 Bstr(pszDst).raw(), 3222 3289 ComSafeArrayAsInParam(aRenameFlags))); 3223 3290 3224 3291 /* Break here, since it makes no sense to rename mroe than one source to 3225 3292 * 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. */ 3226 3298 it = vecSources.end(); 3227 3299 break; … … 3229 3301 else 3230 3302 CHECK_ERROR_BREAK(pCtx->pGuestSession, FileRename(Bstr(strCurSource).raw(), 3231 Bstr( strCurDest).raw(),3303 Bstr(pszDst).raw(), 3232 3304 ComSafeArrayAsInParam(aRenameFlags))); 3233 3305 } … … 3245 3317 } 3246 3318 3247 static DECLCALLBACK(RTEXITCODE) gctlHandleMkTemp(PGCTLCMDCTX pCtx )3319 static DECLCALLBACK(RTEXITCODE) gctlHandleMkTemp(PGCTLCMDCTX pCtx, int argc, char **argv) 3248 3320 { 3249 3321 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 3261 3333 RTGETOPTUNION ValueUnion; 3262 3334 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); 3265 3336 3266 3337 Utf8Str strTemplate; … … 3318 3389 "Creating temporary files is currently not supported!"); 3319 3390 3320 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);3391 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 3321 3392 if (rcExit != RTEXITCODE_SUCCESS) 3322 3393 return rcExit; … … 3363 3434 } 3364 3435 3365 static DECLCALLBACK(RTEXITCODE) gctlHandleStat(PGCTLCMDCTX pCtx )3436 static DECLCALLBACK(RTEXITCODE) gctlHandleStat(PGCTLCMDCTX pCtx, int argc, char **argv) 3366 3437 { 3367 3438 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 3379 3450 RTGETOPTUNION ValueUnion; 3380 3451 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); 3383 3453 3384 3454 DESTDIRMAP mapObjs; 3385 3455 3386 while ((ch = RTGetOpt(&GetState, &ValueUnion)) )3456 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0) 3387 3457 { 3388 3458 /* For options that require an argument, ValueUnion has received the value. */ … … 3412 3482 "No element(s) to check specified!"); 3413 3483 3414 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);3484 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 3415 3485 if (rcExit != RTEXITCODE_SUCCESS) 3416 3486 return rcExit; … … 3473 3543 } 3474 3544 3475 static DECLCALLBACK(RTEXITCODE) gctlHandleUpdateAdditions(PGCTLCMDCTX pCtx )3545 static DECLCALLBACK(RTEXITCODE) gctlHandleUpdateAdditions(PGCTLCMDCTX pCtx, int argc, char **argv) 3476 3546 { 3477 3547 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 3495 3565 RTGETOPTUNION ValueUnion; 3496 3566 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); 3499 3568 3500 3569 int vrc = VINF_SUCCESS; … … 3558 3627 3559 3628 3560 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);3629 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 3561 3630 if (rcExit != RTEXITCODE_SUCCESS) 3562 3631 return rcExit; … … 3600 3669 } 3601 3670 3602 static DECLCALLBACK(RTEXITCODE) gctlHandleList(PGCTLCMDCTX pCtx )3671 static DECLCALLBACK(RTEXITCODE) gctlHandleList(PGCTLCMDCTX pCtx, int argc, char **argv) 3603 3672 { 3604 3673 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 3612 3681 RTGETOPTUNION ValueUnion; 3613 3682 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); 3616 3684 3617 3685 bool fSeenListArg = false; … … 3655 3723 Assert(fListAll || fListSessions); 3656 3724 3657 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);3725 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 3658 3726 if (rcExit != RTEXITCODE_SUCCESS) 3659 3727 return rcExit; … … 3770 3838 } 3771 3839 3772 static DECLCALLBACK(RTEXITCODE) gctlHandleCloseProcess(PGCTLCMDCTX pCtx )3840 static DECLCALLBACK(RTEXITCODE) gctlHandleCloseProcess(PGCTLCMDCTX pCtx, int argc, char **argv) 3773 3841 { 3774 3842 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 3784 3852 RTGETOPTUNION ValueUnion; 3785 3853 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); 3788 3855 3789 3856 std::vector < uint32_t > vecPID; … … 3854 3921 "Either session ID or name (pattern) must be specified"); 3855 3922 3856 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);3923 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 3857 3924 if (rcExit != RTEXITCODE_SUCCESS) 3858 3925 return rcExit; … … 3955 4022 3956 4023 3957 static DECLCALLBACK(RTEXITCODE) gctlHandleCloseSession(PGCTLCMDCTX pCtx )4024 static DECLCALLBACK(RTEXITCODE) gctlHandleCloseSession(PGCTLCMDCTX pCtx, int argc, char **argv) 3958 4025 { 3959 4026 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 3974 4041 RTGETOPTUNION ValueUnion; 3975 4042 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); 3978 4044 3979 4045 ULONG ulSessionID = UINT32_MAX; 3980 4046 Utf8Str strSessionName; 3981 4047 3982 while ((ch = RTGetOpt(&GetState, &ValueUnion)) )4048 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0) 3983 4049 { 3984 4050 /* For options that require an argument, ValueUnion has received the value. */ … … 4017 4083 "Either session ID or name (pattern) must be specified"); 4018 4084 4019 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);4085 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 4020 4086 if (rcExit != RTEXITCODE_SUCCESS) 4021 4087 return rcExit; … … 4081 4147 4082 4148 4083 static DECLCALLBACK(RTEXITCODE) gctlHandleWatch(PGCTLCMDCTX pCtx )4149 static DECLCALLBACK(RTEXITCODE) gctlHandleWatch(PGCTLCMDCTX pCtx, int argc, char **argv) 4084 4150 { 4085 4151 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 4096 4162 RTGETOPTUNION ValueUnion; 4097 4163 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) 4102 4167 { 4103 4168 /* For options that require an argument, ValueUnion has received the value. */ … … 4115 4180 /** @todo Specify a --timeout for waiting only for a certain amount of time? */ 4116 4181 4117 RTEXITCODE rcExit = gctlCtxPost ArgParsingInit(pCtx);4182 RTEXITCODE rcExit = gctlCtxPostOptionParsingInit(pCtx); 4118 4183 if (rcExit != RTEXITCODE_SUCCESS) 4119 4184 return rcExit; … … 4235 4300 4236 4301 /* 4237 * Lookup the command and invoke the handler.4302 * VBoxManage guestcontrol [common-options] <VM> [common-options] <sub-command> ... 4238 4303 * 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.) 4242 4309 */ 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); 4263 4357 } 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; 4268 4365 } 4269 4366 #endif /* !VBOX_ONLY_DOCS */
Note:
See TracChangeset
for help on using the changeset viewer.