Changeset 36035 in vbox for trunk/src/VBox/Frontends
- Timestamp:
- Feb 21, 2011 2:44:15 PM (14 years ago)
- svn:sync-xref-src-repo-rev:
- 70144
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp
r35953 r36035 5 5 6 6 /* 7 * Copyright (C) 201 1 Oracle Corporation7 * Copyright (C) 2010-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 /* 78 /** 79 79 * Special exit codes for returning errors/information of a 80 80 * started guest process to the command line VBoxManage was started from. 81 81 * Useful for e.g. scripting. 82 * @todo r=bird: RTEXITCODE is using the RT prefix, that is reserved for IPRT 83 * use only. 82 84 */ 83 85 enum RTEXITCODE_EXEC … … 91 93 }; 92 94 93 /* 95 /** 94 96 * RTGetOpt-IDs for the guest execution control command line. 97 * @todo r=bird: RTGETOPTDEF_EXEC is using the RT prefix, that is reserved for 98 * IPRT use only. 95 99 */ 96 100 enum RTGETOPTDEF_EXEC … … 210 214 switch (uStatus) 211 215 { 216 /** @todo r=bird: Why do you use guestControl status codes here? Is the Main 217 * API not exposing these constants? If so, it's something that 218 * needs fixing. */ 212 219 case guestControl::PROC_STS_STARTED: 213 220 rc = RTEXITCODE_EXEC_SUCCESS; 214 221 break; 215 222 case guestControl::PROC_STS_TEN: 223 /** @todo check the exit code, 0 is success, !0 should be indicated 224 * by a specail VBoxManage exit code. */ 216 225 rc = RTEXITCODE_EXEC_SUCCESS; 217 226 break; … … 231 240 /* Service/OS is stopping, process was killed, so 232 241 * not exactly an error of the started process ... */ 233 rc = RTEXITCODE_EXEC_SUCCESS; 242 rc = RTEXITCODE_EXEC_SUCCESS; /** @todo r=bird: return failure if you don't know what happend. No false positives, please. */ 234 243 break; 235 244 case guestControl::PROC_STS_ERROR: … … 370 379 //{ "--output-format", RTGETOPTDEF_EXEC_OUTPUTFORMAT, RTGETOPT_REQ_STRING }, 371 380 { "--output-type", RTGETOPTDEF_EXEC_OUTPUTTYPE, RTGETOPT_REQ_STRING }, 381 /** @todo r=bird: output-type and output-format are the same thing. 382 * Please just call it --dos2unix and --unix2dos and 383 * implement it them. (If you insist on flexibility, it would 384 * be --input-type <x> and --output-type <y>, where x and y 385 * could indicate line ending styles (CR/LF/CRLF), encodings 386 * (UTF-8, UTF-16, ++), code pages and plugin provided 387 * conversions. See the conv option of dd and iconv for 388 * inspiration.) 389 * 390 * The default must be no conversion at all, i.e. 391 * --output-type=binary. */ 372 392 { "--password", 'p', RTGETOPT_REQ_STRING }, 373 393 { "--timeout", 't', RTGETOPT_REQ_UINT32 }, … … 377 397 { "--wait-stdout", RTGETOPTDEF_EXEC_WAITFORSTDOUT, RTGETOPT_REQ_NOTHING }, 378 398 { "--wait-stderr", RTGETOPTDEF_EXEC_WAITFORSTDERR, RTGETOPT_REQ_NOTHING }, 399 /* @todo r=bird: '--' is interpreted by RTGetOpt() to indicate the 400 * end of arguments, you don't need to handle this specially. 401 * RTGetOpt() will return VINF_GETOPT_NOT_OPTION for anything following 402 * a '--' argument. So, let me give you some other examples for illustrating 403 * what I mean should work now (all the same, userid/pw missing): 404 * VBoxManage guestcontrol myvm exec --image /bin/busybox cp /foo /bar 405 * VBoxManage guestcontrol myvm exec --image /bin/busybox -- cp /foo /bar 406 * VBoxManage guestcontrol myvm exec --image /bin/busybox cp -- /foo /bar 407 * VBoxManage guestcontrol myvm exec --image /bin/busybox cp /foo -- /bar 408 * 409 * Same, but with standard cp (image defaults to /bin/cp): 410 * VBoxManage guestcontrol myvm exec /bin/cp /foo /bar 411 * VBoxManage guestcontrol myvm exec -- /bin/cp /foo /bar 412 * VBoxManage guestcontrol myvm exec /bin/cp -- /foo /bar 413 * VBoxManage guestcontrol myvm exec /bin/cp /foo -- /bar 414 * 415 * The old example: 416 * VBoxManage guestcontrol myvm exec --image /bin/busybox -- ln -s /foo /bar 417 * 418 * As an example of where '--' is used by standard linux utils, try delete a 419 * file named '-f' without specifying the directory: 420 * > -f # creates the file 421 * rm -v -f # does nothing because '-f' is a rm option. 422 * rm -v -- -f # does the trick because '--' tells rm to not read '-f' as an option. 423 */ 379 424 { "--", RTGETOPTDEF_EXEC_ARGS, RTGETOPT_REQ_STRING } 380 425 }; … … 391 436 Utf8Str Utf8UserName; 392 437 Utf8Str Utf8Password; 393 uint32_t u32TimeoutMS= 0;438 uint32_t cMsTimeout = 0; 394 439 bool fOutputBinary = false; 395 440 bool fWaitForExit = false; … … 413 458 if (RT_FAILURE(vrc)) 414 459 return errorSyntax(USAGE_GUESTCONTROL, "Failed to parse environment value, rc=%Rrc", vrc); 415 else 416 { 417 for (int j = 0; j < cArgs; j++) 418 env.push_back(Bstr(papszArg[j]).raw()); 419 420 RTGetOptArgvFree(papszArg); 421 } 460 for (int j = 0; j < cArgs; j++) 461 env.push_back(Bstr(papszArg[j]).raw()); 462 463 RTGetOptArgvFree(papszArg); 422 464 break; 423 465 } … … 431 473 break; 432 474 433 /* case RTGETOPTDEF_EXEC_OUTPUTFORMAT:434 /** todo Add DOS2UNIX and vice versa handling!475 /** @todo Add DOS2UNIX and vice versa handling! 476 case RTGETOPTDEF_EXEC_OUTPUTFORMAT: 435 477 break;*/ 436 478 … … 455 497 456 498 case 't': /* Timeout */ 457 u32TimeoutMS= ValueUnion.u32;499 cMsTimeout = ValueUnion.u32; 458 500 break; 459 501 … … 523 565 } 524 566 525 if (RT_FAILURE(vrc)) 567 if (RT_FAILURE(vrc)) /** @todo r=bird: You don't need to check vrc here or in any of the two while conditions above because you are now returning directly in those cases. Drop the checks, this will keep things simpler. */ 526 568 { 527 569 RTMsgError("Failed to parse argument '%c', rc=%Rrc", ch, vrc); … … 535 577 return errorSyntax(USAGE_GUESTCONTROL, "No user name specified!"); 536 578 579 /* 580 * <missing comment indicating that we're done parsing args and started doing something else> 581 */ 537 582 HRESULT rc = S_OK; 538 583 if (fVerbose) 539 584 { 540 if ( u32TimeoutMS== 0)585 if (cMsTimeout == 0) 541 586 RTPrintf("Waiting for guest to start process ...\n"); 542 587 else 543 RTPrintf("Waiting for guest to start process (within %ums)\n", u32TimeoutMS);588 RTPrintf("Waiting for guest to start process (within %ums)\n", cMsTimeout); 544 589 } 545 590 … … 548 593 549 594 /* Execute the process. */ 550 int rcProc = RTEXITCODE_EXEC_SUCCESS; 595 int rcProc = RTEXITCODE_EXEC_SUCCESS; /** @todo r=bird: Don't initialize this, please, set it explicitly in the various branches. It's easier to see what it's going to be that way. */ 551 596 ComPtr<IProgress> progress; 552 597 ULONG uPID = 0; … … 557 602 Bstr(Utf8UserName).raw(), 558 603 Bstr(Utf8Password).raw(), 559 u32TimeoutMS,604 cMsTimeout, 560 605 &uPID, 561 606 progress.asOutParam()); 562 607 if (FAILED(rc)) 563 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest)); 608 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest)); /** @todo return straight away and drop state (e.g. rcProc) confusion. */ 564 609 else 565 610 { … … 568 613 if (fWaitForExit) 569 614 { 570 if ( u32TimeoutMS) /* Wait with a certain timeout. */615 if (fVerbose) 571 616 { 572 /* Calculate timeout value left after process has been started. */ 573 uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS; 574 /* Is timeout still bigger than current difference? */ 575 if (u32TimeoutMS > u64Elapsed) 576 { 577 if (fVerbose) 578 RTPrintf("Waiting for process to exit (%ums left) ...\n", u32TimeoutMS - u64Elapsed); 579 } 580 else 581 { 582 if (fVerbose) 583 RTPrintf("No time left to wait for process!\n"); 584 } 617 if (cMsTimeout) /* Wait with a certain timeout. */ 618 { 619 /* Calculate timeout value left after process has been started. */ 620 uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS; 621 /* Is timeout still bigger than current difference? */ 622 if (cMsTimeout > u64Elapsed) 623 RTPrintf("Waiting for process to exit (%ums left) ...\n", cMsTimeout - u64Elapsed); 624 else 625 RTPrintf("No time left to wait for process!\n"); /** @todo a bit misleading ... */ 626 } 627 else /* Wait forever. */ 628 RTPrintf("Waiting for process to exit ...\n"); 585 629 } 586 else if (fVerbose) /* Wait forever. */587 RTPrintf("Waiting for process to exit ...\n");588 630 589 631 /* Setup signal handling if cancelable. */ … … 612 654 || fWaitForStdErr) 613 655 { 656 /** @todo r=bird: The timeout argument is bogus in several 657 * ways: 658 * 1. RT_MAX will evaluate the arguments twice, which may 659 * result in different values because RTTimeMilliTS() 660 * returns a higher value the 2nd time. Worst case: 661 * Imagine when RT_MAX calculates the remaining time 662 * out (first expansion) there is say 60 ms left. Then 663 * we're preempted and rescheduled after, say, 120 ms. 664 * We call RTTimeMilliTS() again and ends up with a 665 * value -60 ms, which translate to a UINT32_MAX - 59 666 * ms timeout. 667 * 668 * 2. When the period expires, we will wait forever since 669 * both 0 and -1 mean indefinite timeout with this API, 670 * at least that's one way of reading the main code. 671 * 672 * 3. There is a signed/unsigned ambiguity in the 673 * RT_MAX expression. The left hand side is signed 674 * integer (0), the right side is unsigned 64-bit. From 675 * what I can tell, the compiler will treat this as 676 * unsigned 64-bit and never return 0. 677 */ 678 /** @todo r=bird: We must separate stderr and stdout 679 * output, seems bunched together here which 680 * won't do the trick for unix BOFHs. */ 614 681 rc = guest->GetProcessOutput(uPID, 0 /* aFlags */, 615 RT_MAX(0, u32TimeoutMS- (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */,682 RT_MAX(0, cMsTimeout - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */, 616 683 _64K, ComSafeArrayAsOutParam(aOutputData)); 617 684 if (FAILED(rc)) … … 685 752 686 753 /* Did we run out of time? */ 687 if ( u32TimeoutMS688 && RTTimeMilliTS() - u64StartMS > u32TimeoutMS)754 if ( cMsTimeout 755 && RTTimeMilliTS() - u64StartMS > cMsTimeout) 689 756 { 690 757 progress->Cancel(); … … 705 772 } 706 773 else if ( fCompleted 707 && SUCCEEDED(rc)) 774 && SUCCEEDED(rc)) /* The GetProcessOutput rc. */ 708 775 { 709 776 LONG iRc; … … 717 784 if (SUCCEEDED(rc) && fVerbose) 718 785 RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, uRetStatus, ctrlExecProcessStatusToText(uRetStatus), uRetFlags); 786 /** @todo r=bird: This isn't taking uRetExitCode into 787 * account. We MUST give the users a way to indicate 788 * whether the guest program terminated with an 0 or 789 * non-zero exit code. */ 719 790 rcProc = ctrlExecProcessStatusToExitCode(uRetStatus); 720 791 } … … 724 795 if (fVerbose) 725 796 RTPrintf("Process execution aborted!\n"); 797 /** @todo r=bird: Should set rcProc here, this is not a 798 * success branch. */ 726 799 } 727 800 } … … 1125 1198 1126 1199 /* 1127 * Check the syntax. We can deduce the correct syntax from the number of 1128 * arguments. 1200 * Check the syntax. 1129 1201 */ 1130 1202 if (pArg->argc < 2) /* At least the source + destination should be present :-). */ … … 1156 1228 1157 1229 int vrc = VINF_SUCCESS; 1158 uint32_t uNoOptionIdx= 0;1230 uint32_t idxNonOption = 0; 1159 1231 bool fUsageOK = true; 1160 1232 while ( (ch = RTGetOpt(&GetState, &ValueUnion)) … … 1191 1263 { 1192 1264 /* Get the actual source + destination. */ 1193 switch ( uNoOptionIdx)1265 switch (idxNonOption) 1194 1266 { 1195 1267 case 0: … … 1202 1274 1203 1275 default: 1276 /** @todo r=bird: {2..UINT32_MAX-1} goes unnoticed 1277 * to /dev/null? That doesn't make much 1278 * sense... Why not just return with a syntax 1279 * error straight away? */ 1204 1280 break; 1205 1281 } 1206 uNoOptionIdx++;1207 if ( uNoOptionIdx== UINT32_MAX)1282 idxNonOption++; 1283 if (idxNonOption == UINT32_MAX) 1208 1284 { 1209 1285 RTMsgError("Too many files specified! Aborting.\n"); 1210 vrc = VERR_TOO_MUCH_DATA; 1286 vrc = VERR_TOO_MUCH_DATA; /** @todo r=bird: return straight away, drop the RT_SUCCESS(vrc) from the while condition. Keep things simple. */ 1211 1287 } 1212 1288 break; … … 1218 1294 } 1219 1295 1220 if (!fUsageOK) 1296 if (!fUsageOK) /** @todo r=bird: fUsageOK - never set to false, just drop it. Keep the state simple. */ 1221 1297 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters"); 1222 1298 … … 1232 1308 return errorSyntax(USAGE_GUESTCONTROL, 1233 1309 "No user name specified!"); 1310 1311 /* 1312 * Done parsing arguments, do stuff. 1313 */ 1234 1314 HRESULT rc = S_OK; 1235 1315 if (fVerbose) … … 1276 1356 } 1277 1357 1278 uint32_t uCurObject = 1;1358 uint32_t iObject = 1; 1279 1359 RTListForEach(&listToCopy, pNode, DIRECTORYENTRY, Node) 1280 1360 { … … 1283 1363 if (fVerbose) 1284 1364 RTPrintf("Copying \"%s\" to \"%s\" (%u/%u) ...\n", 1285 pNode->pszSourcePath, pNode->pszDestPath, uCurObject, cObjects);1365 pNode->pszSourcePath, pNode->pszDestPath, iObject, cObjects); 1286 1366 /* Finally copy the desired file (if no dry run selected). */ 1287 1367 if (!fDryRun) … … 1291 1371 if (RT_FAILURE(vrc)) 1292 1372 break; 1293 uCurObject++;1373 iObject++; 1294 1374 } 1295 1375 if (RT_SUCCESS(vrc) && fVerbose) … … 1336 1416 1337 1417 RTLISTNODE listDirs; 1338 uint32_t uNumDirs = 0;1418 uint32_t cDirs = 0; 1339 1419 RTListInit(&listDirs); 1340 1420 … … 1374 1454 if (RT_SUCCESS(vrc)) 1375 1455 { 1376 uNumDirs++;1377 if ( uNumDirs == UINT32_MAX)1456 cDirs++; 1457 if (cDirs == UINT32_MAX) 1378 1458 { 1379 1459 RTMsgError("Too many directories specified! Aborting.\n"); … … 1392 1472 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters"); 1393 1473 1394 if (! uNumDirs)1474 if (!cDirs) 1395 1475 return errorSyntax(USAGE_GUESTCONTROL, 1396 1476 "No directory to create specified!"); … … 1401 1481 1402 1482 HRESULT rc = S_OK; 1403 if (fVerbose && uNumDirs > 1)1404 RTPrintf("Creating %u directories ...\n", uNumDirs);1483 if (fVerbose && cDirs > 1) 1484 RTPrintf("Creating %u directories ...\n", cDirs); 1405 1485 1406 1486 PDIRECTORYENTRY pNode; … … 1416 1496 if (FAILED(rc)) 1417 1497 { 1418 /* rc ignored */ ctrlPrintError(guest, COM_IIDOF(IGuest));1498 ctrlPrintError(guest, COM_IIDOF(IGuest)); /* (return code ignored, save original rc) */ 1419 1499 break; 1420 1500 } … … 1432 1512 * arguments. 1433 1513 */ 1434 if (pArg->argc < 1) /* At least the VM name should be present :-). */ 1514 if (pArg->argc < 1) /* At least the VM name should be present :-). */ /** @todo r=bird: This is no longer correct. Ditto for all other handlers. */ 1435 1515 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters"); 1436 1516 … … 1534 1614 * Access the guest control store. 1535 1615 * 1536 * @returns 0 on success, 1 on failure1616 * @returns program exit code. 1537 1617 * @note see the command line API description for parameters 1538 1618 */
Note:
See TracChangeset
for help on using the changeset viewer.