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

