Changeset 55535 in vbox for trunk/src/VBox/Frontends/VBoxManage
- Timestamp:
- Apr 30, 2015 2:13:56 AM (10 years ago)
- svn:sync-xref-src-repo-rev:
- 99898
- Location:
- trunk/src/VBox/Frontends/VBoxManage
- Files:
-
- 2 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Frontends/VBoxManage/VBoxManage.h
r55182 r55535 111 111 112 112 #ifdef VBOX_WITH_GUEST_CONTROL 113 # define USAGE_GSTCTRL_EXEC RT_BIT(0) 114 # define USAGE_GSTCTRL_COPYFROM RT_BIT(1) 115 # define USAGE_GSTCTRL_COPYTO RT_BIT(2) 116 # define USAGE_GSTCTRL_CREATEDIR RT_BIT(3) 117 # define USAGE_GSTCTRL_REMOVEDIR RT_BIT(4) 118 # define USAGE_GSTCTRL_REMOVEFILE RT_BIT(5) 119 # define USAGE_GSTCTRL_RENAME RT_BIT(6) 120 # define USAGE_GSTCTRL_CREATETEMP RT_BIT(7) 121 # define USAGE_GSTCTRL_LIST RT_BIT(8) 122 # define USAGE_GSTCTRL_PROCESS RT_BIT(9) 123 # define USAGE_GSTCTRL_KILL RT_BIT(10) 124 # define USAGE_GSTCTRL_SESSION RT_BIT(11) 125 # define USAGE_GSTCTRL_STAT RT_BIT(12) 126 # define USAGE_GSTCTRL_UPDATEADDS RT_BIT(13) 127 # define USAGE_GSTCTRL_WATCH RT_BIT(14) 113 # define USAGE_GSTCTRL_RUN RT_BIT(0) 114 # define USAGE_GSTCTRL_START RT_BIT(1) 115 # define USAGE_GSTCTRL_COPYFROM RT_BIT(2) 116 # define USAGE_GSTCTRL_COPYTO RT_BIT(3) 117 # define USAGE_GSTCTRL_CREATEDIR RT_BIT(4) 118 # define USAGE_GSTCTRL_REMOVEDIR RT_BIT(5) 119 # define USAGE_GSTCTRL_REMOVEFILE RT_BIT(6) 120 # define USAGE_GSTCTRL_RENAME RT_BIT(7) 121 # define USAGE_GSTCTRL_CREATETEMP RT_BIT(8) 122 # define USAGE_GSTCTRL_LIST RT_BIT(9) 123 # define USAGE_GSTCTRL_PROCESS RT_BIT(10) 124 # define USAGE_GSTCTRL_KILL RT_BIT(11) 125 # define USAGE_GSTCTRL_SESSION RT_BIT(12) 126 # define USAGE_GSTCTRL_STAT RT_BIT(13) 127 # define USAGE_GSTCTRL_UPDATEADDS RT_BIT(14) 128 # define USAGE_GSTCTRL_WATCH RT_BIT(15) 129 # define USAGE_GSTCTRL_EXEC RT_BIT(31) /**< @deprecated Remember to remove. */ 128 130 #endif 129 131 -
trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp
r55532 r55535 46 46 #include <iprt/process.h> /* For RTProcSelf(). */ 47 47 #include <iprt/thread.h> 48 #include <iprt/vfs.h> 48 49 49 50 #include <map> … … 218 219 typedef std::map< Utf8Str, std::vector<DESTFILEENTRY> >::iterator DESTDIRMAPITER, *PDESTDIRMAPITER; 219 220 220 /**221 * Special exit codes for returning errors/information of a222 * started guest process to the command line VBoxManage was started from.223 * Useful for e.g. scripting.224 *225 * @note These are frozen as of 4.1.0.226 */227 enum EXITCODEEXEC228 {229 EXITCODEEXEC_SUCCESS = RTEXITCODE_SUCCESS,230 /* Process exited normally but with an exit code <> 0. */231 EXITCODEEXEC_CODE = 16,232 EXITCODEEXEC_FAILED = 17,233 EXITCODEEXEC_TERM_SIGNAL = 18,234 EXITCODEEXEC_TERM_ABEND = 19,235 EXITCODEEXEC_TIMEOUT = 20,236 EXITCODEEXEC_DOWN = 21,237 EXITCODEEXEC_CANCELED = 22238 };239 221 240 222 /* … … 245 227 { 246 228 GETOPTDEF_COMMON_PASSWORD = 1000 229 }; 230 231 /** 232 * Long option IDs for the guestcontrol run command. 233 */ 234 enum kGstCtrlRunOpt 235 { 236 kGstCtrlRunOpt_IgnoreOrphanedProcesses = 1000, 237 kGstCtrlRunOpt_NoProfile, 238 kGstCtrlRunOpt_Dos2Unix, 239 kGstCtrlRunOpt_Unix2Dos, 240 kGstCtrlRunOpt_WaitForStdOut, 241 kGstCtrlRunOpt_NoWaitForStdOut, 242 kGstCtrlRunOpt_WaitForStdErr, 243 kGstCtrlRunOpt_NoWaitForStdErr 247 244 }; 248 245 … … 290 287 }; 291 288 292 enum OUTPUTTYPE293 { 294 OUTPUTTYPE_UNDEFINED= 0,295 OUTPUTTYPE_DOS2UNIX = 10,296 OUTPUTTYPE_UNIX2DOS = 20289 enum kStreamTransform 290 { 291 kStreamTransform_None = 0, 292 kStreamTransform_Dos2Unix, 293 kStreamTransform_Unix2Dos 297 294 }; 298 295 … … 307 304 pcszSep1, pcszSep2, 308 305 uSubCmd == ~0U ? "\n" : ""); 309 if (uSubCmd & USAGE_GSTCTRL_ EXEC)306 if (uSubCmd & USAGE_GSTCTRL_RUN) 310 307 RTStrmPrintf(pStrm, 311 " exec[ute]\n" 312 " --image <path to program> --username <name>\n" 313 " [--passwordfile <file> | --password <password>]\n" 314 " [--domain <domain>] [--verbose] [--timeout <msec>]\n" 315 " [--environment \"<NAME>=<VALUE> [<NAME>=<VALUE>]\"]\n" 316 " [--wait-exit] [--wait-stdout] [--wait-stderr]\n" 317 " [--dos2unix] [--unquoted-args] [--unix2dos]\n" 318 " [-- [<argument1>] ... [<argumentN>]]\n" 308 " run\n" 309 " [--image <path to executable>] --username <name>\n" 310 " [--passwordfile <file> | --password <password>]\n" 311 " [--domain <domain>] [--verbose] [--timeout <msec>]\n" 312 " [--setenv <NAME>=<VALUE>] [--unset <NAME>]\n" 313 " [--unquoted-args] [--no-wait-stdout|--wait-stdout]\n" 314 " [--no-wait-stderr|--wait-stderr]\n" 315 " [--dos2unix] [--unix2dos]\n" 316 " [--] <program/arg0> [argument1] ... [argumentN]]\n" 317 "\n"); 318 if (uSubCmd & USAGE_GSTCTRL_START) 319 RTStrmPrintf(pStrm, 320 " start\n" 321 " [--image <path to executable>] --username <name>\n" 322 " [--passwordfile <file> | --password <password>]\n" 323 " [--domain <domain>] [--verbose] [--timeout <msec>]\n" 324 " [--setenv <NAME>=<VALUE>] [--unset <NAME>]\n" 325 " [--unquoted-args] [--dos2unix] [--unix2dos]\n" 326 " [--] <program/arg0> [argument1] ... [argumentN]]\n" 327 "\n"); 328 if (uSubCmd == USAGE_GSTCTRL_EXEC) 329 RTStrmPrintf(pStrm, 330 " {DEPRECATED} exec[ute]\n" 331 " {DEPRECATED} --image <path to program> --username <name>\n" 332 " {DEPRECATED} [--passwordfile <file> | --password <password>]\n" 333 " {DEPRECATED} [--domain <domain>] [--verbose] [--timeout <msec>]\n" 334 " {DEPRECATED} [--environment \"<NAME>=<VALUE> [<NAME>=<VALUE>]\"]\n" 335 " {DEPRECATED} [--wait-exit] [--wait-stdout] [--wait-stderr]\n" 336 " {DEPRECATED} [--dos2unix] [--unquoted-args] [--unix2dos]\n" 337 " {DEPRECATED} [-- [<argument1>] ... [<argumentN>]]\n" 319 338 "\n"); 320 339 if (uSubCmd & USAGE_GSTCTRL_COPYFROM) … … 537 556 } 538 557 539 static int ctrlExecProcessStatusToExitCode(ProcessStatus_T enmStatus, ULONG uExitCode) 540 { 541 int vrc = EXITCODEEXEC_SUCCESS; 558 const char *ctrlProcessWaitResultToText(ProcessWaitResult_T enmWaitResult) 559 { 560 switch (enmWaitResult) 561 { 562 case ProcessWaitResult_Start: 563 return "started"; 564 case ProcessWaitResult_Terminate: 565 return "terminated"; 566 case ProcessWaitResult_Status: 567 return "status changed"; 568 case ProcessWaitResult_Error: 569 return "error"; 570 case ProcessWaitResult_Timeout: 571 return "timed out"; 572 case ProcessWaitResult_StdIn: 573 return "stdin ready"; 574 case ProcessWaitResult_StdOut: 575 return "data on stdout"; 576 case ProcessWaitResult_StdErr: 577 return "data on stderr"; 578 case ProcessWaitResult_WaitFlagNotSupported: 579 return "waiting flag not supported"; 580 default: 581 break; 582 } 583 return "unknown"; 584 } 585 586 /** 587 * Translates a guest session status to a human readable 588 * string. 589 */ 590 const char *ctrlSessionStatusToText(GuestSessionStatus_T enmStatus) 591 { 542 592 switch (enmStatus) 543 593 { 594 case GuestSessionStatus_Starting: 595 return "starting"; 596 case GuestSessionStatus_Started: 597 return "started"; 598 case GuestSessionStatus_Terminating: 599 return "terminating"; 600 case GuestSessionStatus_Terminated: 601 return "terminated"; 602 case GuestSessionStatus_TimedOutKilled: 603 return "timed out"; 604 case GuestSessionStatus_TimedOutAbnormally: 605 return "timed out, hanging"; 606 case GuestSessionStatus_Down: 607 return "killed"; 608 case GuestSessionStatus_Error: 609 return "error"; 610 default: 611 break; 612 } 613 return "unknown"; 614 } 615 616 /** 617 * Translates a guest file status to a human readable 618 * string. 619 */ 620 const char *ctrlFileStatusToText(FileStatus_T enmStatus) 621 { 622 switch (enmStatus) 623 { 624 case FileStatus_Opening: 625 return "opening"; 626 case FileStatus_Open: 627 return "open"; 628 case FileStatus_Closing: 629 return "closing"; 630 case FileStatus_Closed: 631 return "closed"; 632 case FileStatus_Down: 633 return "killed"; 634 case FileStatus_Error: 635 return "error"; 636 default: 637 break; 638 } 639 return "unknown"; 640 } 641 642 static int ctrlPrintError(com::ErrorInfo &errorInfo) 643 { 644 if ( errorInfo.isFullAvailable() 645 || errorInfo.isBasicAvailable()) 646 { 647 /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way 648 * because it contains more accurate info about what went wrong. */ 649 if (errorInfo.getResultCode() == VBOX_E_IPRT_ERROR) 650 RTMsgError("%ls.", errorInfo.getText().raw()); 651 else 652 { 653 RTMsgError("Error details:"); 654 GluePrintErrorInfo(errorInfo); 655 } 656 return VERR_GENERAL_FAILURE; /** @todo */ 657 } 658 AssertMsgFailedReturn(("Object has indicated no error (%Rhrc)!?\n", errorInfo.getResultCode()), 659 VERR_INVALID_PARAMETER); 660 } 661 662 static int ctrlPrintError(IUnknown *pObj, const GUID &aIID) 663 { 664 com::ErrorInfo ErrInfo(pObj, aIID); 665 return ctrlPrintError(ErrInfo); 666 } 667 668 static int ctrlPrintProgressError(ComPtr<IProgress> pProgress) 669 { 670 int vrc = VINF_SUCCESS; 671 HRESULT rc; 672 673 do 674 { 675 BOOL fCanceled; 676 CHECK_ERROR_BREAK(pProgress, COMGETTER(Canceled)(&fCanceled)); 677 if (!fCanceled) 678 { 679 LONG rcProc; 680 CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&rcProc)); 681 if (FAILED(rcProc)) 682 { 683 com::ProgressErrorInfo ErrInfo(pProgress); 684 vrc = ctrlPrintError(ErrInfo); 685 } 686 } 687 688 } while(0); 689 690 AssertMsgStmt(SUCCEEDED(rc), ("Could not lookup progress information\n"), vrc = VERR_COM_UNEXPECTED); 691 692 return vrc; 693 } 694 695 /** 696 * Un-initializes the VM after guest control usage. 697 * @param pCmdCtx Pointer to command context. 698 * @param uFlags Command context flags. 699 */ 700 static void ctrlUninitVM(PGCTLCMDCTX pCtx, uint32_t uFlags) 701 { 702 AssertPtrReturnVoid(pCtx); 703 704 if (!(pCtx->uFlags & CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER)) 705 ctrlSignalHandlerUninstall(); 706 707 HRESULT rc; 708 709 do 710 { 711 if (!pCtx->pGuestSession.isNull()) 712 { 713 if ( !(pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS) 714 && !(pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_DETACH)) 715 { 716 if (pCtx->fVerbose) 717 RTPrintf("Closing guest session ...\n"); 718 719 CHECK_ERROR(pCtx->pGuestSession, Close()); 720 /* Keep going - don't break here. Try to unlock the 721 * machine down below. */ 722 } 723 else if ( (pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_DETACH) 724 && pCtx->fVerbose) 725 RTPrintf("Guest session detached\n"); 726 727 pCtx->pGuestSession.setNull(); 728 } 729 730 if (pCtx->handlerArg.session) 731 CHECK_ERROR(pCtx->handlerArg.session, UnlockMachine()); 732 733 } while (0); 734 735 for (int i = 0; i < pCtx->iArgc; i++) 736 RTStrFree(pCtx->ppaArgv[i]); 737 RTMemFree(pCtx->ppaArgv); 738 pCtx->iArgc = 0; 739 } 740 741 /** 742 * Initializes the VM for IGuest operations. 743 * 744 * That is, checks whether it's up and running, if it can be locked (shared 745 * only) and returns a valid IGuest pointer on success. Also, it does some 746 * basic command line processing and opens a guest session, if required. 747 * 748 * @return RTEXITCODE status code. 749 * @param pArg Pointer to command line argument structure. 750 * @param pCmdCtx Pointer to command context. 751 * @param uFlags Command context flags. 752 */ 753 static RTEXITCODE ctrlInitVM(HandlerArg *pArg, 754 PGCTLCMDCTX pCtx, uint32_t uFlags, uint32_t uUsage) 755 { 756 AssertPtrReturn(pArg, RTEXITCODE_FAILURE); 757 AssertReturn(pArg->argc > 1, RTEXITCODE_FAILURE); 758 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); 759 760 #ifdef DEBUG_andy 761 RTPrintf("Original argv:\n"); 762 for (int i=0; i<pArg->argc;i++) 763 RTPrintf("\targv[%d]=%s\n", i, pArg->argv[i]); 764 #endif 765 766 RTEXITCODE rcExit = RTEXITCODE_SUCCESS; 767 768 const char *pszNameOrId = pArg->argv[0]; 769 const char *pszCmd = pArg->argv[1]; 770 771 /* Lookup VM. */ 772 ComPtr<IMachine> machine; 773 /* Assume it's an UUID. */ 774 HRESULT rc; 775 CHECK_ERROR(pArg->virtualBox, FindMachine(Bstr(pszNameOrId).raw(), 776 machine.asOutParam())); 777 if (SUCCEEDED(rc)) 778 { 779 /* Machine is running? */ 780 MachineState_T machineState; 781 CHECK_ERROR(machine, COMGETTER(State)(&machineState)); 782 if ( SUCCEEDED(rc) 783 && (machineState != MachineState_Running)) 784 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine \"%s\" is not running (currently %s)!\n", 785 pszNameOrId, machineStateToName(machineState, false)); 786 } 787 else 788 rcExit = RTEXITCODE_FAILURE; 789 790 if (rcExit == RTEXITCODE_SUCCESS) 791 { 792 /* 793 * Process standard options which are served by all commands. 794 */ 795 static const RTGETOPTDEF s_aOptions[] = 796 { 797 { "--username", 'u', RTGETOPT_REQ_STRING }, 798 { "--passwordfile", 'p', RTGETOPT_REQ_STRING }, 799 { "--password", GETOPTDEF_COMMON_PASSWORD, RTGETOPT_REQ_STRING }, 800 { "--domain", 'd', RTGETOPT_REQ_STRING }, 801 { "--verbose", 'v', RTGETOPT_REQ_NOTHING } 802 }; 803 804 805 806 /** @todo r=bird: This is just SOOOO hackish and fragile, especially wrt to the 807 * exec/run commands. If you don't have the full option syntax, there is no way 808 * you can safely tell an option from an option value. sigh. 809 * The way to do this is by a defines with the above s_aOptions entries, calling 810 * a common function in the default case of each option parser, and a routine to 811 * be called at the end of option parsing to open the session. */ 812 813 814 815 /* 816 * Allocate per-command argv. This then only contains the specific arguments 817 * the command needs. 818 */ 819 pCtx->ppaArgv = (char**)RTMemAlloc(pArg->argc * sizeof(char*) + 1); 820 if (!pCtx->ppaArgv) 821 { 822 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Not enough memory for per-command argv\n"); 823 } 824 else 825 { 826 pCtx->iArgc = 0; 827 828 int iArgIdx = 2; /* Skip VM name and guest control command */ 829 int ch; 830 RTGETOPTUNION ValueUnion; 831 RTGETOPTSTATE GetState; 832 RTGetOptInit(&GetState, pArg->argc, pArg->argv, 833 s_aOptions, RT_ELEMENTS(s_aOptions), 834 iArgIdx, 0); 835 836 while ( (ch = RTGetOpt(&GetState, &ValueUnion)) 837 && (rcExit == RTEXITCODE_SUCCESS)) 838 { 839 /* For options that require an argument, ValueUnion has received the value. */ 840 switch (ch) 841 { 842 case 'u': /* User name */ 843 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) 844 pCtx->strUsername = ValueUnion.psz; 845 iArgIdx = GetState.iNext; 846 break; 847 848 case GETOPTDEF_COMMON_PASSWORD: /* Password */ 849 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) 850 { 851 if (pCtx->strPassword.isEmpty()) 852 pCtx->strPassword = ValueUnion.psz; 853 } 854 iArgIdx = GetState.iNext; 855 break; 856 857 case 'p': /* Password file */ 858 { 859 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) 860 rcExit = readPasswordFile(ValueUnion.psz, &pCtx->strPassword); 861 iArgIdx = GetState.iNext; 862 break; 863 } 864 865 case 'd': /* domain */ 866 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) 867 pCtx->strDomain = ValueUnion.psz; 868 iArgIdx = GetState.iNext; 869 break; 870 871 case 'v': /* Verbose */ 872 pCtx->fVerbose = true; 873 iArgIdx = GetState.iNext; 874 break; 875 876 case 'h': /* Help */ 877 errorGetOptEx(USAGE_GUESTCONTROL, uUsage, ch, &ValueUnion); 878 return RTEXITCODE_SYNTAX; 879 880 default: 881 /* Simply skip; might be handled in a specific command 882 * handler later. */ 883 break; 884 885 } /* switch */ 886 887 int iArgDiff = GetState.iNext - iArgIdx; 888 if (iArgDiff) 889 { 890 #ifdef DEBUG_andy 891 RTPrintf("Not handled (iNext=%d, iArgsCur=%d):\n", GetState.iNext, iArgIdx); 892 #endif 893 for (int i = iArgIdx; i < GetState.iNext; i++) 894 { 895 char *pszArg = RTStrDup(pArg->argv[i]); 896 if (!pszArg) 897 { 898 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, 899 "Not enough memory for command line handling\n"); 900 break; 901 } 902 pCtx->ppaArgv[pCtx->iArgc] = pszArg; 903 pCtx->iArgc++; 904 #ifdef DEBUG_andy 905 RTPrintf("\targv[%d]=%s\n", i, pArg->argv[i]); 906 #endif 907 } 908 909 iArgIdx = GetState.iNext; 910 } 911 912 } /* while RTGetOpt */ 913 } 914 } 915 916 /* 917 * Check for mandatory stuff. 918 */ 919 if (rcExit == RTEXITCODE_SUCCESS) 920 { 921 #if 0 922 RTPrintf("argc=%d\n", pCtx->iArgc); 923 for (int i = 0; i < pCtx->iArgc; i++) 924 RTPrintf("argv[%d]=%s\n", i, pCtx->ppaArgv[i]); 925 #endif 926 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) 927 { 928 if (pCtx->strUsername.isEmpty()) 929 rcExit = errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, "No user name specified!"); 930 } 931 } 932 933 if (rcExit == RTEXITCODE_SUCCESS) 934 { 935 /* 936 * Build up a reasonable guest session name. Useful for identifying 937 * a specific session when listing / searching for them. 938 */ 939 char *pszSessionName; 940 if (0 >= RTStrAPrintf(&pszSessionName, 941 "[%RU32] VBoxManage Guest Control [%s] - %s", 942 RTProcSelf(), pszNameOrId, pszCmd)) 943 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No enough memory for session name\n"); 944 945 do 946 { 947 /* Open a session for the VM. */ 948 CHECK_ERROR_BREAK(machine, LockMachine(pArg->session, LockType_Shared)); 949 /* Get the associated console. */ 950 ComPtr<IConsole> console; 951 CHECK_ERROR_BREAK(pArg->session, COMGETTER(Console)(console.asOutParam())); 952 /* ... and session machine. */ 953 ComPtr<IMachine> sessionMachine; 954 CHECK_ERROR_BREAK(pArg->session, COMGETTER(Machine)(sessionMachine.asOutParam())); 955 /* Get IGuest interface. */ 956 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pCtx->pGuest.asOutParam())); 957 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)) 958 { 959 if (pCtx->fVerbose) 960 RTPrintf("Opening guest session as user '%s' ...\n", pCtx->strUsername.c_str()); 961 962 /* Open a guest session. */ 963 Assert(!pCtx->pGuest.isNull()); 964 CHECK_ERROR_BREAK(pCtx->pGuest, CreateSession(Bstr(pCtx->strUsername).raw(), 965 Bstr(pCtx->strPassword).raw(), 966 Bstr(pCtx->strDomain).raw(), 967 Bstr(pszSessionName).raw(), 968 pCtx->pGuestSession.asOutParam())); 969 970 /* 971 * Wait for guest session to start. 972 */ 973 if (pCtx->fVerbose) 974 RTPrintf("Waiting for guest session to start ...\n"); 975 976 com::SafeArray<GuestSessionWaitForFlag_T> aSessionWaitFlags; 977 aSessionWaitFlags.push_back(GuestSessionWaitForFlag_Start); 978 GuestSessionWaitResult_T sessionWaitResult; 979 CHECK_ERROR_BREAK(pCtx->pGuestSession, WaitForArray(ComSafeArrayAsInParam(aSessionWaitFlags), 980 /** @todo Make session handling timeouts configurable. */ 981 30 * 1000, &sessionWaitResult)); 982 983 if ( sessionWaitResult == GuestSessionWaitResult_Start 984 /* Note: This might happen when Guest Additions < 4.3 are installed which don't 985 * support dedicated guest sessions. */ 986 || sessionWaitResult == GuestSessionWaitResult_WaitFlagNotSupported) 987 { 988 CHECK_ERROR_BREAK(pCtx->pGuestSession, COMGETTER(Id)(&pCtx->uSessionID)); 989 if (pCtx->fVerbose) 990 RTPrintf("Guest session (ID %RU32) has been started\n", pCtx->uSessionID); 991 } 992 else 993 { 994 GuestSessionStatus_T sessionStatus; 995 CHECK_ERROR_BREAK(pCtx->pGuestSession, COMGETTER(Status)(&sessionStatus)); 996 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Error starting guest session (current status is: %s)\n", 997 ctrlSessionStatusToText(sessionStatus)); 998 break; 999 } 1000 } 1001 1002 if ( SUCCEEDED(rc) 1003 && !(uFlags & CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER)) 1004 { 1005 ctrlSignalHandlerInstall(); 1006 } 1007 1008 } while (0); 1009 1010 if (FAILED(rc)) 1011 rcExit = RTEXITCODE_FAILURE; 1012 1013 RTStrFree(pszSessionName); 1014 } 1015 1016 if (rcExit == RTEXITCODE_SUCCESS) 1017 { 1018 pCtx->handlerArg = *pArg; 1019 pCtx->uFlags = uFlags; 1020 } 1021 else /* Clean up on failure. */ 1022 ctrlUninitVM(pCtx, uFlags); 1023 1024 return rcExit; 1025 } 1026 1027 1028 /** @name EXITCODEEXEC_XXX - Special run exit codes. 1029 * 1030 * Special exit codes for returning errors/information of a started guest 1031 * process to the command line VBoxManage was started from. Useful for e.g. 1032 * scripting. 1033 * 1034 * ASSUMING that all platforms have at least 7-bits for the exit code we can do 1035 * the following mapping: 1036 * - Guest exit code 0 is mapped to 0 on the host. 1037 * - Guest exit codes 1 thru 93 (0x5d) are displaced by 32, so that 1 1038 * becomes 33 (0x21) on the host and 93 becomes 125 (0x7d) on the host. 1039 * - Guest exit codes 94 (0x5e) and above are mapped to 126 (0x5e). 1040 * 1041 * We ASSUME that all VBoxManage status codes are in the range 0 thru 32. 1042 * 1043 * @note These are frozen as of 4.1.0. 1044 * @note The guest exit code mappings was introduced with 5.0 and the 'run' 1045 * command, they are/was not supported by 'exec'. 1046 * @sa ctrlRunCalculateExitCode 1047 */ 1048 /** Process exited normally but with an exit code <> 0. */ 1049 #define EXITCODEEXEC_CODE ((RTEXITCODE)16) 1050 #define EXITCODEEXEC_FAILED ((RTEXITCODE)17) 1051 #define EXITCODEEXEC_TERM_SIGNAL ((RTEXITCODE)18) 1052 #define EXITCODEEXEC_TERM_ABEND ((RTEXITCODE)19) 1053 #define EXITCODEEXEC_TIMEOUT ((RTEXITCODE)20) 1054 #define EXITCODEEXEC_DOWN ((RTEXITCODE)21) 1055 /** Execution was interrupt by user (ctrl-c). */ 1056 #define EXITCODEEXEC_CANCELED ((RTEXITCODE)22) 1057 /** The first mapped guest (non-zero) exit code. */ 1058 #define EXITCODEEXEC_MAPPED_FIRST 33 1059 /** The last mapped guest (non-zero) exit code value (inclusive). */ 1060 #define EXITCODEEXEC_MAPPED_LAST 125 1061 /** The number of exit codes from EXITCODEEXEC_MAPPED_FIRST to 1062 * EXITCODEEXEC_MAPPED_LAST. This is also the highest guest exit code number 1063 * we're able to map. */ 1064 #define EXITCODEEXEC_MAPPED_RANGE (93) 1065 /** The guest exit code displacement value. */ 1066 #define EXITCODEEXEC_MAPPED_DISPLACEMENT 32 1067 /** The guest exit code was too big to be mapped. */ 1068 #define EXITCODEEXEC_MAPPED_BIG ((RTEXITCODE)126) 1069 /** @} */ 1070 1071 /** 1072 * Calculates the exit code of VBoxManage. 1073 * 1074 * @returns The exit code to return. 1075 * @param enmStatus The guest process status. 1076 * @param uExitCode The associated guest process exit code (where 1077 * applicable). 1078 * @param fReturnExitCodes Set if we're to use the 32-126 range for guest 1079 * exit codes. 1080 */ 1081 static RTEXITCODE ctrlRunCalculateExitCode(ProcessStatus_T enmStatus, ULONG uExitCode, bool fReturnExitCodes) 1082 { 1083 int vrc = RTEXITCODE_SUCCESS; 1084 switch (enmStatus) 1085 { 1086 case ProcessStatus_TerminatedNormally: 1087 if (uExitCode == 0) 1088 return RTEXITCODE_SUCCESS; 1089 if (!fReturnExitCodes) 1090 return EXITCODEEXEC_CODE; 1091 if (uExitCode <= EXITCODEEXEC_MAPPED_RANGE) 1092 return (RTEXITCODE) (uExitCode + EXITCODEEXEC_MAPPED_DISPLACEMENT); 1093 return EXITCODEEXEC_MAPPED_BIG; 1094 1095 case ProcessStatus_TerminatedAbnormally: 1096 return EXITCODEEXEC_TERM_ABEND; 1097 case ProcessStatus_TerminatedSignal: 1098 return EXITCODEEXEC_TERM_SIGNAL; 1099 1100 #if 0 /* see caller! */ 1101 case ProcessStatus_TimedOutKilled: 1102 return EXITCODEEXEC_TIMEOUT; 1103 case ProcessStatus_Down: 1104 return EXITCODEEXEC_DOWN; /* Service/OS is stopping, process was killed. */ 1105 case ProcessStatus_Error: 1106 return EXITCODEEXEC_FAILED; 1107 1108 /* The following is probably for detached? */ 544 1109 case ProcessStatus_Starting: 545 vrc = EXITCODEEXEC_SUCCESS; 1110 return RTEXITCODE_SUCCESS; 1111 case ProcessStatus_Started: 1112 return RTEXITCODE_SUCCESS; 1113 case ProcessStatus_Paused: 1114 return RTEXITCODE_SUCCESS; 1115 case ProcessStatus_Terminating: 1116 return RTEXITCODE_SUCCESS; /** @todo ???? */ 1117 #endif 1118 1119 default: 1120 AssertMsgFailed(("Unknown exit status (%u/%u) from guest process returned!\n", enmStatus, uExitCode)); 1121 return RTEXITCODE_FAILURE; 1122 } 1123 } 1124 1125 1126 /** 1127 * Pumps guest output to the host. 1128 * 1129 * @return IPRT status code. 1130 * @param pProcess Pointer to appropriate process object. 1131 * @param hVfsIosDst Where to write the data. 1132 * @param uHandle Handle where to read the data from. 1133 * @param cMsTimeout Timeout (in ms) to wait for the operation to 1134 * complete. 1135 */ 1136 static int ctrlRunPumpOutput(IProcess *pProcess, RTVFSIOSTREAM hVfsIosDst, ULONG uHandle, RTMSINTERVAL cMsTimeout) 1137 { 1138 AssertPtrReturn(pProcess, VERR_INVALID_POINTER); 1139 Assert(hVfsIosDst != NIL_RTVFSIOSTREAM); 1140 1141 int vrc; 1142 1143 SafeArray<BYTE> aOutputData; 1144 HRESULT hrc = pProcess->Read(uHandle, _64K, RT_MAX(cMsTimeout, 1), ComSafeArrayAsOutParam(aOutputData)); 1145 if (SUCCEEDED(hrc)) 1146 { 1147 size_t cbOutputData = aOutputData.size(); 1148 if (cbOutputData == 0) 1149 vrc = VINF_SUCCESS; 1150 else 1151 { 1152 BYTE const *pbBuf = aOutputData.raw(); 1153 AssertPtr(pbBuf); 1154 1155 vrc = RTVfsIoStrmWrite(hVfsIosDst, pbBuf, cbOutputData, true /*fBlocking*/, NULL); 1156 if (RT_FAILURE(vrc)) 1157 RTMsgError("Unable to write output, rc=%Rrc\n", vrc); 1158 } 1159 } 1160 else 1161 vrc = ctrlPrintError(pProcess, COM_IIDOF(IProcess)); 1162 return vrc; 1163 } 1164 1165 1166 /** 1167 * Configures a host handle for pumping guest bits. 1168 * 1169 * @returns true if enabled and we successfully configured it. 1170 * @param fEnabled Whether pumping this pipe is configured. 1171 * @param enmHandle The IPRT standard handle designation. 1172 * @param pszName The name for user messages. 1173 * @param enmTransformation The transformation to apply. 1174 * @param phVfsIos Where to return the resulting I/O stream handle. 1175 */ 1176 static bool ctrlRunSetupHandle(bool fEnabled, RTHANDLESTD enmHandle, const char *pszName, 1177 kStreamTransform enmTransformation, PRTVFSIOSTREAM phVfsIos) 1178 { 1179 if (fEnabled) 1180 { 1181 int vrc = RTVfsIoStrmFromStdHandle(enmHandle, 0, true /*fLeaveOpen*/, phVfsIos); 1182 if (RT_SUCCESS(vrc)) 1183 { 1184 if (enmTransformation != kStreamTransform_None) 1185 { 1186 RTMsgWarning("Unsupported %s line ending conversion", pszName); 1187 /** @todo Implement dos2unix and unix2dos stream filters. */ 1188 } 1189 return true; 1190 } 1191 RTMsgWarning("Error getting %s handle: %Rrc", pszName, vrc); 1192 } 1193 return false; 1194 } 1195 1196 1197 /** 1198 * Returns the remaining time (in ms) based on the start time and a set 1199 * timeout value. Returns RT_INDEFINITE_WAIT if no timeout was specified. 1200 * 1201 * @return RTMSINTERVAL Time left (in ms). 1202 * @param u64StartMs Start time (in ms). 1203 * @param cMsTimeout Timeout value (in ms). 1204 */ 1205 static RTMSINTERVAL ctrlExecGetRemainingTime(uint64_t u64StartMs, RTMSINTERVAL cMsTimeout) 1206 { 1207 if (!cMsTimeout || cMsTimeout == RT_INDEFINITE_WAIT) /* If no timeout specified, wait forever. */ 1208 return RT_INDEFINITE_WAIT; 1209 1210 uint64_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs; 1211 if (u64ElapsedMs >= cMsTimeout) 1212 return 0; 1213 1214 return cMsTimeout - (RTMSINTERVAL)u64ElapsedMs; 1215 } 1216 1217 /** 1218 * Common handler for the 'run' and 'start' commands. 1219 * 1220 * @returns Command exit code. 1221 * @param pCtx Guest session context. 1222 * @param fRunCmd Set if it's 'run' clear if 'start'. 1223 * @param fHelp The help flag for the command. 1224 */ 1225 static RTEXITCODE handleCtrlProcessRunCommon(PGCTLCMDCTX pCtx, bool fRunCmd, uint32_t fHelp) 1226 { 1227 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); 1228 1229 /* 1230 * Parse arguments. 1231 */ 1232 static const RTGETOPTDEF s_aOptions[] = 1233 { 1234 { "--environment", 'E', RTGETOPT_REQ_STRING }, 1235 { "--executable", 'e', RTGETOPT_REQ_STRING }, 1236 { "--timeout", 't', RTGETOPT_REQ_UINT32 }, 1237 { "--unquoted-args", 'u', RTGETOPT_REQ_NOTHING }, 1238 { "--ignore-operhaned-processes", kGstCtrlRunOpt_IgnoreOrphanedProcesses, RTGETOPT_REQ_NOTHING }, 1239 { "--no-profile", kGstCtrlRunOpt_NoProfile, RTGETOPT_REQ_NOTHING }, 1240 { "--dos2unix", kGstCtrlRunOpt_Dos2Unix, RTGETOPT_REQ_NOTHING }, 1241 { "--unix2dos", kGstCtrlRunOpt_Unix2Dos, RTGETOPT_REQ_NOTHING }, 1242 { "--no-wait-stdout", kGstCtrlRunOpt_NoWaitForStdOut, RTGETOPT_REQ_NOTHING }, 1243 { "--wait-stdout", kGstCtrlRunOpt_WaitForStdOut, RTGETOPT_REQ_NOTHING }, 1244 { "--no-wait-stderr", kGstCtrlRunOpt_NoWaitForStdErr, RTGETOPT_REQ_NOTHING }, 1245 { "--wait-stderr", kGstCtrlRunOpt_WaitForStdErr, RTGETOPT_REQ_NOTHING }, 1246 }; 1247 1248 int ch; 1249 RTGETOPTUNION ValueUnion; 1250 RTGETOPTSTATE GetState; 1251 RTGetOptInit(&GetState, pCtx->iArgc, pCtx->ppaArgv, s_aOptions, RT_ELEMENTS(s_aOptions), 1252 pCtx->iFirstArgc, RTGETOPTINIT_FLAGS_OPTS_FIRST); 1253 1254 com::SafeArray<ProcessCreateFlag_T> aCreateFlags; 1255 com::SafeArray<ProcessWaitForFlag_T> aWaitFlags; 1256 com::SafeArray<IN_BSTR> aArgs; 1257 com::SafeArray<IN_BSTR> aEnv; 1258 const char * pszImage = NULL; 1259 bool fDetached = false; 1260 bool fWaitForStdOut = true; 1261 bool fWaitForStdErr = true; 1262 RTVFSIOSTREAM hVfsStdOut = NIL_RTVFSIOSTREAM; 1263 RTVFSIOSTREAM hVfsStdErr = NIL_RTVFSIOSTREAM; 1264 enum kStreamTransform enmStdOutTransform = kStreamTransform_None; 1265 enum kStreamTransform enmStdErrTransform = kStreamTransform_None; 1266 RTMSINTERVAL cMsTimeout = 0; 1267 1268 try 1269 { 1270 /* Wait for process start in any case. This is useful for scripting VBoxManage 1271 * when relying on its overall exit code. */ 1272 aWaitFlags.push_back(ProcessWaitForFlag_Start); 1273 1274 while ((ch = RTGetOpt(&GetState, &ValueUnion)) != 0) 1275 { 1276 /* For options that require an argument, ValueUnion has received the value. */ 1277 switch (ch) 1278 { 1279 case 'E': 1280 if ( ValueUnion.psz[0] == '\0' 1281 || ValueUnion.psz[0] == '=' 1282 || strchr(ValueUnion.psz, '=') == NULL) 1283 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, 1284 "Invalid argument variable=value pair: '%s'", ValueUnion.psz); 1285 aEnv.push_back(Bstr(ValueUnion.psz).raw()); 1286 break; 1287 1288 case kGstCtrlRunOpt_IgnoreOrphanedProcesses: 1289 aCreateFlags.push_back(ProcessCreateFlag_IgnoreOrphanedProcesses); 1290 break; 1291 1292 case kGstCtrlRunOpt_NoProfile: 1293 aCreateFlags.push_back(ProcessCreateFlag_NoProfile); 1294 break; 1295 1296 case 'i': 1297 pszImage = ValueUnion.psz; 1298 break; 1299 1300 case 'u': 1301 aCreateFlags.push_back(ProcessCreateFlag_UnquotedArguments); 1302 break; 1303 1304 /** @todo Add a hidden flag. */ 1305 1306 case 't': /* Timeout */ 1307 cMsTimeout = ValueUnion.u32; 1308 break; 1309 1310 case kGstCtrlRunOpt_Dos2Unix: 1311 enmStdErrTransform = enmStdOutTransform = kStreamTransform_Dos2Unix; 1312 break; 1313 case kGstCtrlRunOpt_Unix2Dos: 1314 enmStdErrTransform = enmStdOutTransform = kStreamTransform_Unix2Dos; 1315 break; 1316 1317 case kGstCtrlRunOpt_WaitForStdOut: 1318 if (!fRunCmd) 1319 return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stdout"); 1320 fWaitForStdOut = true; 1321 break; 1322 case kGstCtrlRunOpt_NoWaitForStdOut: 1323 if (!fRunCmd) 1324 return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --no-wait-for-stdout"); 1325 fWaitForStdOut = false; 1326 break; 1327 1328 case kGstCtrlRunOpt_WaitForStdErr: 1329 if (!fRunCmd) 1330 return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stderr"); 1331 fWaitForStdErr = true; 1332 break; 1333 case kGstCtrlRunOpt_NoWaitForStdErr: 1334 if (!fRunCmd) 1335 return errorSyntaxEx(USAGE_GUESTCONTROL, fHelp, "Invalid option --wait-for-stderr"); 1336 fWaitForStdErr = false; 1337 break; 1338 1339 case VINF_GETOPT_NOT_OPTION: 1340 aArgs.push_back(Bstr(ValueUnion.psz).raw()); 1341 if (!pszImage) 1342 { 1343 Assert(aArgs.size() == 1); 1344 pszImage = ValueUnion.psz; 1345 } 1346 break; 1347 1348 default: 1349 return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, ch, &ValueUnion); 1350 1351 } /* switch */ 1352 } /* while RTGetOpt */ 1353 1354 /* Must have something to execute. */ 1355 if (!pszImage || *pszImage) 1356 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, "No executable specified!"); 1357 1358 /* 1359 * Finalize process creation and wait flags and input/output streams. 1360 */ 1361 if (!fRunCmd) 1362 { 1363 aCreateFlags.push_back(ProcessCreateFlag_WaitForProcessStartOnly); 1364 Assert(!fWaitForStdOut); 1365 Assert(!fWaitForStdErr); 1366 } 1367 else 1368 { 1369 aWaitFlags.push_back(ProcessWaitForFlag_Terminate); 1370 fWaitForStdOut = ctrlRunSetupHandle(fWaitForStdOut, RTHANDLESTD_OUTPUT, "stdout", enmStdOutTransform, &hVfsStdOut); 1371 if (fWaitForStdOut) 1372 { 1373 aCreateFlags.push_back(ProcessCreateFlag_WaitForStdOut); 1374 aWaitFlags.push_back(ProcessWaitForFlag_StdOut); 1375 } 1376 fWaitForStdErr = ctrlRunSetupHandle(fWaitForStdErr, RTHANDLESTD_ERROR, "stderr", enmStdErrTransform, &hVfsStdErr); 1377 if (fWaitForStdErr) 1378 { 1379 aCreateFlags.push_back(ProcessCreateFlag_WaitForStdErr); 1380 aWaitFlags.push_back(ProcessWaitForFlag_StdErr); 1381 } 1382 } 1383 } 1384 catch (std::bad_alloc &) 1385 { 1386 return RTMsgErrorExit(RTEXITCODE_FAILURE, "VERR_NO_MEMORY\n"); 1387 } 1388 1389 RTEXITCODE rcExit = RTEXITCODE_SUCCESS; 1390 HRESULT rc; 1391 1392 try 1393 { 1394 do 1395 { 1396 /* Get current time stamp to later calculate rest of timeout left. */ 1397 uint64_t msStart = RTTimeMilliTS(); 1398 1399 /* 1400 * Create the process. 1401 */ 1402 if (pCtx->fVerbose) 1403 { 1404 if (cMsTimeout == 0) 1405 RTPrintf("Starting guest process ...\n"); 1406 else 1407 RTPrintf("Starting guest process (within %ums)\n", cMsTimeout); 1408 } 1409 ComPtr<IGuestProcess> pProcess; 1410 CHECK_ERROR_BREAK(pCtx->pGuestSession, ProcessCreate(Bstr(pszImage).raw(), 1411 ComSafeArrayAsInParam(aArgs), 1412 ComSafeArrayAsInParam(aEnv), 1413 ComSafeArrayAsInParam(aCreateFlags), 1414 ctrlExecGetRemainingTime(msStart, cMsTimeout), 1415 pProcess.asOutParam())); 1416 1417 /* 1418 * Explicitly wait for the guest process to be in a started state. 1419 */ 1420 com::SafeArray<ProcessWaitForFlag_T> aWaitStartFlags; 1421 aWaitStartFlags.push_back(ProcessWaitForFlag_Start); 1422 ProcessWaitResult_T waitResult; 1423 CHECK_ERROR_BREAK(pProcess, WaitForArray(ComSafeArrayAsInParam(aWaitStartFlags), 1424 ctrlExecGetRemainingTime(msStart, cMsTimeout), &waitResult)); 1425 1426 ULONG uPID = 0; 1427 CHECK_ERROR_BREAK(pProcess, COMGETTER(PID)(&uPID)); 1428 if (fRunCmd && pCtx->fVerbose) 1429 RTPrintf("Process '%s' (PID %RU32) started\n", pszImage, uPID); 1430 else if (!fRunCmd) /** @todo Introduce a --quiet option for not printing this. */ 1431 { 1432 /* Just print plain PID to make it easier for scripts 1433 * invoking VBoxManage. */ 1434 RTPrintf("[%RU32 - Session %RU32]\n", uPID, pCtx->uSessionID); 1435 } 1436 1437 /* 1438 * Wait for process to exit/start... 1439 */ 1440 RTMSINTERVAL cMsTimeLeft = 1; /* Will be calculated. */ 1441 bool fReadStdOut = false; 1442 bool fReadStdErr = false; 1443 bool fCompleted = false; 1444 bool fCompletedStartCmd = false; 1445 int vrc = VINF_SUCCESS; 1446 1447 while ( !fCompleted 1448 && cMsTimeLeft > 0) 1449 { 1450 cMsTimeLeft = ctrlExecGetRemainingTime(msStart, cMsTimeout); 1451 CHECK_ERROR_BREAK(pProcess, WaitForArray(ComSafeArrayAsInParam(aWaitFlags), 1452 RT_MIN(500 /*ms*/, RT_MAX(cMsTimeLeft, 1 /*ms*/)), 1453 &waitResult)); 1454 switch (waitResult) 1455 { 1456 case ProcessWaitResult_Start: 1457 fCompletedStartCmd = fCompleted = !fRunCmd; /* Only wait for startup if the 'start' command. */ 1458 break; 1459 case ProcessWaitResult_StdOut: 1460 fReadStdOut = true; 1461 break; 1462 case ProcessWaitResult_StdErr: 1463 fReadStdErr = true; 1464 break; 1465 case ProcessWaitResult_Terminate: 1466 if (pCtx->fVerbose) 1467 RTPrintf("Process terminated\n"); 1468 /* Process terminated, we're done. */ 1469 fCompleted = true; 1470 break; 1471 case ProcessWaitResult_WaitFlagNotSupported: 1472 /* The guest does not support waiting for stdout/err, so 1473 * yield to reduce the CPU load due to busy waiting. */ 1474 RTThreadYield(); 1475 fReadStdOut = fReadStdErr = true; 1476 break; 1477 case ProcessWaitResult_Timeout: 1478 { 1479 /** @todo It is really unclear whether we will get stuck with the timeout 1480 * result here if the guest side times out the process and fails to 1481 * kill the process... To be on the save side, double the IPC and 1482 * check the process status every time we time out. */ 1483 ProcessStatus_T enmProcStatus; 1484 CHECK_ERROR_BREAK(pProcess, COMGETTER(Status)(&enmProcStatus)); 1485 if ( enmProcStatus == ProcessStatus_TimedOutKilled 1486 || enmProcStatus == ProcessStatus_TimedOutAbnormally) 1487 fCompleted = true; 1488 fReadStdOut = fReadStdErr = true; 1489 break; 1490 } 1491 case ProcessWaitResult_Status: 1492 /* ignore. */ 1493 break; 1494 case ProcessWaitResult_Error: 1495 /* waitFor is dead in the water, I think, so better leave the loop. */ 1496 vrc = VERR_CALLBACK_RETURN; 1497 break; 1498 1499 case ProcessWaitResult_StdIn: AssertFailed(); /* did ask for this! */ break; 1500 case ProcessWaitResult_None: AssertFailed(); /* used. */ break; 1501 default: AssertFailed(); /* huh? */ break; 1502 } 1503 1504 if (g_fGuestCtrlCanceled) 1505 break; 1506 1507 /* 1508 * Pump output as needed. 1509 */ 1510 if (fReadStdOut) 1511 { 1512 cMsTimeLeft = ctrlExecGetRemainingTime(msStart, cMsTimeout); 1513 int vrc2 = ctrlRunPumpOutput(pProcess, hVfsStdOut, 1 /* StdOut */, cMsTimeLeft); 1514 if (RT_FAILURE(vrc2) && RT_SUCCESS(vrc)) 1515 vrc = vrc2; 1516 fReadStdOut = false; 1517 } 1518 if (fReadStdErr) 1519 { 1520 cMsTimeLeft = ctrlExecGetRemainingTime(msStart, cMsTimeout); 1521 int vrc2 = ctrlRunPumpOutput(pProcess, hVfsStdErr, 2 /* StdErr */, cMsTimeLeft); 1522 if (RT_FAILURE(vrc2) && RT_SUCCESS(vrc)) 1523 vrc = vrc2; 1524 fReadStdErr = false; 1525 } 1526 if ( RT_FAILURE(vrc) 1527 || g_fGuestCtrlCanceled) 1528 break; 1529 1530 /* 1531 * Process events before looping. 1532 */ 1533 NativeEventQueue::getMainEventQueue()->processEventQueue(0); 1534 } /* while */ 1535 1536 /* 1537 * Report status back to the user. 1538 */ 1539 if (g_fGuestCtrlCanceled) 1540 { 1541 if (pCtx->fVerbose) 1542 RTPrintf("Process execution aborted!\n"); 1543 rcExit = EXITCODEEXEC_CANCELED; 1544 } 1545 else if (fCompletedStartCmd) 1546 { 1547 if (pCtx->fVerbose) 1548 RTPrintf("Process successfully started!\n"); 1549 rcExit = RTEXITCODE_SUCCESS; 1550 } 1551 else if (fCompleted) 1552 { 1553 ProcessStatus_T procStatus; 1554 CHECK_ERROR_BREAK(pProcess, COMGETTER(Status)(&procStatus)); 1555 if ( procStatus == ProcessStatus_TerminatedNormally 1556 || procStatus == ProcessStatus_TerminatedAbnormally 1557 || procStatus == ProcessStatus_TerminatedSignal) 1558 { 1559 LONG lExitCode; 1560 CHECK_ERROR_BREAK(pProcess, COMGETTER(ExitCode)(&lExitCode)); 1561 if (pCtx->fVerbose) 1562 RTPrintf("Exit code=%u (Status=%u [%s])\n", 1563 lExitCode, procStatus, ctrlProcessStatusToText(procStatus)); 1564 1565 rcExit = ctrlRunCalculateExitCode(procStatus, lExitCode, true /*fReturnExitCodes*/); 1566 } 1567 else if ( procStatus == ProcessStatus_TimedOutKilled 1568 || procStatus == ProcessStatus_TimedOutAbnormally) 1569 { 1570 if (pCtx->fVerbose) 1571 RTPrintf("Process timed out (guest side) and\n", 1572 procStatus == ProcessStatus_TimedOutAbnormally 1573 ? " failed to terminate so far" : " was terminated"); 1574 rcExit = EXITCODEEXEC_TIMEOUT; 1575 } 1576 else 1577 { 1578 if (pCtx->fVerbose) 1579 RTPrintf("Process now is in status [%s] (unexpected)\n", ctrlProcessStatusToText(procStatus)); 1580 rcExit = RTEXITCODE_FAILURE; 1581 } 1582 } 1583 else if (RT_FAILURE_NP(vrc)) 1584 { 1585 if (pCtx->fVerbose) 1586 RTPrintf("Process monitor loop quit with vrc=%Rrc\n", vrc); 1587 rcExit = RTEXITCODE_FAILURE; 1588 } 1589 else 1590 { 1591 if (pCtx->fVerbose) 1592 RTPrintf("Process monitor loop timed out\n"); 1593 rcExit = EXITCODEEXEC_TIMEOUT; 1594 } 1595 1596 } while (0); 1597 } 1598 catch (std::bad_alloc) 1599 { 1600 rc = E_OUTOFMEMORY; 1601 } 1602 1603 /* 1604 * Decide what to do with the guest session. 1605 * 1606 * If it's the 'start' command where detach the guest process after 1607 * starting, don't close the guest session it is part of, except on 1608 * failure or ctrl-c. 1609 * 1610 * For the 'run' command the guest process quits with us. 1611 */ 1612 if (!fRunCmd && SUCCEEDED(rc) && !g_fGuestCtrlCanceled) 1613 pCtx->uFlags |= CTLCMDCTX_FLAGS_SESSION_DETACH; 1614 1615 /* Make sure we return failure on failure. */ 1616 if (FAILED(rc) && rcExit == RTEXITCODE_SUCCESS) 1617 rcExit = RTEXITCODE_FAILURE; 1618 return rcExit; 1619 } 1620 1621 1622 static DECLCALLBACK(RTEXITCODE) handleCtrlProcessRun(PGCTLCMDCTX pCtx) 1623 { 1624 return handleCtrlProcessRunCommon(pCtx, true /*fRunCmd*/, USAGE_GSTCTRL_RUN); 1625 } 1626 1627 1628 static DECLCALLBACK(RTEXITCODE) handleCtrlProcessStart(PGCTLCMDCTX pCtx) 1629 { 1630 return handleCtrlProcessRunCommon(pCtx, false /*fRunCmd*/, USAGE_GSTCTRL_START); 1631 } 1632 1633 #if 1 /* Old exec code. */ 1634 1635 static int ctrlExecProcessStatusToExitCodeDeprecated(ProcessStatus_T enmStatus, ULONG uExitCode) 1636 { 1637 int vrc = RTEXITCODE_SUCCESS; 1638 switch (enmStatus) 1639 { 1640 case ProcessStatus_Starting: 1641 vrc = RTEXITCODE_SUCCESS; 546 1642 break; 547 1643 case ProcessStatus_Started: 548 vrc = EXITCODEEXEC_SUCCESS;1644 vrc = RTEXITCODE_SUCCESS; 549 1645 break; 550 1646 case ProcessStatus_Paused: 551 vrc = EXITCODEEXEC_SUCCESS;1647 vrc = RTEXITCODE_SUCCESS; 552 1648 break; 553 1649 case ProcessStatus_Terminating: 554 vrc = EXITCODEEXEC_SUCCESS;1650 vrc = RTEXITCODE_SUCCESS; 555 1651 break; 556 1652 case ProcessStatus_TerminatedNormally: 557 vrc = !uExitCode ? EXITCODEEXEC_SUCCESS : EXITCODEEXEC_CODE;1653 vrc = !uExitCode ? RTEXITCODE_SUCCESS : EXITCODEEXEC_CODE; 558 1654 break; 559 1655 case ProcessStatus_TerminatedSignal: … … 584 1680 } 585 1681 586 const char *ctrlProcessWaitResultToText(ProcessWaitResult_T enmWaitResult)587 {588 switch (enmWaitResult)589 {590 case ProcessWaitResult_Start:591 return "started";592 case ProcessWaitResult_Terminate:593 return "terminated";594 case ProcessWaitResult_Status:595 return "status changed";596 case ProcessWaitResult_Error:597 return "error";598 case ProcessWaitResult_Timeout:599 return "timed out";600 case ProcessWaitResult_StdIn:601 return "stdin ready";602 case ProcessWaitResult_StdOut:603 return "data on stdout";604 case ProcessWaitResult_StdErr:605 return "data on stderr";606 case ProcessWaitResult_WaitFlagNotSupported:607 return "waiting flag not supported";608 default:609 break;610 }611 return "unknown";612 }613 614 /**615 * Translates a guest session status to a human readable616 * string.617 */618 const char *ctrlSessionStatusToText(GuestSessionStatus_T enmStatus)619 {620 switch (enmStatus)621 {622 case GuestSessionStatus_Starting:623 return "starting";624 case GuestSessionStatus_Started:625 return "started";626 case GuestSessionStatus_Terminating:627 return "terminating";628 case GuestSessionStatus_Terminated:629 return "terminated";630 case GuestSessionStatus_TimedOutKilled:631 return "timed out";632 case GuestSessionStatus_TimedOutAbnormally:633 return "timed out, hanging";634 case GuestSessionStatus_Down:635 return "killed";636 case GuestSessionStatus_Error:637 return "error";638 default:639 break;640 }641 return "unknown";642 }643 644 /**645 * Translates a guest file status to a human readable646 * string.647 */648 const char *ctrlFileStatusToText(FileStatus_T enmStatus)649 {650 switch (enmStatus)651 {652 case FileStatus_Opening:653 return "opening";654 case FileStatus_Open:655 return "open";656 case FileStatus_Closing:657 return "closing";658 case FileStatus_Closed:659 return "closed";660 case FileStatus_Down:661 return "killed";662 case FileStatus_Error:663 return "error";664 default:665 break;666 }667 return "unknown";668 }669 670 static int ctrlPrintError(com::ErrorInfo &errorInfo)671 {672 if ( errorInfo.isFullAvailable()673 || errorInfo.isBasicAvailable())674 {675 /* If we got a VBOX_E_IPRT error we handle the error in a more gentle way676 * because it contains more accurate info about what went wrong. */677 if (errorInfo.getResultCode() == VBOX_E_IPRT_ERROR)678 RTMsgError("%ls.", errorInfo.getText().raw());679 else680 {681 RTMsgError("Error details:");682 GluePrintErrorInfo(errorInfo);683 }684 return VERR_GENERAL_FAILURE; /** @todo */685 }686 AssertMsgFailedReturn(("Object has indicated no error (%Rhrc)!?\n", errorInfo.getResultCode()),687 VERR_INVALID_PARAMETER);688 }689 690 static int ctrlPrintError(IUnknown *pObj, const GUID &aIID)691 {692 com::ErrorInfo ErrInfo(pObj, aIID);693 return ctrlPrintError(ErrInfo);694 }695 696 static int ctrlPrintProgressError(ComPtr<IProgress> pProgress)697 {698 int vrc = VINF_SUCCESS;699 HRESULT rc;700 701 do702 {703 BOOL fCanceled;704 CHECK_ERROR_BREAK(pProgress, COMGETTER(Canceled)(&fCanceled));705 if (!fCanceled)706 {707 LONG rcProc;708 CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&rcProc));709 if (FAILED(rcProc))710 {711 com::ProgressErrorInfo ErrInfo(pProgress);712 vrc = ctrlPrintError(ErrInfo);713 }714 }715 716 } while(0);717 718 AssertMsgStmt(SUCCEEDED(rc), ("Could not lookup progress information\n"), vrc = VERR_COM_UNEXPECTED);719 720 return vrc;721 }722 723 /**724 * Un-initializes the VM after guest control usage.725 * @param pCmdCtx Pointer to command context.726 * @param uFlags Command context flags.727 */728 static void ctrlUninitVM(PGCTLCMDCTX pCtx, uint32_t uFlags)729 {730 AssertPtrReturnVoid(pCtx);731 732 if (!(pCtx->uFlags & CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER))733 ctrlSignalHandlerUninstall();734 735 HRESULT rc;736 737 do738 {739 if (!pCtx->pGuestSession.isNull())740 {741 if ( !(pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS)742 && !(pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_DETACH))743 {744 if (pCtx->fVerbose)745 RTPrintf("Closing guest session ...\n");746 747 CHECK_ERROR(pCtx->pGuestSession, Close());748 /* Keep going - don't break here. Try to unlock the749 * machine down below. */750 }751 else if ( (pCtx->uFlags & CTLCMDCTX_FLAGS_SESSION_DETACH)752 && pCtx->fVerbose)753 RTPrintf("Guest session detached\n");754 755 pCtx->pGuestSession.setNull();756 }757 758 if (pCtx->handlerArg.session)759 CHECK_ERROR(pCtx->handlerArg.session, UnlockMachine());760 761 } while (0);762 763 for (int i = 0; i < pCtx->iArgc; i++)764 RTStrFree(pCtx->ppaArgv[i]);765 RTMemFree(pCtx->ppaArgv);766 pCtx->iArgc = 0;767 }768 769 /**770 * Initializes the VM for IGuest operations.771 *772 * That is, checks whether it's up and running, if it can be locked (shared773 * only) and returns a valid IGuest pointer on success. Also, it does some774 * basic command line processing and opens a guest session, if required.775 *776 * @return RTEXITCODE status code.777 * @param pArg Pointer to command line argument structure.778 * @param pCmdCtx Pointer to command context.779 * @param uFlags Command context flags.780 */781 static RTEXITCODE ctrlInitVM(HandlerArg *pArg,782 PGCTLCMDCTX pCtx, uint32_t uFlags, uint32_t uUsage)783 {784 AssertPtrReturn(pArg, RTEXITCODE_FAILURE);785 AssertReturn(pArg->argc > 1, RTEXITCODE_FAILURE);786 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);787 788 #ifdef DEBUG_andy789 RTPrintf("Original argv:\n");790 for (int i=0; i<pArg->argc;i++)791 RTPrintf("\targv[%d]=%s\n", i, pArg->argv[i]);792 #endif793 794 RTEXITCODE rcExit = RTEXITCODE_SUCCESS;795 796 const char *pszNameOrId = pArg->argv[0];797 const char *pszCmd = pArg->argv[1];798 799 /* Lookup VM. */800 ComPtr<IMachine> machine;801 /* Assume it's an UUID. */802 HRESULT rc;803 CHECK_ERROR(pArg->virtualBox, FindMachine(Bstr(pszNameOrId).raw(),804 machine.asOutParam()));805 if (SUCCEEDED(rc))806 {807 /* Machine is running? */808 MachineState_T machineState;809 CHECK_ERROR(machine, COMGETTER(State)(&machineState));810 if ( SUCCEEDED(rc)811 && (machineState != MachineState_Running))812 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Machine \"%s\" is not running (currently %s)!\n",813 pszNameOrId, machineStateToName(machineState, false));814 }815 else816 rcExit = RTEXITCODE_FAILURE;817 818 if (rcExit == RTEXITCODE_SUCCESS)819 {820 /*821 * Process standard options which are served by all commands.822 */823 static const RTGETOPTDEF s_aOptions[] =824 {825 { "--username", 'u', RTGETOPT_REQ_STRING },826 { "--passwordfile", 'p', RTGETOPT_REQ_STRING },827 { "--password", GETOPTDEF_COMMON_PASSWORD, RTGETOPT_REQ_STRING },828 { "--domain", 'd', RTGETOPT_REQ_STRING },829 { "--verbose", 'v', RTGETOPT_REQ_NOTHING }830 };831 832 /*833 * Allocate per-command argv. This then only contains the specific arguments834 * the command needs.835 */836 pCtx->ppaArgv = (char**)RTMemAlloc(pArg->argc * sizeof(char*) + 1);837 if (!pCtx->ppaArgv)838 {839 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Not enough memory for per-command argv\n");840 }841 else842 {843 pCtx->iArgc = 0;844 845 int iArgIdx = 2; /* Skip VM name and guest control command */846 int ch;847 RTGETOPTUNION ValueUnion;848 RTGETOPTSTATE GetState;849 RTGetOptInit(&GetState, pArg->argc, pArg->argv,850 s_aOptions, RT_ELEMENTS(s_aOptions),851 iArgIdx, 0);852 853 while ( (ch = RTGetOpt(&GetState, &ValueUnion))854 && (rcExit == RTEXITCODE_SUCCESS))855 {856 /* For options that require an argument, ValueUnion has received the value. */857 switch (ch)858 {859 case 'u': /* User name */860 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))861 pCtx->strUsername = ValueUnion.psz;862 iArgIdx = GetState.iNext;863 break;864 865 case GETOPTDEF_COMMON_PASSWORD: /* Password */866 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))867 {868 if (pCtx->strPassword.isEmpty())869 pCtx->strPassword = ValueUnion.psz;870 }871 iArgIdx = GetState.iNext;872 break;873 874 case 'p': /* Password file */875 {876 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))877 rcExit = readPasswordFile(ValueUnion.psz, &pCtx->strPassword);878 iArgIdx = GetState.iNext;879 break;880 }881 882 case 'd': /* domain */883 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))884 pCtx->strDomain = ValueUnion.psz;885 iArgIdx = GetState.iNext;886 break;887 888 case 'v': /* Verbose */889 pCtx->fVerbose = true;890 iArgIdx = GetState.iNext;891 break;892 893 case 'h': /* Help */894 errorGetOptEx(USAGE_GUESTCONTROL, uUsage, ch, &ValueUnion);895 return RTEXITCODE_SYNTAX;896 897 default:898 /* Simply skip; might be handled in a specific command899 * handler later. */900 break;901 902 } /* switch */903 904 int iArgDiff = GetState.iNext - iArgIdx;905 if (iArgDiff)906 {907 #ifdef DEBUG_andy908 RTPrintf("Not handled (iNext=%d, iArgsCur=%d):\n", GetState.iNext, iArgIdx);909 #endif910 for (int i = iArgIdx; i < GetState.iNext; i++)911 {912 char *pszArg = RTStrDup(pArg->argv[i]);913 if (!pszArg)914 {915 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE,916 "Not enough memory for command line handling\n");917 break;918 }919 pCtx->ppaArgv[pCtx->iArgc] = pszArg;920 pCtx->iArgc++;921 #ifdef DEBUG_andy922 RTPrintf("\targv[%d]=%s\n", i, pArg->argv[i]);923 #endif924 }925 926 iArgIdx = GetState.iNext;927 }928 929 } /* while RTGetOpt */930 }931 }932 933 /*934 * Check for mandatory stuff.935 */936 if (rcExit == RTEXITCODE_SUCCESS)937 {938 #if 0939 RTPrintf("argc=%d\n", pCtx->iArgc);940 for (int i = 0; i < pCtx->iArgc; i++)941 RTPrintf("argv[%d]=%s\n", i, pCtx->ppaArgv[i]);942 #endif943 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))944 {945 if (pCtx->strUsername.isEmpty())946 rcExit = errorSyntaxEx(USAGE_GUESTCONTROL, uUsage, "No user name specified!");947 }948 }949 950 if (rcExit == RTEXITCODE_SUCCESS)951 {952 /*953 * Build up a reasonable guest session name. Useful for identifying954 * a specific session when listing / searching for them.955 */956 char *pszSessionName;957 if (0 >= RTStrAPrintf(&pszSessionName,958 "[%RU32] VBoxManage Guest Control [%s] - %s",959 RTProcSelf(), pszNameOrId, pszCmd))960 return RTMsgErrorExit(RTEXITCODE_FAILURE, "No enough memory for session name\n");961 962 do963 {964 /* Open a session for the VM. */965 CHECK_ERROR_BREAK(machine, LockMachine(pArg->session, LockType_Shared));966 /* Get the associated console. */967 ComPtr<IConsole> console;968 CHECK_ERROR_BREAK(pArg->session, COMGETTER(Console)(console.asOutParam()));969 /* ... and session machine. */970 ComPtr<IMachine> sessionMachine;971 CHECK_ERROR_BREAK(pArg->session, COMGETTER(Machine)(sessionMachine.asOutParam()));972 /* Get IGuest interface. */973 CHECK_ERROR_BREAK(console, COMGETTER(Guest)(pCtx->pGuest.asOutParam()));974 if (!(uFlags & CTLCMDCTX_FLAGS_SESSION_ANONYMOUS))975 {976 if (pCtx->fVerbose)977 RTPrintf("Opening guest session as user '%s' ...\n", pCtx->strUsername.c_str());978 979 /* Open a guest session. */980 Assert(!pCtx->pGuest.isNull());981 CHECK_ERROR_BREAK(pCtx->pGuest, CreateSession(Bstr(pCtx->strUsername).raw(),982 Bstr(pCtx->strPassword).raw(),983 Bstr(pCtx->strDomain).raw(),984 Bstr(pszSessionName).raw(),985 pCtx->pGuestSession.asOutParam()));986 987 /*988 * Wait for guest session to start.989 */990 if (pCtx->fVerbose)991 RTPrintf("Waiting for guest session to start ...\n");992 993 com::SafeArray<GuestSessionWaitForFlag_T> aSessionWaitFlags;994 aSessionWaitFlags.push_back(GuestSessionWaitForFlag_Start);995 GuestSessionWaitResult_T sessionWaitResult;996 CHECK_ERROR_BREAK(pCtx->pGuestSession, WaitForArray(ComSafeArrayAsInParam(aSessionWaitFlags),997 /** @todo Make session handling timeouts configurable. */998 30 * 1000, &sessionWaitResult));999 1000 if ( sessionWaitResult == GuestSessionWaitResult_Start1001 /* Note: This might happen when Guest Additions < 4.3 are installed which don't1002 * support dedicated guest sessions. */1003 || sessionWaitResult == GuestSessionWaitResult_WaitFlagNotSupported)1004 {1005 CHECK_ERROR_BREAK(pCtx->pGuestSession, COMGETTER(Id)(&pCtx->uSessionID));1006 if (pCtx->fVerbose)1007 RTPrintf("Guest session (ID %RU32) has been started\n", pCtx->uSessionID);1008 }1009 else1010 {1011 GuestSessionStatus_T sessionStatus;1012 CHECK_ERROR_BREAK(pCtx->pGuestSession, COMGETTER(Status)(&sessionStatus));1013 rcExit = RTMsgErrorExit(RTEXITCODE_FAILURE, "Error starting guest session (current status is: %s)\n",1014 ctrlSessionStatusToText(sessionStatus));1015 break;1016 }1017 }1018 1019 if ( SUCCEEDED(rc)1020 && !(uFlags & CTLCMDCTX_FLAGS_NO_SIGNAL_HANDLER))1021 {1022 ctrlSignalHandlerInstall();1023 }1024 1025 } while (0);1026 1027 if (FAILED(rc))1028 rcExit = RTEXITCODE_FAILURE;1029 1030 RTStrFree(pszSessionName);1031 }1032 1033 if (rcExit == RTEXITCODE_SUCCESS)1034 {1035 pCtx->handlerArg = *pArg;1036 pCtx->uFlags = uFlags;1037 }1038 else /* Clean up on failure. */1039 ctrlUninitVM(pCtx, uFlags);1040 1041 return rcExit;1042 }1043 1682 1044 1683 /** … … 1049 1688 * @param pStrmOutput Where to write the data. 1050 1689 * @param uHandle Handle where to read the data from. 1051 * @param uTimeoutMS Timeout (in ms) to wait for the operation to complete. 1690 * @param cMsTimeout Timeout (in ms) to wait for the operation to 1691 * complete. 1692 * @remarks Obsolete. 1052 1693 */ 1053 static int ctrlExecPrintOutput (IProcess *pProcess, PRTSTREAM pStrmOutput,1054 ULONG uHandle, ULONG uTimeoutMS)1694 static int ctrlExecPrintOutputDeprecated(IProcess *pProcess, PRTSTREAM pStrmOutput, 1695 ULONG uHandle, RTMSINTERVAL cMsTimeout) 1055 1696 { 1056 1697 AssertPtrReturn(pProcess, VERR_INVALID_POINTER); … … 1060 1701 1061 1702 SafeArray<BYTE> aOutputData; 1062 HRESULT rc = pProcess->Read(uHandle, _64K, uTimeoutMS,1703 HRESULT rc = pProcess->Read(uHandle, _64K, cMsTimeout, 1063 1704 ComSafeArrayAsOutParam(aOutputData)); 1064 if (FAILED(rc)) 1065 vrc = ctrlPrintError(pProcess, COM_IIDOF(IProcess)); 1066 else 1705 if (SUCCEEDED(rc)) 1067 1706 { 1068 1707 size_t cbOutputData = aOutputData.size(); … … 1072 1711 AssertPtr(pBuf); 1073 1712 pBuf[cbOutputData - 1] = 0; /* Properly terminate buffer. */ 1074 1075 /** @todo implement the dos2unix/unix2dos conversions */1076 1713 1077 1714 /* … … 1088 1725 cbOutputData = strlen(pszBufUTF8); 1089 1726 1090 ULONG cbOutputDataPrint = cbOutputData;1727 ULONG cbOutputDataPrint = (ULONG)cbOutputData; 1091 1728 for (char *s = pszBufUTF8, *d = s; 1092 1729 s - pszBufUTF8 < (ssize_t)cbOutputData; … … 1113 1750 } 1114 1751 } 1115 1752 else 1753 vrc = ctrlPrintError(pProcess, COM_IIDOF(IProcess)); 1116 1754 return vrc; 1117 1755 } 1118 1756 1119 /** 1120 * Returns the remaining time (in ms) based on the start time and a set 1121 * timeout value. Returns RT_INDEFINITE_WAIT if no timeout was specified. 1122 * 1123 * @return RTMSINTERVAL Time left (in ms). 1124 * @param u64StartMs Start time (in ms). 1125 * @param cMsTimeout Timeout value (in ms). 1126 */ 1127 inline RTMSINTERVAL ctrlExecGetRemainingTime(uint64_t u64StartMs, RTMSINTERVAL cMsTimeout) 1128 { 1129 if (!cMsTimeout || cMsTimeout == RT_INDEFINITE_WAIT) /* If no timeout specified, wait forever. */ 1130 return RT_INDEFINITE_WAIT; 1131 1132 uint64_t u64ElapsedMs = RTTimeMilliTS() - u64StartMs; 1133 if (u64ElapsedMs >= cMsTimeout) 1134 return 0; 1135 1136 return cMsTimeout - (RTMSINTERVAL)u64ElapsedMs; 1137 } 1138 1139 static DECLCALLBACK(RTEXITCODE) handleCtrlProcessExec(PGCTLCMDCTX pCtx) 1757 1758 static DECLCALLBACK(RTEXITCODE) handleCtrlProcessExecDeprecated(PGCTLCMDCTX pCtx) 1140 1759 { 1141 1760 AssertPtrReturn(pCtx, RTEXITCODE_FAILURE); … … 1175 1794 com::SafeArray<ProcessCreateFlag_T> aCreateFlags; 1176 1795 com::SafeArray<ProcessWaitForFlag_T> aWaitFlags; 1177 com::SafeArray<IN_BSTR> aArgs; 1796 com::SafeArray<IN_BSTR> aArgsWithout0; 1797 com::SafeArray<IN_BSTR> aArgsWith0; 1178 1798 com::SafeArray<IN_BSTR> aEnv; 1179 1799 RTMSINTERVAL cMsTimeout = 0; 1180 OUTPUTTYPE eOutputType = OUTPUTTYPE_UNDEFINED;1181 1800 bool fDetached = true; 1182 1801 int vrc = VINF_SUCCESS; … … 1194 1813 switch (ch) 1195 1814 { 1196 case GETOPTDEF_EXEC_DOS2UNIX:1197 if (eOutputType != OUTPUTTYPE_UNDEFINED)1198 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC,1199 "More than one output type (dos2unix/unix2dos) specified!");1200 eOutputType = OUTPUTTYPE_DOS2UNIX;1201 break;1202 1203 1815 case 'e': /* Environment */ 1204 1816 { … … 1208 1820 vrc = RTGetOptArgvFromString(&papszArg, &cArgs, ValueUnion.psz, NULL); 1209 1821 if (RT_FAILURE(vrc)) 1210 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_ EXEC,1822 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, 1211 1823 "Failed to parse environment value, rc=%Rrc", vrc); 1212 1824 for (int j = 0; j < cArgs; j++) … … 1240 1852 1241 1853 case GETOPTDEF_EXEC_UNIX2DOS: 1242 if (eOutputType != OUTPUTTYPE_UNDEFINED) 1243 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC, 1244 "More than one output type (dos2unix/unix2dos) specified!"); 1245 eOutputType = OUTPUTTYPE_UNIX2DOS; 1246 break; 1854 case GETOPTDEF_EXEC_DOS2UNIX: 1855 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, 1856 "Output conversion not implemented yet!"); 1247 1857 1248 1858 case GETOPTDEF_EXEC_WAITFOREXIT: … … 1264 1874 1265 1875 case VINF_GETOPT_NOT_OPTION: 1266 if (aArgs .size() == 0 && strCmd.isEmpty())1876 if (aArgsWithout0.size() == 0 && strCmd.isEmpty()) 1267 1877 strCmd = ValueUnion.psz; 1268 1878 else 1269 aArgs .push_back(Bstr(ValueUnion.psz).raw());1879 aArgsWithout0.push_back(Bstr(ValueUnion.psz).raw()); 1270 1880 break; 1271 1881 … … 1274 1884 * contain a single dash, e.g. "-- foo.exe -s". */ 1275 1885 if (GetState.argc == GetState.iNext) 1276 aArgs .push_back(Bstr(ValueUnion.psz).raw());1886 aArgsWithout0.push_back(Bstr(ValueUnion.psz).raw()); 1277 1887 else 1278 return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_ EXEC, ch, &ValueUnion);1888 return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, ch, &ValueUnion); 1279 1889 break; 1280 1890 1281 1891 } /* switch */ 1282 1892 } /* while RTGetOpt */ 1893 1894 /* Create aArgsWith0 from strCmd and aArgsWithout0. */ 1895 aArgsWith0.push_back(Bstr(strCmd).raw()); 1896 for (size_t i = 0; i < aArgsWithout0.size(); i++) 1897 aArgsWith0.push_back(aArgsWithout0[i]); 1283 1898 } 1284 1899 catch (std::bad_alloc &) … … 1291 1906 1292 1907 if (strCmd.isEmpty()) 1293 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_ EXEC,1908 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, 1294 1909 "No command to execute specified!"); 1295 1296 /** @todo Any output conversion not supported yet! */1297 if (eOutputType != OUTPUTTYPE_UNDEFINED)1298 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC,1299 "Output conversion not implemented yet!");1300 1910 1301 1911 RTEXITCODE rcExit = RTEXITCODE_SUCCESS; … … 1326 1936 ComPtr<IGuestProcess> pProcess; 1327 1937 CHECK_ERROR_BREAK(pCtx->pGuestSession, ProcessCreate(Bstr(strCmd).raw(), 1328 ComSafeArrayAsInParam(aArgs ),1938 ComSafeArrayAsInParam(aArgsWith0), 1329 1939 ComSafeArrayAsInParam(aEnv), 1330 1940 ComSafeArrayAsInParam(aCreateFlags), … … 1421 2031 { 1422 2032 cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout); 1423 vrc = ctrlExecPrintOutput (pProcess, g_pStdOut,1424 1 /* StdOut */, cMsTimeLeft);2033 vrc = ctrlExecPrintOutputDeprecated(pProcess, g_pStdOut, 2034 1 /* StdOut */, cMsTimeLeft); 1425 2035 fReadStdOut = false; 1426 2036 } … … 1429 2039 { 1430 2040 cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout); 1431 vrc = ctrlExecPrintOutput (pProcess, g_pStdErr,1432 2 /* StdErr */, cMsTimeLeft);2041 vrc = ctrlExecPrintOutputDeprecated(pProcess, g_pStdErr, 2042 2 /* StdErr */, cMsTimeLeft); 1433 2043 fReadStdErr = false; 1434 2044 } … … 1467 2077 exitCode, procStatus, ctrlProcessStatusToText(procStatus)); 1468 2078 1469 rcExit = (RTEXITCODE)ctrlExecProcessStatusToExitCode (procStatus, exitCode);2079 rcExit = (RTEXITCODE)ctrlExecProcessStatusToExitCodeDeprecated(procStatus, exitCode); 1470 2080 } 1471 2081 else if (pCtx->fVerbose) … … 1526 2136 return rcExit; 1527 2137 } 2138 2139 #endif /* Old exec code. */ 1528 2140 1529 2141 /** … … 2718 3330 } 2719 3331 2720 uint32_t cDirs = mapDirs.size();3332 size_t cDirs = mapDirs.size(); 2721 3333 if (!cDirs) 2722 3334 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CREATEDIR, … … 2781 3393 } 2782 3394 2783 uint32_t cDirs = mapDirs.size();3395 size_t cDirs = mapDirs.size(); 2784 3396 if (!cDirs) 2785 3397 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_REMOVEDIR, … … 2864 3476 } 2865 3477 2866 uint32_t cFiles = mapDirs.size();3478 size_t cFiles = mapDirs.size(); 2867 3479 if (!cFiles) 2868 3480 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_REMOVEFILE, … … 2942 3554 return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize, rc=%Rrc\n", vrc); 2943 3555 2944 uint32_t cSources = vecSources.size();3556 size_t cSources = vecSources.size(); 2945 3557 if (!cSources) 2946 3558 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RENAME, … … 3179 3791 } 3180 3792 3181 uint32_t cObjs = mapObjs.size();3793 size_t cObjs = mapObjs.size(); 3182 3794 if (!cObjs) 3183 3795 return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_STAT, … … 3948 4560 || !RTStrICmp(pArg->argv[1], "execute")) 3949 4561 { 3950 gctlCmd.pfnHandler = handleCtrlProcessExec ;4562 gctlCmd.pfnHandler = handleCtrlProcessExecDeprecated; 3951 4563 uUsage = USAGE_GSTCTRL_EXEC; 4564 } 4565 else if (!strcmp(pArg->argv[1], "run")) 4566 { 4567 gctlCmd.pfnHandler = handleCtrlProcessRun; 4568 uUsage = USAGE_GSTCTRL_RUN; 4569 } 4570 else if (!strcmp(pArg->argv[1], "start")) 4571 { 4572 gctlCmd.pfnHandler = handleCtrlProcessStart; 4573 uUsage = USAGE_GSTCTRL_START; 3952 4574 } 3953 4575 else if (!RTStrICmp(pArg->argv[1], "copyfrom"))
Note:
See TracChangeset
for help on using the changeset viewer.