VirtualBox

Ignore:
Timestamp:
Apr 30, 2015 2:13:56 AM (10 years ago)
Author:
vboxsync
svn:sync-xref-src-repo-rev:
99898
Message:

Main,VBoxManage,VBoxShell,ValidationKit: Changed the IGuestSession:createProcess[Ex] interfaces to take argv[0] as input separate from the executable name. This is not yet implemented on the guest side.

Location:
trunk/src/VBox/Frontends/VBoxManage
Files:
2 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManage.h

    r55182 r55535  
    111111
    112112#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. */
    128130#endif
    129131
  • trunk/src/VBox/Frontends/VBoxManage/VBoxManageGuestCtrl.cpp

    r55532 r55535  
    4646#include <iprt/process.h> /* For RTProcSelf(). */
    4747#include <iprt/thread.h>
     48#include <iprt/vfs.h>
    4849
    4950#include <map>
     
    218219typedef std::map< Utf8Str, std::vector<DESTFILEENTRY> >::iterator DESTDIRMAPITER, *PDESTDIRMAPITER;
    219220
    220 /**
    221  * Special exit codes for returning errors/information of a
    222  * 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 EXITCODEEXEC
    228 {
    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       = 22
    238 };
    239221
    240222/*
     
    245227{
    246228    GETOPTDEF_COMMON_PASSWORD = 1000
     229};
     230
     231/**
     232 * Long option IDs for the guestcontrol run command.
     233 */
     234enum 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
    247244};
    248245
     
    290287};
    291288
    292 enum OUTPUTTYPE
    293 {
    294     OUTPUTTYPE_UNDEFINED = 0,
    295     OUTPUTTYPE_DOS2UNIX  = 10,
    296     OUTPUTTYPE_UNIX2DOS  = 20
     289enum kStreamTransform
     290{
     291    kStreamTransform_None = 0,
     292    kStreamTransform_Dos2Unix,
     293    kStreamTransform_Unix2Dos
    297294};
    298295
     
    307304                 pcszSep1, pcszSep2,
    308305                 uSubCmd == ~0U ? "\n" : "");
    309     if (uSubCmd & USAGE_GSTCTRL_EXEC)
     306    if (uSubCmd & USAGE_GSTCTRL_RUN)
    310307        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"
    319338                 "\n");
    320339    if (uSubCmd & USAGE_GSTCTRL_COPYFROM)
     
    537556}
    538557
    539 static int ctrlExecProcessStatusToExitCode(ProcessStatus_T enmStatus, ULONG uExitCode)
    540 {
    541     int vrc = EXITCODEEXEC_SUCCESS;
     558const 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 */
     590const char *ctrlSessionStatusToText(GuestSessionStatus_T enmStatus)
     591{
    542592    switch (enmStatus)
    543593    {
     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 */
     620const 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
     642static 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
     662static int ctrlPrintError(IUnknown *pObj, const GUID &aIID)
     663{
     664    com::ErrorInfo ErrInfo(pObj, aIID);
     665    return ctrlPrintError(ErrInfo);
     666}
     667
     668static 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 */
     700static 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 */
     753static 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 */
     1081static 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? */
    5441109        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 */
     1136static 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 */
     1176static 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 */
     1205static 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 */
     1225static 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
     1622static DECLCALLBACK(RTEXITCODE) handleCtrlProcessRun(PGCTLCMDCTX pCtx)
     1623{
     1624    return handleCtrlProcessRunCommon(pCtx, true /*fRunCmd*/, USAGE_GSTCTRL_RUN);
     1625}
     1626
     1627
     1628static DECLCALLBACK(RTEXITCODE) handleCtrlProcessStart(PGCTLCMDCTX pCtx)
     1629{
     1630    return handleCtrlProcessRunCommon(pCtx, false /*fRunCmd*/, USAGE_GSTCTRL_START);
     1631}
     1632
     1633#if 1 /* Old exec code. */
     1634
     1635static 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;
    5461642            break;
    5471643        case ProcessStatus_Started:
    548             vrc = EXITCODEEXEC_SUCCESS;
     1644            vrc = RTEXITCODE_SUCCESS;
    5491645            break;
    5501646        case ProcessStatus_Paused:
    551             vrc = EXITCODEEXEC_SUCCESS;
     1647            vrc = RTEXITCODE_SUCCESS;
    5521648            break;
    5531649        case ProcessStatus_Terminating:
    554             vrc = EXITCODEEXEC_SUCCESS;
     1650            vrc = RTEXITCODE_SUCCESS;
    5551651            break;
    5561652        case ProcessStatus_TerminatedNormally:
    557             vrc = !uExitCode ? EXITCODEEXEC_SUCCESS : EXITCODEEXEC_CODE;
     1653            vrc = !uExitCode ? RTEXITCODE_SUCCESS : EXITCODEEXEC_CODE;
    5581654            break;
    5591655        case ProcessStatus_TerminatedSignal:
     
    5841680}
    5851681
    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 readable
    616  * 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 readable
    646  * 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 way
    676          * 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         else
    680         {
    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     do
    702     {
    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     do
    738     {
    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 the
    749                  * 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 (shared
    773  * only) and returns a valid IGuest pointer on success. Also, it does some
    774  * 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_andy
    789     RTPrintf("Original argv:\n");
    790     for (int i=0; i<pArg->argc;i++)
    791         RTPrintf("\targv[%d]=%s\n", i, pArg->argv[i]);
    792 #endif
    793 
    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     else
    816         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 arguments
    834          * 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         else
    842         {
    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 command
    899                          * handler later. */
    900                         break;
    901 
    902                 } /* switch */
    903 
    904                 int iArgDiff = GetState.iNext - iArgIdx;
    905                 if (iArgDiff)
    906                 {
    907 #ifdef DEBUG_andy
    908                     RTPrintf("Not handled (iNext=%d, iArgsCur=%d):\n", GetState.iNext, iArgIdx);
    909 #endif
    910                     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_andy
    922                         RTPrintf("\targv[%d]=%s\n", i, pArg->argv[i]);
    923 #endif
    924                     }
    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 0
    939         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 #endif
    943         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 identifying
    954          * 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         do
    963         {
    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_Start
    1001                     /* Note: This might happen when Guest Additions < 4.3 are installed which don't
    1002                      *       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                 else
    1010                 {
    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 }
    10431682
    10441683/**
     
    10491688 * @param   pStrmOutput     Where to write the data.
    10501689 * @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.
    10521693 */
    1053 static int ctrlExecPrintOutput(IProcess *pProcess, PRTSTREAM pStrmOutput,
    1054                                ULONG uHandle, ULONG uTimeoutMS)
     1694static int ctrlExecPrintOutputDeprecated(IProcess *pProcess, PRTSTREAM pStrmOutput,
     1695                                         ULONG uHandle, RTMSINTERVAL cMsTimeout)
    10551696{
    10561697    AssertPtrReturn(pProcess, VERR_INVALID_POINTER);
     
    10601701
    10611702    SafeArray<BYTE> aOutputData;
    1062     HRESULT rc = pProcess->Read(uHandle, _64K, uTimeoutMS,
     1703    HRESULT rc = pProcess->Read(uHandle, _64K, cMsTimeout,
    10631704                                ComSafeArrayAsOutParam(aOutputData));
    1064     if (FAILED(rc))
    1065         vrc = ctrlPrintError(pProcess, COM_IIDOF(IProcess));
    1066     else
     1705    if (SUCCEEDED(rc))
    10671706    {
    10681707        size_t cbOutputData = aOutputData.size();
     
    10721711            AssertPtr(pBuf);
    10731712            pBuf[cbOutputData - 1] = 0; /* Properly terminate buffer. */
    1074 
    1075             /** @todo implement the dos2unix/unix2dos conversions */
    10761713
    10771714            /*
     
    10881725                cbOutputData = strlen(pszBufUTF8);
    10891726
    1090                 ULONG cbOutputDataPrint = cbOutputData;
     1727                ULONG cbOutputDataPrint = (ULONG)cbOutputData;
    10911728                for (char *s = pszBufUTF8, *d = s;
    10921729                     s - pszBufUTF8 < (ssize_t)cbOutputData;
     
    11131750        }
    11141751    }
    1115 
     1752    else
     1753        vrc = ctrlPrintError(pProcess, COM_IIDOF(IProcess));
    11161754    return vrc;
    11171755}
    11181756
    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
     1758static DECLCALLBACK(RTEXITCODE) handleCtrlProcessExecDeprecated(PGCTLCMDCTX pCtx)
    11401759{
    11411760    AssertPtrReturn(pCtx, RTEXITCODE_FAILURE);
     
    11751794    com::SafeArray<ProcessCreateFlag_T>  aCreateFlags;
    11761795    com::SafeArray<ProcessWaitForFlag_T> aWaitFlags;
    1177     com::SafeArray<IN_BSTR>              aArgs;
     1796    com::SafeArray<IN_BSTR>              aArgsWithout0;
     1797    com::SafeArray<IN_BSTR>              aArgsWith0;
    11781798    com::SafeArray<IN_BSTR>              aEnv;
    11791799    RTMSINTERVAL                         cMsTimeout      = 0;
    1180     OUTPUTTYPE                           eOutputType     = OUTPUTTYPE_UNDEFINED;
    11811800    bool                                 fDetached       = true;
    11821801    int                                  vrc             = VINF_SUCCESS;
     
    11941813            switch (ch)
    11951814            {
    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 
    12031815                case 'e': /* Environment */
    12041816                {
     
    12081820                    vrc = RTGetOptArgvFromString(&papszArg, &cArgs, ValueUnion.psz, NULL);
    12091821                    if (RT_FAILURE(vrc))
    1210                         return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC,
     1822                        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN,
    12111823                                             "Failed to parse environment value, rc=%Rrc", vrc);
    12121824                    for (int j = 0; j < cArgs; j++)
     
    12401852
    12411853                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!");
    12471857
    12481858                case GETOPTDEF_EXEC_WAITFOREXIT:
     
    12641874
    12651875                case VINF_GETOPT_NOT_OPTION:
    1266                     if (aArgs.size() == 0 && strCmd.isEmpty())
     1876                    if (aArgsWithout0.size() == 0 && strCmd.isEmpty())
    12671877                        strCmd = ValueUnion.psz;
    12681878                    else
    1269                         aArgs.push_back(Bstr(ValueUnion.psz).raw());
     1879                        aArgsWithout0.push_back(Bstr(ValueUnion.psz).raw());
    12701880                    break;
    12711881
     
    12741884                     *       contain a single dash, e.g. "-- foo.exe -s". */
    12751885                    if (GetState.argc == GetState.iNext)
    1276                         aArgs.push_back(Bstr(ValueUnion.psz).raw());
     1886                        aArgsWithout0.push_back(Bstr(ValueUnion.psz).raw());
    12771887                    else
    1278                         return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC, ch, &ValueUnion);
     1888                        return errorGetOptEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN, ch, &ValueUnion);
    12791889                    break;
    12801890
    12811891            } /* switch */
    12821892        } /* 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]);
    12831898    }
    12841899    catch (std::bad_alloc &)
     
    12911906
    12921907    if (strCmd.isEmpty())
    1293         return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_EXEC,
     1908        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RUN,
    12941909                             "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!");
    13001910
    13011911    RTEXITCODE rcExit = RTEXITCODE_SUCCESS;
     
    13261936            ComPtr<IGuestProcess> pProcess;
    13271937            CHECK_ERROR_BREAK(pCtx->pGuestSession, ProcessCreate(Bstr(strCmd).raw(),
    1328                                                                  ComSafeArrayAsInParam(aArgs),
     1938                                                                 ComSafeArrayAsInParam(aArgsWith0),
    13291939                                                                 ComSafeArrayAsInParam(aEnv),
    13301940                                                                 ComSafeArrayAsInParam(aCreateFlags),
     
    14212031                {
    14222032                    cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout);
    1423                     vrc = ctrlExecPrintOutput(pProcess, g_pStdOut,
    1424                                               1 /* StdOut */, cMsTimeLeft);
     2033                    vrc = ctrlExecPrintOutputDeprecated(pProcess, g_pStdOut,
     2034                                                        1 /* StdOut */, cMsTimeLeft);
    14252035                    fReadStdOut = false;
    14262036                }
     
    14292039                {
    14302040                    cMsTimeLeft = ctrlExecGetRemainingTime(u64StartMS, cMsTimeout);
    1431                     vrc = ctrlExecPrintOutput(pProcess, g_pStdErr,
    1432                                               2 /* StdErr */, cMsTimeLeft);
     2041                    vrc = ctrlExecPrintOutputDeprecated(pProcess, g_pStdErr,
     2042                                                        2 /* StdErr */, cMsTimeLeft);
    14332043                    fReadStdErr = false;
    14342044                }
     
    14672077                                         exitCode, procStatus, ctrlProcessStatusToText(procStatus));
    14682078
    1469                             rcExit = (RTEXITCODE)ctrlExecProcessStatusToExitCode(procStatus, exitCode);
     2079                            rcExit = (RTEXITCODE)ctrlExecProcessStatusToExitCodeDeprecated(procStatus, exitCode);
    14702080                        }
    14712081                        else if (pCtx->fVerbose)
     
    15262136    return rcExit;
    15272137}
     2138
     2139#endif /* Old exec code. */
    15282140
    15292141/**
     
    27183330    }
    27193331
    2720     uint32_t cDirs = mapDirs.size();
     3332    size_t cDirs = mapDirs.size();
    27213333    if (!cDirs)
    27223334        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_CREATEDIR,
     
    27813393    }
    27823394
    2783     uint32_t cDirs = mapDirs.size();
     3395    size_t cDirs = mapDirs.size();
    27843396    if (!cDirs)
    27853397        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_REMOVEDIR,
     
    28643476    }
    28653477
    2866     uint32_t cFiles = mapDirs.size();
     3478    size_t cFiles = mapDirs.size();
    28673479    if (!cFiles)
    28683480        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_REMOVEFILE,
     
    29423554        return RTMsgErrorExit(RTEXITCODE_FAILURE, "Failed to initialize, rc=%Rrc\n", vrc);
    29433555
    2944     uint32_t cSources = vecSources.size();
     3556    size_t cSources = vecSources.size();
    29453557    if (!cSources)
    29463558        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_RENAME,
     
    31793791    }
    31803792
    3181     uint32_t cObjs = mapObjs.size();
     3793    size_t cObjs = mapObjs.size();
    31823794    if (!cObjs)
    31833795        return errorSyntaxEx(USAGE_GUESTCONTROL, USAGE_GSTCTRL_STAT,
     
    39484560        || !RTStrICmp(pArg->argv[1], "execute"))
    39494561    {
    3950         gctlCmd.pfnHandler = handleCtrlProcessExec;
     4562        gctlCmd.pfnHandler = handleCtrlProcessExecDeprecated;
    39514563        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;
    39524574    }
    39534575    else if (!RTStrICmp(pArg->argv[1], "copyfrom"))
Note: See TracChangeset for help on using the changeset viewer.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette