Changeset 36206 in vbox for trunk/src/VBox/Frontends
- Timestamp:
- Mar 8, 2011 4:32:21 PM (14 years ago)
- File:
-
- 1 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp
r36189 r36206 29 29 #include <VBox/com/ErrorInfo.h> 30 30 #include <VBox/com/errorprint.h> 31 32 31 #include <VBox/com/VirtualBox.h> 33 32 #include <VBox/com/EventQueue.h> 34 33 35 #include <VBox/HostServices/GuestControlSvc.h> /* for PROC_STS_XXX */ 34 #include <VBox/err.h> 35 #include <VBox/log.h> 36 36 37 37 #include <iprt/asm.h> … … 108 108 }; 109 109 110 enum OUTPUT _TYPE111 { 112 OUTPUT _TYPE_UNDEFINED = 0,113 OUTPUT _TYPE_DOS2UNIX = 10,114 OUTPUT _TYPE_UNIX2DOS = 20110 enum OUTPUTTYPE 111 { 112 OUTPUTTYPE_UNDEFINED = 0, 113 OUTPUTTYPE_DOS2UNIX = 10, 114 OUTPUTTYPE_UNIX2DOS = 20 115 115 }; 116 116 … … 120 120 { 121 121 RTStrmPrintf(pStrm, 122 "VBoxManage guestcontrol <vmname>|<uuid> exec[ute]\n" 122 "VBoxManage guestcontrol <vmname>|<uuid>\n" 123 " exec[ute]\n" 123 124 " --image <path to program>\n" 124 125 " --username <name> --password <password>\n" … … 133 134 * stuff, e.g. "VBoxManage guestcontrol execute <VMName> --username <> ... -- /bin/rm -Rf /foo". */ 134 135 "\n" 135 " <vmname>|<uuid>copyto|cp\n"136 " copyto|cp\n" 136 137 " <source on host> <destination on guest>\n" 137 138 " --username <name> --password <password>\n" 138 139 " [--dryrun] [--follow] [--recursive] [--verbose]\n" 139 140 "\n" 140 " <vmname>|<uuid>createdir[ectory]|mkdir|md\n"141 " createdir[ectory]|mkdir|md\n" 141 142 " <directory to create on guest>\n" 142 143 " --username <name> --password <password>\n" 143 144 " [--parents] [--mode <mode>] [--verbose]\n" 144 145 "\n" 145 " <vmname>|<uuid>updateadditions\n"146 " updateadditions\n" 146 147 " [--source <guest additions .ISO>] [--verbose]\n" 147 148 "\n"); … … 402 403 Utf8Str Utf8Password; 403 404 uint32_t cMsTimeout = 0; 404 OUTPUT _TYPE eOutputType = OUTPUT_TYPE_UNDEFINED;405 OUTPUTTYPE eOutputType = OUTPUTTYPE_UNDEFINED; 405 406 bool fOutputBinary = false; 406 407 bool fWaitForExit = false; … … 417 418 { 418 419 case GETOPTDEF_EXEC_DOS2UNIX: 419 if (eOutputType != OUTPUT _TYPE_UNDEFINED)420 if (eOutputType != OUTPUTTYPE_UNDEFINED) 420 421 return errorSyntax(USAGE_GUESTCONTROL, "More than one output type (dos2unix/unix2dos) specified!"); 421 eOutputType = OUTPUT _TYPE_DOS2UNIX;422 eOutputType = OUTPUTTYPE_DOS2UNIX; 422 423 break; 423 424 … … 456 457 457 458 case GETOPTDEF_EXEC_UNIX2DOS: 458 if (eOutputType != OUTPUT _TYPE_UNDEFINED)459 if (eOutputType != OUTPUTTYPE_UNDEFINED) 459 460 return errorSyntax(USAGE_GUESTCONTROL, "More than one output type (dos2unix/unix2dos) specified!"); 460 eOutputType = OUTPUT _TYPE_UNIX2DOS;461 eOutputType = OUTPUTTYPE_UNIX2DOS; 461 462 break; 462 463 … … 485 486 case VINF_GETOPT_NOT_OPTION: 486 487 { 488 #if 0 /** @todo r=bird: enable this when the argv[0] issue has been addressed. */ 489 if (args.size() == 0 && Utf8Cmd.isEmpty()) 490 Utf8Cmd = ValueUnion.psz; 491 args.push_back(Bstr(ValueUnion.psz).raw()); 492 #else 487 493 if (Utf8Cmd.isEmpty()) 494 { 488 495 Utf8Cmd = ValueUnion.psz; 496 if (Utf8Cmd.isEmpty()) 497 return RTMsgErrorExit(RTEXITCODE_SYNTAX, "The first argument cannot be empty yet, wait for 4.1.0"); 498 } 489 499 else 490 {491 /* Push current parameter to vector. */492 500 args.push_back(Bstr(ValueUnion.psz).raw()); 493 494 /* 495 * Add all following parameters after this one to our guest process 496 * argument vector. 497 */ 498 while ( (ch = RTGetOpt(&GetState, &ValueUnion)) 499 && RT_SUCCESS(vrc)) 500 { 501 /* 502 * Is this option unknown or not recognized an option? Then just 503 * add the raw string value to our argument vector. 504 */ 505 if ( ch == VINF_GETOPT_NOT_OPTION 506 || ch == VERR_GETOPT_UNKNOWN_OPTION) 507 args.push_back(Bstr(ValueUnion.psz).raw()); 508 /* 509 * If this is an option/parameter we already defined for our actual 510 * execution command we need to take the pDef->pszLong value instead. 511 */ 512 else if (ValueUnion.pDef) 513 args.push_back(Bstr(ValueUnion.pDef->pszLong).raw()); 514 else 515 AssertMsgFailed(("Unknown parameter type detected!\n")); 516 } 517 } 501 #endif 518 502 break; 519 503 } … … 560 544 if (FAILED(rc)) 561 545 return ctrlPrintError(guest, COM_IIDOF(IGuest)); 562 else 546 547 if (fVerbose) 548 RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.c_str(), uPID); 549 if (fWaitForExit) 563 550 { 564 551 if (fVerbose) 565 RTPrintf("Process '%s' (PID: %u) started\n", Utf8Cmd.c_str(), uPID); 566 if (fWaitForExit) 567 { 568 if (fVerbose) 569 { 570 if (cMsTimeout) /* Wait with a certain timeout. */ 552 { 553 if (cMsTimeout) /* Wait with a certain timeout. */ 554 { 555 /* Calculate timeout value left after process has been started. */ 556 uint64_t u64Elapsed = RTTimeMilliTS() - u64StartMS; 557 /* Is timeout still bigger than current difference? */ 558 if (cMsTimeout > u64Elapsed) 559 RTPrintf("Waiting for process to exit (%ums left) ...\n", cMsTimeout - u64Elapsed); 560 else 561 RTPrintf("No time left to wait for process!\n"); /** @todo a bit misleading ... */ 562 } 563 else /* Wait forever. */ 564 RTPrintf("Waiting for process to exit ...\n"); 565 } 566 567 /* Setup signal handling if cancelable. */ 568 ASSERT(progress); 569 bool fCanceledAlready = false; 570 BOOL fCancelable; 571 HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable); 572 if (FAILED(hrc)) 573 fCancelable = FALSE; 574 if (fCancelable) 575 ctrlSignalHandlerInstall(); 576 577 /* Wait for process to exit ... */ 578 BOOL fCompleted = FALSE; 579 BOOL fCanceled = FALSE; 580 int cMilliesSleep = 0; 581 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted)))) 582 { 583 SafeArray<BYTE> aOutputData; 584 ULONG cbOutputData = 0; 585 586 /* 587 * Some data left to output? 588 */ 589 if ( fWaitForStdOut 590 || fWaitForStdErr) 591 { 592 /** @todo r=bird: The timeout argument is bogus in several 593 * ways: 594 * 1. RT_MAX will evaluate the arguments twice, which may 595 * result in different values because RTTimeMilliTS() 596 * returns a higher value the 2nd time. Worst case: 597 * Imagine when RT_MAX calculates the remaining time 598 * out (first expansion) there is say 60 ms left. Then 599 * we're preempted and rescheduled after, say, 120 ms. 600 * We call RTTimeMilliTS() again and ends up with a 601 * value -60 ms, which translate to a UINT32_MAX - 59 602 * ms timeout. 603 * 604 * 2. When the period expires, we will wait forever since 605 * both 0 and -1 mean indefinite timeout with this API, 606 * at least that's one way of reading the main code. 607 * 608 * 3. There is a signed/unsigned ambiguity in the 609 * RT_MAX expression. The left hand side is signed 610 * integer (0), the right side is unsigned 64-bit. From 611 * what I can tell, the compiler will treat this as 612 * unsigned 64-bit and never return 0. 613 */ 614 /** @todo r=bird: We must separate stderr and stdout 615 * output, seems bunched together here which 616 * won't do the trick for unix BOFHs. */ 617 rc = guest->GetProcessOutput(uPID, 0 /* aFlags */, 618 RT_MAX(0, cMsTimeout - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */, 619 _64K, ComSafeArrayAsOutParam(aOutputData)); 620 if (FAILED(rc)) 571 621 { 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 (cMsTimeout > u64Elapsed) 576 RTPrintf("Waiting for process to exit (%ums left) ...\n", cMsTimeout - u64Elapsed); 577 else 578 RTPrintf("No time left to wait for process!\n"); /** @todo a bit misleading ... */ 622 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest)); 623 cbOutputData = 0; 579 624 } 580 else /* Wait forever. */ 581 RTPrintf("Waiting for process to exit ...\n"); 582 } 583 584 /* Setup signal handling if cancelable. */ 585 ASSERT(progress); 586 bool fCanceledAlready = false; 587 BOOL fCancelable; 588 HRESULT hrc = progress->COMGETTER(Cancelable)(&fCancelable); 589 if (FAILED(hrc)) 590 fCancelable = FALSE; 591 if (fCancelable) 592 ctrlSignalHandlerInstall(); 593 594 /* Wait for process to exit ... */ 595 BOOL fCompleted = FALSE; 596 BOOL fCanceled = FALSE; 597 int cMilliesSleep = 0; 598 while (SUCCEEDED(progress->COMGETTER(Completed(&fCompleted)))) 599 { 600 SafeArray<BYTE> aOutputData; 601 ULONG cbOutputData = 0; 602 603 /* 604 * Some data left to output? 605 */ 606 if ( fWaitForStdOut 607 || fWaitForStdErr) 625 else 608 626 { 609 /** @todo r=bird: The timeout argument is bogus in several 610 * ways: 611 * 1. RT_MAX will evaluate the arguments twice, which may 612 * result in different values because RTTimeMilliTS() 613 * returns a higher value the 2nd time. Worst case: 614 * Imagine when RT_MAX calculates the remaining time 615 * out (first expansion) there is say 60 ms left. Then 616 * we're preempted and rescheduled after, say, 120 ms. 617 * We call RTTimeMilliTS() again and ends up with a 618 * value -60 ms, which translate to a UINT32_MAX - 59 619 * ms timeout. 620 * 621 * 2. When the period expires, we will wait forever since 622 * both 0 and -1 mean indefinite timeout with this API, 623 * at least that's one way of reading the main code. 624 * 625 * 3. There is a signed/unsigned ambiguity in the 626 * RT_MAX expression. The left hand side is signed 627 * integer (0), the right side is unsigned 64-bit. From 628 * what I can tell, the compiler will treat this as 629 * unsigned 64-bit and never return 0. 630 */ 631 /** @todo r=bird: We must separate stderr and stdout 632 * output, seems bunched together here which 633 * won't do the trick for unix BOFHs. */ 634 rc = guest->GetProcessOutput(uPID, 0 /* aFlags */, 635 RT_MAX(0, cMsTimeout - (RTTimeMilliTS() - u64StartMS)) /* Timeout in ms */, 636 _64K, ComSafeArrayAsOutParam(aOutputData)); 637 if (FAILED(rc)) 627 cbOutputData = aOutputData.size(); 628 if (cbOutputData > 0) 638 629 { 639 vrc = ctrlPrintError(guest, COM_IIDOF(IGuest)); 640 cbOutputData = 0; 641 } 642 else 643 { 644 cbOutputData = aOutputData.size(); 645 if (cbOutputData > 0) 630 /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a 631 * generic problem and the new VFS APIs will handle it more 632 * transparently. (requires writing dos2unix/unix2dos filters ofc) */ 633 if (eOutputType != OUTPUTTYPE_UNDEFINED) 646 634 { 647 /** @todo r=bird: Use a VFS I/O stream filter for doing this, it's a 648 * generic problem and the new VFS APIs will handle it more 649 * transparently. (requires writing dos2unix/unix2dos filters ofc) */ 650 if (eOutputType != OUTPUT_TYPE_UNDEFINED) 635 /* 636 * If aOutputData is text data from the guest process' stdout or stderr, 637 * it has a platform dependent line ending. So standardize on 638 * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on 639 * Windows. Otherwise we end up with CR/CR/LF on Windows. 640 */ 641 ULONG cbOutputDataPrint = cbOutputData; 642 for (BYTE *s = aOutputData.raw(), *d = s; 643 s - aOutputData.raw() < (ssize_t)cbOutputData; 644 s++, d++) 651 645 { 652 /* 653 * If aOutputData is text data from the guest process' stdout or stderr, 654 * it has a platform dependent line ending. So standardize on 655 * Unix style, as RTStrmWrite does the LF -> CR/LF replacement on 656 * Windows. Otherwise we end up with CR/CR/LF on Windows. 657 */ 658 ULONG cbOutputDataPrint = cbOutputData; 659 for (BYTE *s = aOutputData.raw(), *d = s; 660 s - aOutputData.raw() < (ssize_t)cbOutputData; 661 s++, d++) 646 if (*s == '\r') 662 647 { 663 if (*s == '\r') 664 { 665 /* skip over CR, adjust destination */ 666 d--; 667 cbOutputDataPrint--; 668 } 669 else if (s != d) 670 *d = *s; 648 /* skip over CR, adjust destination */ 649 d--; 650 cbOutputDataPrint--; 671 651 } 672 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint); 652 else if (s != d) 653 *d = *s; 673 654 } 674 else /* Just dump all data as we got it ... */ 675 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputData); 655 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputDataPrint); 676 656 } 657 else /* Just dump all data as we got it ... */ 658 RTStrmWrite(g_pStdOut, aOutputData.raw(), cbOutputData); 677 659 } 678 660 } 679 680 /* No more output data left? */ 681 if (cbOutputData <= 0) 682 { 683 /* Only break out from process handling loop if we processed (displayed) 684 * all output data or if there simply never was output data and the process 685 * has been marked as complete. */ 686 if (fCompleted) 687 break; 688 } 689 690 /* Process async cancelation */ 691 if (g_fGuestCtrlCanceled && !fCanceledAlready) 692 { 693 hrc = progress->Cancel(); 694 if (SUCCEEDED(hrc)) 695 fCanceledAlready = TRUE; 696 else 697 g_fGuestCtrlCanceled = false; 698 } 699 700 /* Progress canceled by Main API? */ 701 if ( SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled))) 702 && fCanceled) 661 } 662 663 /* No more output data left? */ 664 if (cbOutputData <= 0) 665 { 666 /* Only break out from process handling loop if we processed (displayed) 667 * all output data or if there simply never was output data and the process 668 * has been marked as complete. */ 669 if (fCompleted) 703 670 break; 704 705 /* Did we run out of time? */ 706 if ( cMsTimeout 707 && RTTimeMilliTS() - u64StartMS > cMsTimeout) 708 { 709 progress->Cancel(); 710 break; 711 } 712 } /* while */ 713 714 /* Undo signal handling */ 715 if (fCancelable) 716 ctrlSignalHandlerUninstall(); 717 718 /* Report status back to the user. */ 719 if (fCanceled) 720 { 721 if (fVerbose) 722 RTPrintf("Process execution canceled!\n"); 723 rcProc = EXITCODE_EXEC_CANCELED; 724 } 725 else if ( fCompleted 726 && SUCCEEDED(rc)) /* The GetProcessOutput rc. */ 727 { 728 LONG iRc; 729 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc); 730 if (FAILED(iRc)) 731 vrc = ctrlPrintProgressError(progress); 671 } 672 673 /* Process async cancelation */ 674 if (g_fGuestCtrlCanceled && !fCanceledAlready) 675 { 676 hrc = progress->Cancel(); 677 if (SUCCEEDED(hrc)) 678 fCanceledAlready = TRUE; 732 679 else 733 { 734 ExecuteProcessStatus_T retStatus; 735 ULONG uRetExitCode, uRetFlags; 736 rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus); 737 if (SUCCEEDED(rc) && fVerbose) 738 RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, retStatus, ctrlExecProcessStatusToText(retStatus), uRetFlags); 739 rcProc = ctrlExecProcessStatusToExitCode(retStatus, uRetExitCode); 740 } 741 } 680 g_fGuestCtrlCanceled = false; 681 } 682 683 /* Progress canceled by Main API? */ 684 if ( SUCCEEDED(progress->COMGETTER(Canceled(&fCanceled))) 685 && fCanceled) 686 break; 687 688 /* Did we run out of time? */ 689 if ( cMsTimeout 690 && RTTimeMilliTS() - u64StartMS > cMsTimeout) 691 { 692 progress->Cancel(); 693 break; 694 } 695 } /* while */ 696 697 /* Undo signal handling */ 698 if (fCancelable) 699 ctrlSignalHandlerUninstall(); 700 701 /* Report status back to the user. */ 702 if (fCanceled) 703 { 704 if (fVerbose) 705 RTPrintf("Process execution canceled!\n"); 706 rcProc = EXITCODE_EXEC_CANCELED; 707 } 708 else if ( fCompleted 709 && SUCCEEDED(rc)) /* The GetProcessOutput rc. */ 710 { 711 LONG iRc; 712 CHECK_ERROR_RET(progress, COMGETTER(ResultCode)(&iRc), rc); 713 if (FAILED(iRc)) 714 vrc = ctrlPrintProgressError(progress); 742 715 else 743 716 { 744 if (fVerbose) 745 RTPrintf("Process execution aborted!\n"); 746 rcProc = EXITCODE_EXEC_TERM_ABEND; 747 } 717 ExecuteProcessStatus_T retStatus; 718 ULONG uRetExitCode, uRetFlags; 719 rc = guest->GetProcessStatus(uPID, &uRetExitCode, &uRetFlags, &retStatus); 720 if (SUCCEEDED(rc) && fVerbose) 721 RTPrintf("Exit code=%u (Status=%u [%s], Flags=%u)\n", uRetExitCode, retStatus, ctrlExecProcessStatusToText(retStatus), uRetFlags); 722 rcProc = ctrlExecProcessStatusToExitCode(retStatus, uRetExitCode); 723 } 724 } 725 else 726 { 727 if (fVerbose) 728 RTPrintf("Process execution aborted!\n"); 729 rcProc = EXITCODE_EXEC_TERM_ABEND; 748 730 } 749 731 } … … 761 743 * @param pszFileDest Full qualified destination path (optional). 762 744 * @param pList Copy list used for insertion. 745 * 746 * @todo r=bird: Since everyone is maintaining an entry count, it would make 747 * sense to abstract the list. To simplify cleanup work, it would be 748 * preferable to use a standard C++ container template class. 763 749 */ 764 750 static int ctrlDirectoryEntryAppend(const char *pszFileSource, const char *pszFileDest, … … 850 836 AssertPtrReturn(pList, VERR_INVALID_POINTER); 851 837 838 /* 839 * Construct current path. 840 */ 841 char szCurDir[RTPATH_MAX]; 842 int rc = RTStrCopy(szCurDir, sizeof(szCurDir), pszRootDir); 843 if (RT_SUCCESS(rc) && pszSubDir != NULL) 844 rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir); 845 846 /* 847 * Open directory without a filter - RTDirOpenFiltered unfortunately 848 * cannot handle sub directories so we have to do the filtering ourselves. 849 */ 852 850 PRTDIR pDir = NULL; 853 854 int rc = VINF_SUCCESS;855 char szCurDir[RTPATH_MAX];856 /* Construct current path. */857 if (RTStrPrintf(szCurDir, sizeof(szCurDir), pszRootDir))858 {859 if (pszSubDir != NULL)860 rc = RTPathAppend(szCurDir, sizeof(szCurDir), pszSubDir);861 }862 else863 rc = VERR_NO_MEMORY;864 865 851 if (RT_SUCCESS(rc)) 866 852 { 867 /* Open directory without a filter - RTDirOpenFiltered unfortunately868 * cannot handle sub directories so we have to do the filtering ourselves. */869 853 rc = RTDirOpen(&pDir, szCurDir); 870 for (;RT_SUCCESS(rc);) 854 if (RT_FAILURE(rc)) 855 pDir = NULL; 856 } 857 if (RT_SUCCESS(rc)) 858 { 859 /* 860 * Enumerate the directory tree. 861 */ 862 while (RT_SUCCESS(rc)) 871 863 { 872 864 RTDIRENTRY DirEntry; … … 881 873 { 882 874 case RTDIRENTRYTYPE_DIRECTORY: 883 /* Skip "." and ".." entri res. */875 /* Skip "." and ".." entries. */ 884 876 if ( !strcmp(DirEntry.szName, ".") 885 877 || !strcmp(DirEntry.szName, "..")) 886 {887 878 break; 888 } 879 889 880 if (fFlags & CopyFileFlag_Recursive) 890 881 { … … 918 909 case RTDIRENTRYTYPE_FILE: 919 910 { 920 bool fProcess = false; 921 if (pszFilter && RTStrSimplePatternMatch(pszFilter, DirEntry.szName)) 922 fProcess = true; 923 else if (!pszFilter) 924 fProcess = true; 925 926 if (fProcess) 911 if ( !pszFilter 912 || RTStrSimplePatternMatch(pszFilter, DirEntry.szName)) 927 913 { 928 914 char *pszFileSource = NULL; … … 946 932 rc = ctrlDirectoryEntryAppend(pszFileSource, pszFileDest, pList); 947 933 if (RT_SUCCESS(rc)) 948 *pcObjects = *pcObjects +1;934 *pcObjects += 1; 949 935 } 950 936 … … 963 949 break; 964 950 } 965 } 966 967 if (pDir) 951 968 952 RTDirClose(pDir); 953 } 969 954 return rc; 970 955 } … … 1145 1130 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); 1146 1131 1132 /** @todo r=bird: This command isn't very unix friendly in general. mkdir 1133 * is much better (partly because it is much simpler of course). The main 1134 * arguments against this is that (1) all but two options conflicts with 1135 * what 'man cp' tells me on a GNU/Linux system, (2) wildchar matching is 1136 * done windows CMD style (though not in a 100% compatible way), and (3) 1137 * that only one source is allowed - efficiently sabotaging default 1138 * wildcard expansion by a unix shell. The best solution here would be 1139 * two different variant, one windowsy (xcopy) and one unixy (gnu cp). */ 1140 /** @todo r=bird: Why isn't IGuest::CopyToGuest powerful enough to do 1141 * all this recursive copying? */ 1142 1147 1143 static const RTGETOPTDEF s_aOptions[] = 1148 1144 { 1149 { "--dryrun", 'd', RTGETOPT_REQ_NOTHING }, 1150 { "--follow", 'F', RTGETOPT_REQ_NOTHING }, 1151 { "--password", 'p', RTGETOPT_REQ_STRING }, 1152 { "--recursive", 'R', RTGETOPT_REQ_NOTHING }, 1153 { "--username", 'u', RTGETOPT_REQ_STRING }, 1145 { "--dryrun", 'd', RTGETOPT_REQ_NOTHING }, /**< @todo r=bird: '-d' incompatible with GNU cp. */ 1146 { "--follow", 'F', RTGETOPT_REQ_NOTHING }, /**< @todo r=bird: This isn't a GNU cp option. This is instead spread over several options. */ 1147 { "--password", 'p', RTGETOPT_REQ_STRING }, /**< @todo r=bird: Just drop these short options since (BSD, GNU, ++) cp uses '-p' to indicate that ownership, timestamp and other stuff should be preserved. Other file commands probably use '-p' as well. */ 1148 { "--recursive", 'R', RTGETOPT_REQ_NOTHING }, /**< @todo r=bird: Most cp implementations treats '-r' as an alias for '-R'. */ 1149 { "--username", 'u', RTGETOPT_REQ_STRING }, /**< @todo r=bird: Just drop these short options since GNU cp uses '-u' to indicate update-only (as does 4nt). Other file commands probably uses '-u' as well. */ 1154 1150 { "--verbose", 'v', RTGETOPT_REQ_NOTHING } 1155 1151 }; … … 1171 1167 int vrc = VINF_SUCCESS; 1172 1168 uint32_t idxNonOption = 0; 1173 while ( ch = RTGetOpt(&GetState, &ValueUnion))1169 while ((ch = RTGetOpt(&GetState, &ValueUnion))) 1174 1170 { 1175 1171 /* For options that require an argument, ValueUnion has received the value. */ … … 1213 1209 break; 1214 1210 1211 /* @todo r=bird: VBoxManage guestcontrol execute <VMName> copyto hostfile.1 hostfile.2 hostfile.3 guestdir/ 1212 * A better way to implement this would be to tell RTGetOptInit to sort the argument, so 1213 * that when VINF_GETOPT_NOT_OPTION is returned, the remainder of pArg->argv are all arguments. */ 1214 1215 1215 default: 1216 1216 return errorSyntax(USAGE_GUESTCONTROL, "Too many parameters specified, only source and destination allowed!"); 1217 /* Never reached. */1218 break;1219 1217 } 1220 1218 idxNonOption++; … … 1320 1318 AssertPtrReturn(pArg, VERR_INVALID_PARAMETER); 1321 1319 1320 /* 1321 * Parse arguments. 1322 * 1323 * Note! No direct returns here, everyone must go thru the cleanup at the 1324 * end of this function. 1325 */ 1322 1326 static const RTGETOPTDEF s_aOptions[] = 1323 1327 { … … 1337 1341 Utf8Str Utf8Password; 1338 1342 uint32_t fFlags = CreateDirectoryFlag_None; 1339 uint32_t uMode = 0;1343 uint32_t fDirMode = 0; /* 0 == default mode, right? */ 1340 1344 bool fVerbose = false; 1341 1345 … … 1344 1348 RTListInit(&listDirs); 1345 1349 1346 int vrc = VINF_SUCCESS; 1347 bool fUsageOK = true; 1350 RTEXITCODE rcExit = RTEXITCODE_SUCCESS; 1348 1351 while ( (ch = RTGetOpt(&GetState, &ValueUnion)) 1349 && RT_SUCCESS(vrc))1352 && rcExit == RTEXITCODE_SUCCESS) 1350 1353 { 1351 1354 /* For options that require an argument, ValueUnion has received the value. */ … … 1353 1356 { 1354 1357 case 'm': /* Mode */ 1355 uMode = ValueUnion.u32;1358 fDirMode = ValueUnion.u32; 1356 1359 break; 1357 1360 … … 1374 1377 case VINF_GETOPT_NOT_OPTION: 1375 1378 { 1376 vrc = ctrlDirectoryEntryAppend(NULL, /* No source given */ 1377 ValueUnion.psz, /* Destination */ 1378 &listDirs); 1379 /** @todo r=bird: Simplify by letting RTGetOptInit sort the argv, just like 1380 * for cp. */ 1381 int vrc = ctrlDirectoryEntryAppend(NULL, /* No source given */ 1382 ValueUnion.psz, /* Destination */ 1383 &listDirs); 1379 1384 if (RT_SUCCESS(vrc)) 1380 1385 { 1381 1386 cDirs++; 1382 1387 if (cDirs == UINT32_MAX) 1383 { 1384 RTMsgError("Too many directories specified! Aborting.\n"); 1385 vrc = VERR_TOO_MUCH_DATA; 1386 } 1388 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Too many directories specified! Aborting."); 1387 1389 } 1390 else 1391 rcExit = RTMsgErrorExit(RTEXITCODE_SYNTAX, "Failed to append directory: %Rrc", vrc); 1388 1392 break; 1389 1393 } 1390 1394 1391 1395 default: 1392 return RTGetOptPrintError(ch, &ValueUnion); 1393 } 1394 } 1395 1396 if (!fUsageOK) 1397 return errorSyntax(USAGE_GUESTCONTROL, "Incorrect parameters"); 1398 1399 if (!cDirs) 1400 return errorSyntax(USAGE_GUESTCONTROL, 1401 "No directory to create specified!"); 1402 1403 if (Utf8UserName.isEmpty()) 1404 return errorSyntax(USAGE_GUESTCONTROL, 1405 "No user name specified!"); 1406 1407 HRESULT rc = S_OK; 1408 if (fVerbose && cDirs > 1) 1409 RTPrintf("Creating %u directories ...\n", cDirs); 1410 1411 PDIRECTORYENTRY pNode; 1412 RTListForEach(&listDirs, pNode, DIRECTORYENTRY, Node) 1413 { 1414 if (fVerbose) 1415 RTPrintf("Creating directory \"%s\" ...\n", pNode->pszDestPath); 1416 1417 ComPtr<IProgress> progress; 1418 rc = guest->CreateDirectory(Bstr(pNode->pszDestPath).raw(), 1419 Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(), 1420 uMode, fFlags, progress.asOutParam()); 1421 if (FAILED(rc)) 1422 { 1423 ctrlPrintError(guest, COM_IIDOF(IGuest)); /* (return code ignored, save original rc) */ 1424 break; 1425 } 1426 } 1396 rcExit = RTGetOptPrintError(ch, &ValueUnion); 1397 break; 1398 } 1399 } 1400 1401 if (rcExit == RTEXITCODE_SUCCESS && !cDirs) 1402 rcExit = errorSyntax(USAGE_GUESTCONTROL, "No directory to create specified!"); 1403 1404 if (rcExit == RTEXITCODE_SUCCESS && Utf8UserName.isEmpty()) 1405 rcExit = errorSyntax(USAGE_GUESTCONTROL, "No user name specified!"); 1406 1407 if (rcExit == RTEXITCODE_SUCCESS) 1408 { 1409 /* 1410 * Create the directories. 1411 */ 1412 HRESULT hrc = S_OK; 1413 if (fVerbose && cDirs > 1) 1414 RTPrintf("Creating %u directories ...\n", cDirs); 1415 1416 PDIRECTORYENTRY pNode; 1417 RTListForEach(&listDirs, pNode, DIRECTORYENTRY, Node) 1418 { 1419 if (fVerbose) 1420 RTPrintf("Creating directory \"%s\" ...\n", pNode->pszDestPath); 1421 1422 ComPtr<IProgress> progress; 1423 hrc = guest->CreateDirectory(Bstr(pNode->pszDestPath).raw(), 1424 Bstr(Utf8UserName).raw(), Bstr(Utf8Password).raw(), 1425 fDirMode, fFlags, progress.asOutParam()); 1426 if (FAILED(hrc)) 1427 { 1428 ctrlPrintError(guest, COM_IIDOF(IGuest)); /* (return code ignored, save original rc) */ 1429 break; 1430 } 1431 } 1432 if (FAILED(hrc)) 1433 rcExit = RTEXITCODE_FAILURE; 1434 } 1435 1436 /* 1437 * Clean up and return. 1438 */ 1427 1439 ctrlDirectoryListDestroy(&listDirs); 1428 return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE; 1440 1441 return rcExit; 1429 1442 } 1430 1443
Note:
See TracChangeset
for help on using the changeset viewer.