コード例 #1
0
/**
 *  Entry point.
 */
extern "C" DECLEXPORT(int) TrustedMain(int argc, char **argv, char **envp)
{
    const char *vrdePort = NULL;
    const char *vrdeAddress = NULL;
    const char *vrdeEnabled = NULL;
    unsigned cVRDEProperties = 0;
    const char *aVRDEProperties[16];
    unsigned fRawR0 = ~0U;
    unsigned fRawR3 = ~0U;
    unsigned fPATM  = ~0U;
    unsigned fCSAM  = ~0U;
#ifdef VBOX_WITH_VIDEO_REC
    unsigned fVIDEOREC = 0;
    unsigned long ulFrameWidth = 800;
    unsigned long ulFrameHeight = 600;
    unsigned long ulBitRate = 300000;
    char pszMPEGFile[RTPATH_MAX];
    const char *pszFileNameParam = "VBox-%d.vob";
#endif /* VBOX_WITH_VIDEO_REC */

    LogFlow (("VBoxHeadless STARTED.\n"));
    RTPrintf (VBOX_PRODUCT " Headless Interface " VBOX_VERSION_STRING "\n"
              "(C) 2008-" VBOX_C_YEAR " " VBOX_VENDOR "\n"
              "All rights reserved.\n\n");

#ifdef VBOX_WITH_VIDEO_REC
    /* Parse the environment */
    parse_environ(&ulFrameWidth, &ulFrameHeight, &ulBitRate, &pszFileNameParam);
#endif

    enum eHeadlessOptions
    {
        OPT_RAW_R0 = 0x100,
        OPT_NO_RAW_R0,
        OPT_RAW_R3,
        OPT_NO_RAW_R3,
        OPT_PATM,
        OPT_NO_PATM,
        OPT_CSAM,
        OPT_NO_CSAM,
        OPT_SETTINGSPW,
        OPT_SETTINGSPW_FILE,
        OPT_COMMENT
    };

    static const RTGETOPTDEF s_aOptions[] =
    {
        { "-startvm", 's', RTGETOPT_REQ_STRING },
        { "--startvm", 's', RTGETOPT_REQ_STRING },
        { "-vrdpport", 'p', RTGETOPT_REQ_STRING },     /* VRDE: deprecated. */
        { "--vrdpport", 'p', RTGETOPT_REQ_STRING },    /* VRDE: deprecated. */
        { "-vrdpaddress", 'a', RTGETOPT_REQ_STRING },  /* VRDE: deprecated. */
        { "--vrdpaddress", 'a', RTGETOPT_REQ_STRING }, /* VRDE: deprecated. */
        { "-vrdp", 'v', RTGETOPT_REQ_STRING },         /* VRDE: deprecated. */
        { "--vrdp", 'v', RTGETOPT_REQ_STRING },        /* VRDE: deprecated. */
        { "-vrde", 'v', RTGETOPT_REQ_STRING },
        { "--vrde", 'v', RTGETOPT_REQ_STRING },
        { "-vrdeproperty", 'e', RTGETOPT_REQ_STRING },
        { "--vrdeproperty", 'e', RTGETOPT_REQ_STRING },
        { "-rawr0", OPT_RAW_R0, 0 },
        { "--rawr0", OPT_RAW_R0, 0 },
        { "-norawr0", OPT_NO_RAW_R0, 0 },
        { "--norawr0", OPT_NO_RAW_R0, 0 },
        { "-rawr3", OPT_RAW_R3, 0 },
        { "--rawr3", OPT_RAW_R3, 0 },
        { "-norawr3", OPT_NO_RAW_R3, 0 },
        { "--norawr3", OPT_NO_RAW_R3, 0 },
        { "-patm", OPT_PATM, 0 },
        { "--patm", OPT_PATM, 0 },
        { "-nopatm", OPT_NO_PATM, 0 },
        { "--nopatm", OPT_NO_PATM, 0 },
        { "-csam", OPT_CSAM, 0 },
        { "--csam", OPT_CSAM, 0 },
        { "-nocsam", OPT_NO_CSAM, 0 },
        { "--nocsam", OPT_NO_CSAM, 0 },
        { "--settingspw", OPT_SETTINGSPW, RTGETOPT_REQ_STRING },
        { "--settingspwfile", OPT_SETTINGSPW_FILE, RTGETOPT_REQ_STRING },
#ifdef VBOX_WITH_VIDEO_REC
        { "-capture", 'c', 0 },
        { "--capture", 'c', 0 },
        { "--width", 'w', RTGETOPT_REQ_UINT32 },
        { "--height", 'h', RTGETOPT_REQ_UINT32 }, /* great choice of short option! */
        { "--bitrate", 'r', RTGETOPT_REQ_UINT32 },
        { "--filename", 'f', RTGETOPT_REQ_STRING },
#endif /* VBOX_WITH_VIDEO_REC defined */
        { "-comment", OPT_COMMENT, RTGETOPT_REQ_STRING },
        { "--comment", OPT_COMMENT, RTGETOPT_REQ_STRING }
    };

    const char *pcszNameOrUUID = NULL;

    // parse the command line
    int ch;
    const char *pcszSettingsPw = NULL;
    const char *pcszSettingsPwFile = NULL;
    RTGETOPTUNION ValueUnion;
    RTGETOPTSTATE GetState;
    RTGetOptInit(&GetState, argc, argv, s_aOptions, RT_ELEMENTS(s_aOptions), 1, 0 /* fFlags */);
    while ((ch = RTGetOpt(&GetState, &ValueUnion)))
    {
        switch(ch)
        {
            case 's':
                pcszNameOrUUID = ValueUnion.psz;
                break;
            case 'p':
                RTPrintf("Warning: '-p' or '-vrdpport' are deprecated. Use '-e \"TCP/Ports=%s\"'\n", ValueUnion.psz);
                vrdePort = ValueUnion.psz;
                break;
            case 'a':
                RTPrintf("Warning: '-a' or '-vrdpaddress' are deprecated. Use '-e \"TCP/Address=%s\"'\n", ValueUnion.psz);
                vrdeAddress = ValueUnion.psz;
                break;
            case 'v':
                vrdeEnabled = ValueUnion.psz;
                break;
            case 'e':
                if (cVRDEProperties < RT_ELEMENTS(aVRDEProperties))
                    aVRDEProperties[cVRDEProperties++] = ValueUnion.psz;
                else
                     RTPrintf("Warning: too many VRDE properties. Ignored: '%s'\n", ValueUnion.psz);
                break;
            case OPT_RAW_R0:
                fRawR0 = true;
                break;
            case OPT_NO_RAW_R0:
                fRawR0 = false;
                break;
            case OPT_RAW_R3:
                fRawR3 = true;
                break;
            case OPT_NO_RAW_R3:
                fRawR3 = false;
                break;
            case OPT_PATM:
                fPATM = true;
                break;
            case OPT_NO_PATM:
                fPATM = false;
                break;
            case OPT_CSAM:
                fCSAM = true;
                break;
            case OPT_NO_CSAM:
                fCSAM = false;
                break;
            case OPT_SETTINGSPW:
                pcszSettingsPw = ValueUnion.psz;
                break;
            case OPT_SETTINGSPW_FILE:
                pcszSettingsPwFile = ValueUnion.psz;
                break;
#ifdef VBOX_WITH_VIDEO_REC
            case 'c':
                fVIDEOREC = true;
                break;
            case 'w':
                ulFrameWidth = ValueUnion.u32;
                break;
            case 'r':
                ulBitRate = ValueUnion.u32;
                break;
            case 'f':
                pszFileNameParam = ValueUnion.psz;
                break;
#endif /* VBOX_WITH_VIDEO_REC defined */
            case 'h':
#ifdef VBOX_WITH_VIDEO_REC
                if ((GetState.pDef->fFlags & RTGETOPT_REQ_MASK) != RTGETOPT_REQ_NOTHING)
                {
                    ulFrameHeight = ValueUnion.u32;
                    break;
                }
#endif
                show_usage();
                return 0;
            case OPT_COMMENT:
                /* nothing to do */
                break;
            case 'V':
                RTPrintf("%sr%s\n", RTBldCfgVersion(), RTBldCfgRevisionStr());
                return 0;
            default:
                ch = RTGetOptPrintError(ch, &ValueUnion);
                show_usage();
                return ch;
        }
    }

#ifdef VBOX_WITH_VIDEO_REC
    if (ulFrameWidth < 512 || ulFrameWidth > 2048 || ulFrameWidth % 2)
    {
        LogError("VBoxHeadless: ERROR: please specify an even frame width between 512 and 2048", 0);
        return 1;
    }
    if (ulFrameHeight < 384 || ulFrameHeight > 1536 || ulFrameHeight % 2)
    {
        LogError("VBoxHeadless: ERROR: please specify an even frame height between 384 and 1536", 0);
        return 1;
    }
    if (ulBitRate < 300000 || ulBitRate > 1000000)
    {
        LogError("VBoxHeadless: ERROR: please specify an even bitrate between 300000 and 1000000", 0);
        return 1;
    }
    /* Make sure we only have %d or %u (or none) in the file name specified */
    char *pcPercent = (char*)strchr(pszFileNameParam, '%');
    if (pcPercent != 0 && *(pcPercent + 1) != 'd' && *(pcPercent + 1) != 'u')
    {
        LogError("VBoxHeadless: ERROR: Only %%d and %%u are allowed in the capture file name.", -1);
        return 1;
    }
    /* And no more than one % in the name */
    if (pcPercent != 0 && strchr(pcPercent + 1, '%') != 0)
    {
        LogError("VBoxHeadless: ERROR: Only one format modifier is allowed in the capture file name.", -1);
        return 1;
    }
    RTStrPrintf(&pszMPEGFile[0], RTPATH_MAX, pszFileNameParam, RTProcSelf());
#endif /* defined VBOX_WITH_VIDEO_REC */

    if (!pcszNameOrUUID)
    {
        show_usage();
        return 1;
    }

    HRESULT rc;

    rc = com::Initialize();
#ifdef VBOX_WITH_XPCOM
    if (rc == NS_ERROR_FILE_ACCESS_DENIED)
    {
        char szHome[RTPATH_MAX] = "";
        com::GetVBoxUserHomeDirectory(szHome, sizeof(szHome));
        RTPrintf("Failed to initialize COM because the global settings directory '%s' is not accessible!", szHome);
        return 1;
    }
#endif
    if (FAILED(rc))
    {
        RTPrintf("VBoxHeadless: ERROR: failed to initialize COM!\n");
        return 1;
    }

    ComPtr<IVirtualBoxClient> pVirtualBoxClient;
    ComPtr<IVirtualBox> virtualBox;
    ComPtr<ISession> session;
    ComPtr<IMachine> machine;
    bool fSessionOpened = false;
    ComPtr<IEventListener> vboxClientListener;
    ComPtr<IEventListener> vboxListener;
    ComObjPtr<ConsoleEventListenerImpl> consoleListener;

    do
    {
        rc = pVirtualBoxClient.createInprocObject(CLSID_VirtualBoxClient);
        if (FAILED(rc))
        {
            RTPrintf("VBoxHeadless: ERROR: failed to create the VirtualBoxClient object!\n");
            com::ErrorInfo info;
            if (!info.isFullAvailable() && !info.isBasicAvailable())
            {
                com::GluePrintRCMessage(rc);
                RTPrintf("Most likely, the VirtualBox COM server is not running or failed to start.\n");
            }
            else
                GluePrintErrorInfo(info);
            break;
        }

        rc = pVirtualBoxClient->COMGETTER(VirtualBox)(virtualBox.asOutParam());
        if (FAILED(rc))
        {
            RTPrintf("Failed to get VirtualBox object (rc=%Rhrc)!\n", rc);
            break;
        }
        rc = pVirtualBoxClient->COMGETTER(Session)(session.asOutParam());
        if (FAILED(rc))
        {
            RTPrintf("Failed to get session object (rc=%Rhrc)!\n", rc);
            break;
        }

        if (pcszSettingsPw)
        {
            CHECK_ERROR(virtualBox, SetSettingsSecret(Bstr(pcszSettingsPw).raw()));
            if (FAILED(rc))
                break;
        }
        else if (pcszSettingsPwFile)
        {
            int rcExit = settingsPasswordFile(virtualBox, pcszSettingsPwFile);
            if (rcExit != RTEXITCODE_SUCCESS)
                break;
        }

        ComPtr<IMachine> m;

        rc = virtualBox->FindMachine(Bstr(pcszNameOrUUID).raw(), m.asOutParam());
        if (FAILED(rc))
        {
            LogError("Invalid machine name or UUID!\n", rc);
            break;
        }
        Bstr id;
        m->COMGETTER(Id)(id.asOutParam());
        AssertComRC(rc);
        if (FAILED(rc))
            break;

        Log(("VBoxHeadless: Opening a session with machine (id={%s})...\n",
              Utf8Str(id).c_str()));

        // open a session
        CHECK_ERROR_BREAK(m, LockMachine(session, LockType_VM));
        fSessionOpened = true;

        /* get the console */
        ComPtr<IConsole> console;
        CHECK_ERROR_BREAK(session, COMGETTER(Console)(console.asOutParam()));

        /* get the mutable machine */
        CHECK_ERROR_BREAK(console, COMGETTER(Machine)(machine.asOutParam()));

        ComPtr<IDisplay> display;
        CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));

#ifdef VBOX_WITH_VIDEO_REC
        IFramebuffer *pFramebuffer = 0;
        RTLDRMOD hLdrVideoRecFB;
        PFNREGISTERVIDEORECFB pfnRegisterVideoRecFB;

        if (fVIDEOREC)
        {
            HRESULT         rcc = S_OK;
            int             rrc = VINF_SUCCESS;
            RTERRINFOSTATIC ErrInfo;

            Log2(("VBoxHeadless: loading VBoxVideoRecFB and libvpx shared library\n"));
            RTErrInfoInitStatic(&ErrInfo);
            rrc = SUPR3HardenedLdrLoadAppPriv("VBoxVideoRecFB", &hLdrVideoRecFB, RTLDRLOAD_FLAGS_LOCAL, &ErrInfo.Core);

            if (RT_SUCCESS(rrc))
            {
                Log2(("VBoxHeadless: looking up symbol VBoxRegisterVideoRecFB\n"));
                rrc = RTLdrGetSymbol(hLdrVideoRecFB, "VBoxRegisterVideoRecFB",
                                     reinterpret_cast<void **>(&pfnRegisterVideoRecFB));
                if (RT_FAILURE(rrc))
                    LogError("Failed to load the video capture extension, possibly due to a damaged file\n", rrc);
            }
            else
                LogError("Failed to load the video capture extension\n", rrc); /** @todo stupid function, no formatting options. */
            if (RT_SUCCESS(rrc))
            {
                Log2(("VBoxHeadless: calling pfnRegisterVideoRecFB\n"));
                rcc = pfnRegisterVideoRecFB(ulFrameWidth, ulFrameHeight, ulBitRate,
                                         pszMPEGFile, &pFramebuffer);
                if (rcc != S_OK)
                    LogError("Failed to initialise video capturing - make sure that the file format\n"
                             "you wish to use is supported on your system\n", rcc);
            }
            if (RT_SUCCESS(rrc) && rcc == S_OK)
            {
                Log2(("VBoxHeadless: Registering framebuffer\n"));
                pFramebuffer->AddRef();
                display->SetFramebuffer(VBOX_VIDEO_PRIMARY_SCREEN, pFramebuffer);
            }
            if (!RT_SUCCESS(rrc) || rcc != S_OK)
                rc = E_FAIL;
        }
        if (rc != S_OK)
        {
            break;
        }
#endif /* defined(VBOX_WITH_VIDEO_REC) */
        ULONG cMonitors = 1;
        machine->COMGETTER(MonitorCount)(&cMonitors);

        unsigned uScreenId;
        for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
        {
# ifdef VBOX_WITH_VIDEO_REC
            if (fVIDEOREC && uScreenId == 0)
            {
                /* Already registered. */
                continue;
            }
# endif
            VRDPFramebuffer *pVRDPFramebuffer = new VRDPFramebuffer();
            if (!pVRDPFramebuffer)
            {
                RTPrintf("Error: could not create framebuffer object %d\n", uScreenId);
                break;
            }
            pVRDPFramebuffer->AddRef();
            display->SetFramebuffer(uScreenId, pVRDPFramebuffer);
        }
        if (uScreenId < cMonitors)
        {
            break;
        }

        // fill in remaining slots with null framebuffers
        for (uScreenId = 0; uScreenId < cMonitors; uScreenId++)
        {
            ComPtr<IFramebuffer> fb;
            LONG xOrigin, yOrigin;
            HRESULT hrc2 = display->GetFramebuffer(uScreenId,
                                                   fb.asOutParam(),
                                                   &xOrigin, &yOrigin);
            if (hrc2 == S_OK && fb.isNull())
            {
                NullFB *pNullFB =  new NullFB();
                pNullFB->AddRef();
                pNullFB->init();
                display->SetFramebuffer(uScreenId, pNullFB);
            }
        }

        /* get the machine debugger (isn't necessarily available) */
        ComPtr <IMachineDebugger> machineDebugger;
        console->COMGETTER(Debugger)(machineDebugger.asOutParam());
        if (machineDebugger)
        {
            Log(("Machine debugger available!\n"));
        }

        if (fRawR0 != ~0U)
        {
            if (!machineDebugger)
            {
                RTPrintf("Error: No debugger object; -%srawr0 cannot be executed!\n", fRawR0 ? "" : "no");
                break;
            }
            machineDebugger->COMSETTER(RecompileSupervisor)(!fRawR0);
        }
        if (fRawR3 != ~0U)
        {
            if (!machineDebugger)
            {
                RTPrintf("Error: No debugger object; -%srawr3 cannot be executed!\n", fRawR3 ? "" : "no");
                break;
            }
            machineDebugger->COMSETTER(RecompileUser)(!fRawR3);
        }
        if (fPATM != ~0U)
        {
            if (!machineDebugger)
            {
                RTPrintf("Error: No debugger object; -%spatm cannot be executed!\n", fPATM ? "" : "no");
                break;
            }
            machineDebugger->COMSETTER(PATMEnabled)(fPATM);
        }
        if (fCSAM != ~0U)
        {
            if (!machineDebugger)
            {
                RTPrintf("Error: No debugger object; -%scsam cannot be executed!\n", fCSAM ? "" : "no");
                break;
            }
            machineDebugger->COMSETTER(CSAMEnabled)(fCSAM);
        }

        /* initialize global references */
        gConsole = console;
        gEventQ = com::EventQueue::getMainEventQueue();

        /* VirtualBoxClient events registration. */
        {
            ComPtr<IEventSource> pES;
            CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
            ComObjPtr<VirtualBoxClientEventListenerImpl> listener;
            listener.createObject();
            listener->init(new VirtualBoxClientEventListener());
            vboxClientListener = listener;
            com::SafeArray<VBoxEventType_T> eventTypes;
            eventTypes.push_back(VBoxEventType_OnVBoxSVCAvailabilityChanged);
            CHECK_ERROR(pES, RegisterListener(vboxClientListener, ComSafeArrayAsInParam(eventTypes), true));
        }

        /* Console events registration. */
        {
            ComPtr<IEventSource> es;
            CHECK_ERROR(console, COMGETTER(EventSource)(es.asOutParam()));
            consoleListener.createObject();
            consoleListener->init(new ConsoleEventListener());
            com::SafeArray<VBoxEventType_T> eventTypes;
            eventTypes.push_back(VBoxEventType_OnMouseCapabilityChanged);
            eventTypes.push_back(VBoxEventType_OnStateChanged);
            eventTypes.push_back(VBoxEventType_OnVRDEServerInfoChanged);
            eventTypes.push_back(VBoxEventType_OnCanShowWindow);
            eventTypes.push_back(VBoxEventType_OnShowWindow);
            CHECK_ERROR(es, RegisterListener(consoleListener, ComSafeArrayAsInParam(eventTypes), true));
        }

        /* default is to enable the remote desktop server (backward compatibility) */
        BOOL fVRDEEnable = true;
        BOOL fVRDEEnabled;
        ComPtr <IVRDEServer> vrdeServer;
        CHECK_ERROR_BREAK(machine, COMGETTER(VRDEServer)(vrdeServer.asOutParam()));
        CHECK_ERROR_BREAK(vrdeServer, COMGETTER(Enabled)(&fVRDEEnabled));

        if (vrdeEnabled != NULL)
        {
            /* -vrdeServer on|off|config */
            if (!strcmp(vrdeEnabled, "off") || !strcmp(vrdeEnabled, "disable"))
                fVRDEEnable = false;
            else if (!strcmp(vrdeEnabled, "config"))
            {
                if (!fVRDEEnabled)
                    fVRDEEnable = false;
            }
            else if (strcmp(vrdeEnabled, "on") && strcmp(vrdeEnabled, "enable"))
            {
                RTPrintf("-vrdeServer requires an argument (on|off|config)\n");
                break;
            }
        }

        if (fVRDEEnable)
        {
            Log(("VBoxHeadless: Enabling VRDE server...\n"));

            /* set VRDE port if requested by the user */
            if (vrdePort != NULL)
            {
                Bstr bstr = vrdePort;
                CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), bstr.raw()));
            }
            /* set VRDE address if requested by the user */
            if (vrdeAddress != NULL)
            {
                CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Address").raw(), Bstr(vrdeAddress).raw()));
            }

            /* Set VRDE properties. */
            if (cVRDEProperties > 0)
            {
                for (unsigned i = 0; i < cVRDEProperties; i++)
                {
                    /* Parse 'name=value' */
                    char *pszProperty = RTStrDup(aVRDEProperties[i]);
                    if (pszProperty)
                    {
                        char *pDelimiter = strchr(pszProperty, '=');
                        if (pDelimiter)
                        {
                            *pDelimiter = '\0';

                            Bstr bstrName = pszProperty;
                            Bstr bstrValue = &pDelimiter[1];
                            CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
                        }
                        else
                        {
                            RTPrintf("Error: Invalid VRDE property '%s'\n", aVRDEProperties[i]);
                            RTStrFree(pszProperty);
                            rc = E_INVALIDARG;
                            break;
                        }
                        RTStrFree(pszProperty);
                    }
                    else
                    {
                        RTPrintf("Error: Failed to allocate memory for VRDE property '%s'\n", aVRDEProperties[i]);
                        rc = E_OUTOFMEMORY;
                        break;
                    }
                }
                if (FAILED(rc))
                    break;
            }

            /* enable VRDE server (only if currently disabled) */
            if (!fVRDEEnabled)
            {
                CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
            }
        }
        else
        {
            /* disable VRDE server (only if currently enabled */
            if (fVRDEEnabled)
            {
                CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
            }
        }

        /* Disable the host clipboard before powering up */
        console->COMSETTER(UseHostClipboard)(false);

        Log(("VBoxHeadless: Powering up the machine...\n"));

        ComPtr <IProgress> progress;
        CHECK_ERROR_BREAK(console, PowerUp(progress.asOutParam()));

        /*
         * Wait for the result because there can be errors.
         *
         * It's vital to process events while waiting (teleportation deadlocks),
         * so we'll poll for the completion instead of waiting on it.
         */
        for (;;)
        {
            BOOL fCompleted;
            rc = progress->COMGETTER(Completed)(&fCompleted);
            if (FAILED(rc) || fCompleted)
                break;

            /* Process pending events, then wait for new ones. Note, this
             * processes NULL events signalling event loop termination. */
            gEventQ->processEventQueue(0);
            if (!g_fTerminateFE)
                gEventQ->processEventQueue(500);
        }

        if (SUCCEEDED(progress->WaitForCompletion(-1)))
        {
            /* Figure out if the operation completed with a failed status
             * and print the error message. Terminate immediately, and let
             * the cleanup code take care of potentially pending events. */
            LONG progressRc;
            progress->COMGETTER(ResultCode)(&progressRc);
            rc = progressRc;
            if (FAILED(rc))
            {
                com::ProgressErrorInfo info(progress);
                if (info.isBasicAvailable())
                {
                    RTPrintf("Error: failed to start machine. Error message: %ls\n", info.getText().raw());
                }
                else
                {
                    RTPrintf("Error: failed to start machine. No error message available!\n");
                }
                break;
            }
        }

        /* VirtualBox events registration. */
        {
            ComPtr<IEventSource> es;
            CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
            ComObjPtr<VirtualBoxEventListenerImpl> listener;
            listener.createObject();
            listener->init(new VirtualBoxEventListener());
            vboxListener = listener;
            com::SafeArray<VBoxEventType_T> eventTypes;
            eventTypes.push_back(VBoxEventType_OnGuestPropertyChanged);

            /**
             * @todo Set the notification pattern to "/VirtualBox/GuestInfo/OS/ *Logged*"
             *       to not cause too much load. The current API is broken as
             *       IMachine::GuestPropertyNotificationPatterns() would change the
             *       filter for _all_ clients. This is not what we want!
             */
            CHECK_ERROR(es, RegisterListener(vboxListener, ComSafeArrayAsInParam(eventTypes), true));
        }

#ifdef VBOX_WITH_SAVESTATE_ON_SIGNAL
        signal(SIGINT, SaveState);
        signal(SIGTERM, SaveState);
#endif

        Log(("VBoxHeadless: Waiting for PowerDown...\n"));

        while (   !g_fTerminateFE
               && RT_SUCCESS(gEventQ->processEventQueue(RT_INDEFINITE_WAIT)))
            /* nothing */ ;

        Log(("VBoxHeadless: event loop has terminated...\n"));

#ifdef VBOX_WITH_VIDEO_REC
        if (pFramebuffer)
        {
            pFramebuffer->Release();
            Log(("Released framebuffer\n"));
            pFramebuffer = NULL;
        }
#endif /* defined(VBOX_WITH_VIDEO_REC) */

        /* we don't have to disable VRDE here because we don't save the settings of the VM */
    }
    while (0);

    /*
     * Get the machine state.
     */
    MachineState_T machineState = MachineState_Aborted;
    if (!machine.isNull())
        machine->COMGETTER(State)(&machineState);

    /*
     * Turn off the VM if it's running
     */
    if (   gConsole
        && (   machineState == MachineState_Running
            || machineState == MachineState_Teleporting
            || machineState == MachineState_LiveSnapshotting
            /** @todo power off paused VMs too? */
           )
       )
    do
    {
        consoleListener->getWrapped()->ignorePowerOffEvents(true);
        ComPtr<IProgress> pProgress;
        CHECK_ERROR_BREAK(gConsole, PowerDown(pProgress.asOutParam()));
        CHECK_ERROR_BREAK(pProgress, WaitForCompletion(-1));
        BOOL completed;
        CHECK_ERROR_BREAK(pProgress, COMGETTER(Completed)(&completed));
        ASSERT(completed);
        LONG hrc;
        CHECK_ERROR_BREAK(pProgress, COMGETTER(ResultCode)(&hrc));
        if (FAILED(hrc))
        {
            RTPrintf("VBoxHeadless: ERROR: Failed to power down VM!");
            com::ErrorInfo info;
            if (!info.isFullAvailable() && !info.isBasicAvailable())
                com::GluePrintRCMessage(hrc);
            else
                GluePrintErrorInfo(info);
            break;
        }
    } while (0);

    /* VirtualBox callback unregistration. */
    if (vboxListener)
    {
        ComPtr<IEventSource> es;
        CHECK_ERROR(virtualBox, COMGETTER(EventSource)(es.asOutParam()));
        if (!es.isNull())
            CHECK_ERROR(es, UnregisterListener(vboxListener));
        vboxListener.setNull();
    }

    /* Console callback unregistration. */
    if (consoleListener)
    {
        ComPtr<IEventSource> es;
        CHECK_ERROR(gConsole, COMGETTER(EventSource)(es.asOutParam()));
        if (!es.isNull())
            CHECK_ERROR(es, UnregisterListener(consoleListener));
        consoleListener.setNull();
    }

    /* VirtualBoxClient callback unregistration. */
    if (vboxClientListener)
    {
        ComPtr<IEventSource> pES;
        CHECK_ERROR(pVirtualBoxClient, COMGETTER(EventSource)(pES.asOutParam()));
        if (!pES.isNull())
            CHECK_ERROR(pES, UnregisterListener(vboxClientListener));
        vboxClientListener.setNull();
    }

    /* No more access to the 'console' object, which will be uninitialized by the next session->Close call. */
    gConsole = NULL;

    if (fSessionOpened)
    {
        /*
         * Close the session. This will also uninitialize the console and
         * unregister the callback we've registered before.
         */
        Log(("VBoxHeadless: Closing the session...\n"));
        session->UnlockMachine();
    }

    /* Must be before com::Shutdown */
    session.setNull();
    virtualBox.setNull();
    pVirtualBoxClient.setNull();
    machine.setNull();

    com::Shutdown();

    LogFlow(("VBoxHeadless FINISHED.\n"));

    return FAILED(rc) ? 1 : 0;
}
コード例 #2
0
/**
 * Implementation for all VBoxManage snapshot ... subcommands.
 * @param a
 * @return
 */
int handleSnapshot(HandlerArg *a)
{
    HRESULT rc;

    /* we need at least a VM and a command */
    if (a->argc < 2)
        return errorSyntax(USAGE_SNAPSHOT, "Not enough parameters");

    /* the first argument must be the VM */
    Bstr bstrMachine(a->argv[0]);
    ComPtr<IMachine> ptrMachine;
    CHECK_ERROR(a->virtualBox, FindMachine(bstrMachine.raw(),
                                           ptrMachine.asOutParam()));
    if (!ptrMachine)
        return 1;

    do
    {
        /* we have to open a session for this task (new or shared) */
        rc = ptrMachine->LockMachine(a->session, LockType_Shared);
        ComPtr<IConsole> console;
        CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));

        /* switch based on the command */
        bool fDelete = false,
             fRestore = false,
             fRestoreCurrent = false;

        if (!strcmp(a->argv[1], "take"))
        {
            /* there must be a name */
            if (a->argc < 3)
            {
                errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
                rc = E_FAIL;
                break;
            }
            Bstr name(a->argv[2]);

            /* parse the optional arguments */
            Bstr desc;
            bool fPause = false;
            static const RTGETOPTDEF s_aTakeOptions[] =
            {
                { "--description", 'd', RTGETOPT_REQ_STRING },
                { "-description",  'd', RTGETOPT_REQ_STRING },
                { "-desc",         'd', RTGETOPT_REQ_STRING },
                { "--pause",       'p', RTGETOPT_REQ_NOTHING }
            };
            RTGETOPTSTATE GetOptState;
            RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTakeOptions, RT_ELEMENTS(s_aTakeOptions),
                         3, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
            int ch;
            RTGETOPTUNION Value;
            while (   SUCCEEDED(rc)
                      && (ch = RTGetOpt(&GetOptState, &Value)))
            {
                switch (ch)
                {
                case 'p':
                    fPause = true;
                    break;

                case 'd':
                    desc = Value.psz;
                    break;

                default:
                    errorGetOpt(USAGE_SNAPSHOT, ch, &Value);
                    rc = E_FAIL;
                    break;
                }
            }
            if (FAILED(rc))
                break;

            /*
             * XXX for now, do ALWAYS pause as live snapshots are still broken
             */
            fPause = true;
            if (fPause)
            {
                MachineState_T machineState;
                CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
                if (machineState == MachineState_Running)
                    CHECK_ERROR_BREAK(console, Pause());
                else
                    fPause = false;
            }

            ComPtr<IProgress> progress;
            CHECK_ERROR_BREAK(console, TakeSnapshot(name.raw(), desc.raw(),
                                                    progress.asOutParam()));

            rc = showProgress(progress);
            CHECK_PROGRESS_ERROR(progress, ("Failed to take snapshot"));

            if (fPause)
            {
                MachineState_T machineState;
                CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
                if (machineState == MachineState_Paused)
                {
                    if (SUCCEEDED(rc))
                        CHECK_ERROR_BREAK(console, Resume());
                    else
                        console->Resume();
                }
            }
        }
        else if (    (fDelete = !strcmp(a->argv[1], "delete"))
                     || (fRestore = !strcmp(a->argv[1], "restore"))
                     || (fRestoreCurrent = !strcmp(a->argv[1], "restorecurrent"))
                )
        {
            if (fRestoreCurrent)
            {
                if (a->argc > 2)
                {
                    errorSyntax(USAGE_SNAPSHOT, "Too many arguments");
                    rc = E_FAIL;
                    break;
                }
            }
            /* exactly one parameter: snapshot name */
            else if (a->argc != 3)
            {
                errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
                rc = E_FAIL;
                break;
            }

            ComPtr<ISnapshot> pSnapshot;
            ComPtr<IProgress> pProgress;
            Bstr bstrSnapGuid;

            if (fRestoreCurrent)
            {
                CHECK_ERROR_BREAK(ptrMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
            }
            else
            {
                // restore or delete snapshot: then resolve cmd line argument to snapshot instance
                CHECK_ERROR_BREAK(ptrMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
                                  pSnapshot.asOutParam()));
            }

            CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Id)(bstrSnapGuid.asOutParam()));

            if (fDelete)
            {
                CHECK_ERROR_BREAK(console, DeleteSnapshot(bstrSnapGuid.raw(),
                                  pProgress.asOutParam()));
            }
            else
            {
                // restore or restore current
                RTPrintf("Restoring snapshot %ls\n", bstrSnapGuid.raw());
                CHECK_ERROR_BREAK(console, RestoreSnapshot(pSnapshot, pProgress.asOutParam()));
            }

            rc = showProgress(pProgress);
            CHECK_PROGRESS_ERROR(pProgress, ("Snapshot operation failed"));
        }
        else if (!strcmp(a->argv[1], "edit"))
        {
            if (a->argc < 3)
            {
                errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
                rc = E_FAIL;
                break;
            }

            ComPtr<ISnapshot> snapshot;

            if (   !strcmp(a->argv[2], "--current")
                    || !strcmp(a->argv[2], "-current"))
            {
                CHECK_ERROR_BREAK(ptrMachine, COMGETTER(CurrentSnapshot)(snapshot.asOutParam()));
            }
            else
            {
                CHECK_ERROR_BREAK(ptrMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
                                  snapshot.asOutParam()));
            }

            /* parse options */
            for (int i = 3; i < a->argc; i++)
            {
                if (   !strcmp(a->argv[i], "--name")
                        || !strcmp(a->argv[i], "-name")
                        || !strcmp(a->argv[i], "-newname"))
                {
                    if (a->argc <= i + 1)
                    {
                        errorArgument("Missing argument to '%s'", a->argv[i]);
                        rc = E_FAIL;
                        break;
                    }
                    i++;
                    snapshot->COMSETTER(Name)(Bstr(a->argv[i]).raw());
                }
                else if (   !strcmp(a->argv[i], "--description")
                            || !strcmp(a->argv[i], "-description")
                            || !strcmp(a->argv[i], "-newdesc"))
                {
                    if (a->argc <= i + 1)
                    {
                        errorArgument("Missing argument to '%s'", a->argv[i]);
                        rc = E_FAIL;
                        break;
                    }
                    i++;
                    snapshot->COMSETTER(Description)(Bstr(a->argv[i]).raw());
                }
                else
                {
                    errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
                    rc = E_FAIL;
                    break;
                }
            }

        }
        else if (!strcmp(a->argv[1], "showvminfo"))
        {
            /* exactly one parameter: snapshot name */
            if (a->argc != 3)
            {
                errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
                rc = E_FAIL;
                break;
            }

            ComPtr<ISnapshot> snapshot;

            CHECK_ERROR_BREAK(ptrMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
                              snapshot.asOutParam()));

            /* get the machine of the given snapshot */
            ComPtr<IMachine> ptrMachine2;
            snapshot->COMGETTER(Machine)(ptrMachine2.asOutParam());
            showVMInfo(a->virtualBox, ptrMachine2, VMINFO_NONE, console);
        }
        else if (!strcmp(a->argv[1], "list"))
            rc = handleSnapshotList(a, ptrMachine) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
        else if (!strcmp(a->argv[1], "dump"))          // undocumented parameter to debug snapshot info
            DumpSnapshot(ptrMachine);
        else
        {
            errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[1]).c_str());
            rc = E_FAIL;
        }
    } while (0);

    a->session->UnlockMachine();

    return SUCCEEDED(rc) ? 0 : 1;
}
コード例 #3
0
    STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
    {
        switch (aType)
        {
            case VBoxEventType_OnMouseCapabilityChanged:
            {

                ComPtr<IMouseCapabilityChangedEvent> mccev = aEvent;
                Assert(mccev);

                BOOL fSupportsAbsolute = false;
                mccev->COMGETTER(SupportsAbsolute)(&fSupportsAbsolute);

                /* Emit absolute mouse event to actually enable the host mouse cursor. */
                if (fSupportsAbsolute && gConsole)
                {
                    ComPtr<IMouse> mouse;
                    gConsole->COMGETTER(Mouse)(mouse.asOutParam());
                    if (mouse)
                    {
                        mouse->PutMouseEventAbsolute(-1, -1, 0, 0 /* Horizontal wheel */, 0);
                    }
                }
                break;
            }
            case VBoxEventType_OnStateChanged:
            {
                ComPtr<IStateChangedEvent> scev = aEvent;
                Assert(scev);

                MachineState_T machineState;
                scev->COMGETTER(State)(&machineState);

                /* Terminate any event wait operation if the machine has been
                 * PoweredDown/Saved/Aborted. */
                if (machineState < MachineState_Running && !m_fIgnorePowerOffEvents)
                {
                    g_fTerminateFE = true;
                    gEventQ->interruptEventQueueProcessing();
                }

                break;
            }
            case VBoxEventType_OnVRDEServerInfoChanged:
            {
                ComPtr<IVRDEServerInfoChangedEvent> rdicev = aEvent;
                Assert(rdicev);

                if (gConsole)
                {
                    ComPtr<IVRDEServerInfo> info;
                    gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
                    if (info)
                    {
                        LONG port;
                        info->COMGETTER(Port)(&port);
                        if (port != mLastVRDEPort)
                        {
                            if (port == -1)
                                RTPrintf("VRDE server is inactive.\n");
                            else if (port == 0)
                                RTPrintf("VRDE server failed to start.\n");
                            else
                                RTPrintf("VRDE server is listening on port %d.\n", port);

                            mLastVRDEPort = port;
                        }
                    }
                }
                break;
            }
            case VBoxEventType_OnCanShowWindow:
            {
                ComPtr<ICanShowWindowEvent> cswev = aEvent;
                Assert(cswev);
                cswev->AddVeto(NULL);
                break;
            }
            case VBoxEventType_OnShowWindow:
            {
                ComPtr<IShowWindowEvent> swev = aEvent;
                Assert(swev);
                swev->COMSETTER(WinId)(0);
                break;
            }
            default:
                AssertFailed();
        }
        return S_OK;
    }
コード例 #4
0
    STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
    {
        switch (aType)
        {
            case VBoxEventType_OnGuestPropertyChanged:
            {
                ComPtr<IGuestPropertyChangedEvent> gpcev = aEvent;
                Assert(gpcev);

                Bstr strKey;
                gpcev->COMGETTER(Name)(strKey.asOutParam());

                Utf8Str utf8Key = strKey;
                LogRelFlow(("Guest property \"%s\" has been changed\n", utf8Key.c_str()));

                if (utf8Key.equals("/VirtualBox/GuestInfo/OS/NoLoggedInUsers"))
                {
                    LogRelFlow(("Guest indicates that there %s logged in users (anymore)\n",
                                utf8Key.equals("true") ? "are no" : "are"));

                    /* Check if this is our machine and the "disconnect on logout feature" is enabled. */
                    BOOL fProcessDisconnectOnGuestLogout = FALSE;
                    ComPtr <IMachine> machine;
                    HRESULT hrc = S_OK;

                    if (gConsole)
                    {
                        hrc = gConsole->COMGETTER(Machine)(machine.asOutParam());
                        if (SUCCEEDED(hrc) && machine)
                        {
                            Bstr id, machineId;
                            hrc = machine->COMGETTER(Id)(id.asOutParam());
                            gpcev->COMGETTER(MachineId)(machineId.asOutParam());
                            if (id == machineId)
                            {
                                Bstr strDiscon;
                                hrc = machine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout").raw(),
                                                            strDiscon.asOutParam());
                                if (SUCCEEDED(hrc))
                                {
                                    Utf8Str utf8Discon = strDiscon;
                                    fProcessDisconnectOnGuestLogout = utf8Discon.equals("1")
                                                                    ? TRUE : FALSE;

                                    LogRelFlow(("VRDE: ExtraData VRDP/DisconnectOnGuestLogout=%s\n",
                                                utf8Discon.c_str()));
                                }
                            }
                        }
                    }
                    else
                        LogRel(("VRDE: No console available, skipping disconnect on guest logout check\n"));

                    LogRelFlow(("VRDE: hrc=%Rhrc: Host %s disconnecting clients (current host state known: %s)\n",
                                hrc, fProcessDisconnectOnGuestLogout ? "will handle" : "does not handle",
                                mfNoLoggedInUsers ? "No users logged in" : "Users logged in"));

                    if (fProcessDisconnectOnGuestLogout)
                    {
                        bool fDropConnection = false;

                        Bstr value;
                        gpcev->COMGETTER(Value)(value.asOutParam());
                        Utf8Str utf8Value = value;

                        if (!mfNoLoggedInUsers) /* Only if the property really changes. */
                        {
                            if (   utf8Value == "true"
                                /* Guest property got deleted due to reset,
                                 * so it has no value anymore. */
                                || utf8Value.isEmpty())
                            {
                                mfNoLoggedInUsers = true;
                                fDropConnection = true;
                            }
                        }
                        else if (utf8Value == "false")
                            mfNoLoggedInUsers = false;
                        /* Guest property got deleted due to reset,
                         * take the shortcut without touching the mfNoLoggedInUsers
                         * state. */
                        else if (utf8Value.isEmpty())
                            fDropConnection = true;

                        LogRelFlow(("VRDE: szNoLoggedInUsers=%s, mfNoLoggedInUsers=%RTbool, fDropConnection=%RTbool\n",
                                    utf8Value.c_str(), mfNoLoggedInUsers, fDropConnection));

                        if (fDropConnection)
                        {
                            /* If there is a connection, drop it. */
                            ComPtr<IVRDEServerInfo> info;
                            hrc = gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
                            if (SUCCEEDED(hrc) && info)
                            {
                                ULONG cClients = 0;
                                hrc = info->COMGETTER(NumberOfClients)(&cClients);

                                LogRelFlow(("VRDE: connected clients=%RU32\n", cClients));
                                if (SUCCEEDED(hrc) && cClients > 0)
                                {
                                    ComPtr <IVRDEServer> vrdeServer;
                                    hrc = machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
                                    if (SUCCEEDED(hrc) && vrdeServer)
                                    {
                                        LogRel(("VRDE: the guest user has logged out, disconnecting remote clients.\n"));
                                        vrdeServer->COMSETTER(Enabled)(FALSE);
                                        vrdeServer->COMSETTER(Enabled)(TRUE);
                                    }
                                }
                            }
                        }
                    }

                    LogRelFlow(("VRDE: returned with=%Rhrc\n", hrc));
                }
                break;
            }
            default:
                AssertFailed();
        }

        return S_OK;
    }
コード例 #5
0
/**
 * Implementation for all VBoxManage snapshot ... subcommands.
 * @param a
 * @return
 */
RTEXITCODE handleSnapshot(HandlerArg *a)
{
    HRESULT rc;

    /* we need at least a VM and a command */
    if (a->argc < 2)
        return errorSyntax(USAGE_SNAPSHOT, "Not enough parameters");

    /* the first argument must be the VM */
    Bstr bstrMachine(a->argv[0]);
    ComPtr<IMachine> pMachine;
    CHECK_ERROR(a->virtualBox, FindMachine(bstrMachine.raw(),
                                           pMachine.asOutParam()));
    if (!pMachine)
        return RTEXITCODE_FAILURE;

    /* we have to open a session for this task (new or shared) */
    CHECK_ERROR_RET(pMachine, LockMachine(a->session, LockType_Shared), RTEXITCODE_FAILURE);
    do
    {
        /* replace the (read-only) IMachine object by a writable one */
        ComPtr<IMachine> sessionMachine;
        CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));

        /* switch based on the command */
        bool fDelete = false,
             fRestore = false,
             fRestoreCurrent = false;

        if (!strcmp(a->argv[1], "take"))
        {
            /* there must be a name */
            if (a->argc < 3)
            {
                errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
                rc = E_FAIL;
                break;
            }
            Bstr name(a->argv[2]);

            /* parse the optional arguments */
            Bstr desc;
            bool fPause = true; /* default is NO live snapshot */
            SnapshotUniqueFlags enmUnique = SnapshotUniqueFlags_Null;
            static const RTGETOPTDEF s_aTakeOptions[] =
            {
                { "--description", 'd', RTGETOPT_REQ_STRING },
                { "-description",  'd', RTGETOPT_REQ_STRING },
                { "-desc",         'd', RTGETOPT_REQ_STRING },
                { "--pause",       'p', RTGETOPT_REQ_NOTHING },
                { "--live",        'l', RTGETOPT_REQ_NOTHING },
                { "--uniquename",  'u', RTGETOPT_REQ_STRING }
            };
            RTGETOPTSTATE GetOptState;
            RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTakeOptions, RT_ELEMENTS(s_aTakeOptions),
                         3, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
            int ch;
            RTGETOPTUNION Value;
            int vrc;
            while (   SUCCEEDED(rc)
                   && (ch = RTGetOpt(&GetOptState, &Value)))
            {
                switch (ch)
                {
                    case 'p':
                        fPause = true;
                        break;

                    case 'l':
                        fPause = false;
                        break;

                    case 'd':
                        desc = Value.psz;
                        break;

                    case 'u':
                        vrc = parseSnapshotUniqueFlags(Value.psz, &enmUnique);
                        if (RT_FAILURE(vrc))
                            return errorArgument("Invalid unique name description '%s'", Value.psz);
                        break;

                    default:
                        errorGetOpt(USAGE_SNAPSHOT, ch, &Value);
                        rc = E_FAIL;
                        break;
                }
            }
            if (FAILED(rc))
                break;

            if (enmUnique & (SnapshotUniqueFlags_Number | SnapshotUniqueFlags_Timestamp))
            {
                ComPtr<ISnapshot> pSnapshot;
                rc = sessionMachine->FindSnapshot(name.raw(),
                                                  pSnapshot.asOutParam());
                if (SUCCEEDED(rc) || (enmUnique & SnapshotUniqueFlags_Force))
                {
                    /* there is a duplicate, need to create a unique name */
                    uint32_t count = 0;
                    RTTIMESPEC now;

                    if (enmUnique & SnapshotUniqueFlags_Number)
                    {
                        if (enmUnique & SnapshotUniqueFlags_Force)
                            count = 1;
                        else
                            count = 2;
                        RTTimeSpecSetNano(&now, 0); /* Shut up MSC */
                    }
                    else
                        RTTimeNow(&now);

                    while (count < 500)
                    {
                        Utf8Str suffix;
                        if (enmUnique & SnapshotUniqueFlags_Number)
                            suffix = Utf8StrFmt("%u", count);
                        else
                        {
                            RTTIMESPEC nowplus = now;
                            RTTimeSpecAddSeconds(&nowplus, count);
                            RTTIME stamp;
                            RTTimeExplode(&stamp, &nowplus);
                            suffix = Utf8StrFmt("%04u-%02u-%02uT%02u:%02u:%02uZ", stamp.i32Year, stamp.u8Month, stamp.u8MonthDay, stamp.u8Hour, stamp.u8Minute, stamp.u8Second);
                        }
                        Bstr tryName = name;
                        if (enmUnique & SnapshotUniqueFlags_Space)
                            tryName = BstrFmt("%ls %s", name.raw(), suffix.c_str());
                        else
                            tryName = BstrFmt("%ls%s", name.raw(), suffix.c_str());
                        count++;
                        rc = sessionMachine->FindSnapshot(tryName.raw(),
                                                          pSnapshot.asOutParam());
                        if (FAILED(rc))
                        {
                            name = tryName;
                            break;
                        }
                    }
                    if (SUCCEEDED(rc))
                    {
                        errorArgument("Failed to generate a unique snapshot name");
                        rc = E_FAIL;
                        break;
                    }
                }
                rc = S_OK;
            }

            ComPtr<IProgress> progress;
            Bstr snapId;
            CHECK_ERROR_BREAK(sessionMachine, TakeSnapshot(name.raw(), desc.raw(),
                                                           fPause, snapId.asOutParam(),
                                                           progress.asOutParam()));

            rc = showProgress(progress);
            if (SUCCEEDED(rc))
                RTPrintf("Snapshot taken. UUID: %ls\n", snapId.raw());
            else
                CHECK_PROGRESS_ERROR(progress, ("Failed to take snapshot"));
        }
        else if (    (fDelete = !strcmp(a->argv[1], "delete"))
                  || (fRestore = !strcmp(a->argv[1], "restore"))
                  || (fRestoreCurrent = !strcmp(a->argv[1], "restorecurrent"))
                )
        {
            if (fRestoreCurrent)
            {
                if (a->argc > 2)
                {
                    errorSyntax(USAGE_SNAPSHOT, "Too many arguments");
                    rc = E_FAIL;
                    break;
                }
            }
            /* exactly one parameter: snapshot name */
            else if (a->argc != 3)
            {
                errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
                rc = E_FAIL;
                break;
            }

            ComPtr<ISnapshot> pSnapshot;

            if (fRestoreCurrent)
            {
                CHECK_ERROR_BREAK(sessionMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
                if (pSnapshot.isNull())
                {
                    RTPrintf("This machine does not have any snapshots\n");
                    return RTEXITCODE_FAILURE;
                }
            }
            else
            {
                // restore or delete snapshot: then resolve cmd line argument to snapshot instance
                CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
                                                               pSnapshot.asOutParam()));
            }

            Bstr bstrSnapGuid;
            CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Id)(bstrSnapGuid.asOutParam()));

            Bstr bstrSnapName;
            CHECK_ERROR_BREAK(pSnapshot, COMGETTER(Name)(bstrSnapName.asOutParam()));

            ComPtr<IProgress> pProgress;

            RTPrintf("%s snapshot '%ls' (%ls)\n",
                     fDelete ? "Deleting" : "Restoring", bstrSnapName.raw(), bstrSnapGuid.raw());

            if (fDelete)
            {
                CHECK_ERROR_BREAK(sessionMachine, DeleteSnapshot(bstrSnapGuid.raw(),
                                                                 pProgress.asOutParam()));
            }
            else
            {
                // restore or restore current
                CHECK_ERROR_BREAK(sessionMachine, RestoreSnapshot(pSnapshot, pProgress.asOutParam()));
            }

            rc = showProgress(pProgress);
            CHECK_PROGRESS_ERROR(pProgress, ("Snapshot operation failed"));
        }
        else if (!strcmp(a->argv[1], "edit"))
        {
            if (a->argc < 3)
            {
                errorSyntax(USAGE_SNAPSHOT, "Missing snapshot name");
                rc = E_FAIL;
                break;
            }

            ComPtr<ISnapshot> pSnapshot;

            if (   !strcmp(a->argv[2], "--current")
                || !strcmp(a->argv[2], "-current"))
            {
                CHECK_ERROR_BREAK(sessionMachine, COMGETTER(CurrentSnapshot)(pSnapshot.asOutParam()));
                if (pSnapshot.isNull())
                {
                    RTPrintf("This machine does not have any snapshots\n");
                    return RTEXITCODE_FAILURE;
                }
            }
            else
            {
                CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
                                                               pSnapshot.asOutParam()));
            }

            /* parse options */
            for (int i = 3; i < a->argc; i++)
            {
                if (   !strcmp(a->argv[i], "--name")
                    || !strcmp(a->argv[i], "-name")
                    || !strcmp(a->argv[i], "-newname"))
                {
                    if (a->argc <= i + 1)
                    {
                        errorArgument("Missing argument to '%s'", a->argv[i]);
                        rc = E_FAIL;
                        break;
                    }
                    i++;
                    pSnapshot->COMSETTER(Name)(Bstr(a->argv[i]).raw());
                }
                else if (   !strcmp(a->argv[i], "--description")
                         || !strcmp(a->argv[i], "-description")
                         || !strcmp(a->argv[i], "-newdesc"))
                {
                    if (a->argc <= i + 1)
                    {
                        errorArgument("Missing argument to '%s'", a->argv[i]);
                        rc = E_FAIL;
                        break;
                    }
                    i++;
                    pSnapshot->COMSETTER(Description)(Bstr(a->argv[i]).raw());
                }
                else
                {
                    errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[i]).c_str());
                    rc = E_FAIL;
                    break;
                }
            }

        }
        else if (!strcmp(a->argv[1], "showvminfo"))
        {
            /* exactly one parameter: snapshot name */
            if (a->argc != 3)
            {
                errorSyntax(USAGE_SNAPSHOT, "Expecting snapshot name only");
                rc = E_FAIL;
                break;
            }

            ComPtr<ISnapshot> pSnapshot;

            CHECK_ERROR_BREAK(sessionMachine, FindSnapshot(Bstr(a->argv[2]).raw(),
                                                           pSnapshot.asOutParam()));

            /* get the machine of the given snapshot */
            ComPtr<IMachine> pMachine2;
            pSnapshot->COMGETTER(Machine)(pMachine2.asOutParam());
            showVMInfo(a->virtualBox, pMachine2, NULL, VMINFO_NONE);
        }
        else if (!strcmp(a->argv[1], "list"))
            rc = handleSnapshotList(a, sessionMachine) == RTEXITCODE_SUCCESS ? S_OK : E_FAIL;
        else if (!strcmp(a->argv[1], "dump"))          // undocumented parameter to debug snapshot info
            DumpSnapshot(sessionMachine);
        else
        {
            errorSyntax(USAGE_SNAPSHOT, "Invalid parameter '%s'", Utf8Str(a->argv[1]).c_str());
            rc = E_FAIL;
        }
    } while (0);

    a->session->UnlockMachine();

    return SUCCEEDED(rc) ? RTEXITCODE_SUCCESS : RTEXITCODE_FAILURE;
}
コード例 #6
0
int handleControlVM(HandlerArg *a)
{
    using namespace com;
    HRESULT rc;

    if (a->argc < 2)
        return errorSyntax(USAGE_CONTROLVM, "Not enough parameters");

    /* try to find the given machine */
    ComPtr <IMachine> machine;
    CHECK_ERROR(a->virtualBox, FindMachine(Bstr(a->argv[0]).raw(),
                                           machine.asOutParam()));
    if (FAILED(rc))
        return 1;

    /* open a session for the VM */
    CHECK_ERROR_RET(machine, LockMachine(a->session, LockType_Shared), 1);

    do
    {
        /* get the associated console */
        ComPtr<IConsole> console;
        CHECK_ERROR_BREAK(a->session, COMGETTER(Console)(console.asOutParam()));
        /* ... and session machine */
        ComPtr<IMachine> sessionMachine;
        CHECK_ERROR_BREAK(a->session, COMGETTER(Machine)(sessionMachine.asOutParam()));

        /* which command? */
        if (!strcmp(a->argv[1], "pause"))
        {
            CHECK_ERROR_BREAK(console, Pause());
        }
        else if (!strcmp(a->argv[1], "resume"))
        {
            CHECK_ERROR_BREAK(console, Resume());
        }
        else if (!strcmp(a->argv[1], "reset"))
        {
            CHECK_ERROR_BREAK(console, Reset());
        }
        else if (!strcmp(a->argv[1], "unplugcpu"))
        {
            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'. Expected CPU number.", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            unsigned n = parseNum(a->argv[2], 32, "CPU");

            CHECK_ERROR_BREAK(sessionMachine, HotUnplugCPU(n));
        }
        else if (!strcmp(a->argv[1], "plugcpu"))
        {
            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'. Expected CPU number.", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            unsigned n = parseNum(a->argv[2], 32, "CPU");

            CHECK_ERROR_BREAK(sessionMachine, HotPlugCPU(n));
        }
        else if (!strcmp(a->argv[1], "cpuexecutioncap"))
        {
            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'. Expected execution cap number.", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            unsigned n = parseNum(a->argv[2], 100, "ExecutionCap");

            CHECK_ERROR_BREAK(sessionMachine, COMSETTER(CPUExecutionCap)(n));
        }
        else if (!strcmp(a->argv[1], "clipboard"))
        {
            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'. Expected clipboard mode.", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            ClipboardMode_T mode;
            if (!strcmp(a->argv[2], "disabled"))
                mode = ClipboardMode_Disabled;
            else if (!strcmp(a->argv[2], "hosttoguest"))
                mode = ClipboardMode_HostToGuest;
            else if (!strcmp(a->argv[2], "guesttohost"))
                mode = ClipboardMode_GuestToHost;
            else if (!strcmp(a->argv[2], "bidirectional"))
                mode = ClipboardMode_Bidirectional;
            else
            {
                errorArgument("Invalid '%s' argument '%s'.", a->argv[1], a->argv[2]);
                rc = E_FAIL;
            }
            if (SUCCEEDED(rc))
            {
                CHECK_ERROR_BREAK(sessionMachine, COMSETTER(ClipboardMode)(mode));
            }
        }
        else if (!strcmp(a->argv[1], "draganddrop"))
        {
            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'. Expected drag'n'drop mode.", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            DragAndDropMode_T mode;
            if (!strcmp(a->argv[2], "disabled"))
                mode = DragAndDropMode_Disabled;
            else if (!strcmp(a->argv[2], "hosttoguest"))
                mode = DragAndDropMode_HostToGuest;
            else if (!strcmp(a->argv[2], "guesttohost"))
                mode = DragAndDropMode_GuestToHost;
            else if (!strcmp(a->argv[2], "bidirectional"))
                mode = DragAndDropMode_Bidirectional;
            else
            {
                errorArgument("Invalid '%s' argument '%s'.", a->argv[1], a->argv[2]);
                rc = E_FAIL;
            }
            if (SUCCEEDED(rc))
            {
                CHECK_ERROR_BREAK(sessionMachine, COMSETTER(DragAndDropMode)(mode));
            }
        }
        else if (!strcmp(a->argv[1], "poweroff"))
        {
            ComPtr<IProgress> progress;
            CHECK_ERROR_BREAK(console, PowerDown(progress.asOutParam()));

            rc = showProgress(progress);
            CHECK_PROGRESS_ERROR(progress, ("Failed to power off machine"));
        }
        else if (!strcmp(a->argv[1], "savestate"))
        {
            /* first pause so we don't trigger a live save which needs more time/resources */
            bool fPaused = false;
            rc = console->Pause();
            if (FAILED(rc))
            {
                bool fError = true;
                if (rc == VBOX_E_INVALID_VM_STATE)
                {
                    /* check if we are already paused */
                    MachineState_T machineState;
                    CHECK_ERROR_BREAK(console, COMGETTER(State)(&machineState));
                    /* the error code was lost by the previous instruction */
                    rc = VBOX_E_INVALID_VM_STATE;
                    if (machineState != MachineState_Paused)
                    {
                        RTMsgError("Machine in invalid state %d -- %s\n",
                                   machineState, machineStateToName(machineState, false));
                    }
                    else
                    {
                        fError = false;
                        fPaused = true;
                    }
                }
                if (fError)
                    break;
            }

            ComPtr<IProgress> progress;
            CHECK_ERROR(console, SaveState(progress.asOutParam()));
            if (FAILED(rc))
            {
                if (!fPaused)
                    console->Resume();
                break;
            }

            rc = showProgress(progress);
            CHECK_PROGRESS_ERROR(progress, ("Failed to save machine state"));
            if (FAILED(rc))
            {
                if (!fPaused)
                    console->Resume();
            }
        }
        else if (!strcmp(a->argv[1], "acpipowerbutton"))
        {
            CHECK_ERROR_BREAK(console, PowerButton());
        }
        else if (!strcmp(a->argv[1], "acpisleepbutton"))
        {
            CHECK_ERROR_BREAK(console, SleepButton());
        }
        else if (!strcmp(a->argv[1], "keyboardputscancode"))
        {
            ComPtr<IKeyboard> keyboard;
            CHECK_ERROR_BREAK(console, COMGETTER(Keyboard)(keyboard.asOutParam()));

            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'. Expected IBM PC AT set 2 keyboard scancode(s) as hex byte(s).", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            std::list<LONG> llScancodes;

            /* Process the command line. */
            int i;
            for (i = 1 + 1; i < a->argc; i++)
            {
                if (   RT_C_IS_XDIGIT (a->argv[i][0])
                    && RT_C_IS_XDIGIT (a->argv[i][1])
                    && a->argv[i][2] == 0)
                {
                    uint8_t u8Scancode;
                    int irc = RTStrToUInt8Ex(a->argv[i], NULL, 16, &u8Scancode);
                    if (RT_FAILURE (irc))
                    {
                        RTMsgError("Converting '%s' returned %Rrc!", a->argv[i], rc);
                        rc = E_FAIL;
                        break;
                    }

                    llScancodes.push_back(u8Scancode);
                }
                else
                {
                    RTMsgError("Error: '%s' is not a hex byte!", a->argv[i]);
                    rc = E_FAIL;
                    break;
                }
            }

            if (FAILED(rc))
                break;

            /* Send scancodes to the VM. */
            com::SafeArray<LONG> saScancodes(llScancodes);
            ULONG codesStored = 0;
            CHECK_ERROR_BREAK(keyboard, PutScancodes(ComSafeArrayAsInParam(saScancodes),
                                                     &codesStored));
            if (codesStored < saScancodes.size())
            {
                RTMsgError("Only %d scancodes were stored", codesStored);
                rc = E_FAIL;
                break;
            }
        }
        else if (!strncmp(a->argv[1], "setlinkstate", 12))
        {
            /* Get the number of network adapters */
            ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);

            unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
            if (!n)
            {
                rc = E_FAIL;
                break;
            }
            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }
            /* get the corresponding network adapter */
            ComPtr<INetworkAdapter> adapter;
            CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
            if (adapter)
            {
                if (!strcmp(a->argv[2], "on"))
                {
                    CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(TRUE));
                }
                else if (!strcmp(a->argv[2], "off"))
                {
                    CHECK_ERROR_BREAK(adapter, COMSETTER(CableConnected)(FALSE));
                }
                else
                {
                    errorArgument("Invalid link state '%s'", Utf8Str(a->argv[2]).c_str());
                    rc = E_FAIL;
                    break;
                }
            }
        }
        /* here the order in which strncmp is called is important
         * cause nictracefile can be very well compared with
         * nictrace and nic and thus everything will always fail
         * if the order is changed
         */
        else if (!strncmp(a->argv[1], "nictracefile", 12))
        {
            /* Get the number of network adapters */
            ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
            unsigned n = parseNum(&a->argv[1][12], NetworkAdapterCount, "NIC");
            if (!n)
            {
                rc = E_FAIL;
                break;
            }
            if (a->argc <= 2)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            /* get the corresponding network adapter */
            ComPtr<INetworkAdapter> adapter;
            CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
            if (adapter)
            {
                BOOL fEnabled;
                adapter->COMGETTER(Enabled)(&fEnabled);
                if (fEnabled)
                {
                    if (a->argv[2])
                    {
                        CHECK_ERROR_RET(adapter, COMSETTER(TraceFile)(Bstr(a->argv[2]).raw()), 1);
                    }
                    else
                    {
                        errorArgument("Invalid filename or filename not specified for NIC %lu", n);
                        rc = E_FAIL;
                        break;
                    }
                }
                else
                    RTMsgError("The NIC %d is currently disabled and thus its tracefile can't be changed", n);
            }
        }
        else if (!strncmp(a->argv[1], "nictrace", 8))
        {
            /* Get the number of network adapters */
            ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);

            unsigned n = parseNum(&a->argv[1][8], NetworkAdapterCount, "NIC");
            if (!n)
            {
                rc = E_FAIL;
                break;
            }
            if (a->argc <= 2)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            /* get the corresponding network adapter */
            ComPtr<INetworkAdapter> adapter;
            CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
            if (adapter)
            {
                BOOL fEnabled;
                adapter->COMGETTER(Enabled)(&fEnabled);
                if (fEnabled)
                {
                    if (!strcmp(a->argv[2], "on"))
                    {
                        CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(TRUE), 1);
                    }
                    else if (!strcmp(a->argv[2], "off"))
                    {
                        CHECK_ERROR_RET(adapter, COMSETTER(TraceEnabled)(FALSE), 1);
                    }
                    else
                    {
                        errorArgument("Invalid nictrace%lu argument '%s'", n, Utf8Str(a->argv[2]).c_str());
                        rc = E_FAIL;
                        break;
                    }
                }
                else
                    RTMsgError("The NIC %d is currently disabled and thus its trace flag can't be changed", n);
            }
        }
        else if(   a->argc > 2
                && !strncmp(a->argv[1], "natpf", 5))
        {
            /* Get the number of network adapters */
            ULONG NetworkAdapterCount = getMaxNics(a->virtualBox, sessionMachine);
            ComPtr<INATEngine> engine;
            unsigned n = parseNum(&a->argv[1][5], NetworkAdapterCount, "NIC");
            if (!n)
            {
                rc = E_FAIL;
                break;
            }
            if (a->argc <= 2)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            /* get the corresponding network adapter */
            ComPtr<INetworkAdapter> adapter;
            CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
            if (!adapter)
            {
                rc = E_FAIL;
                break;
            }
            CHECK_ERROR(adapter, COMGETTER(NATEngine)(engine.asOutParam()));
            if (!engine)
            {
                rc = E_FAIL;
                break;
            }

            if (!strcmp(a->argv[2], "delete"))
            {
                if (a->argc >= 3)
                    CHECK_ERROR(engine, RemoveRedirect(Bstr(a->argv[3]).raw()));
            }
            else
            {
#define ITERATE_TO_NEXT_TERM(ch)                                           \
    do {                                                                   \
        while (*ch != ',')                                                 \
        {                                                                  \
            if (*ch == 0)                                                  \
            {                                                              \
                return errorSyntax(USAGE_CONTROLVM,                        \
                                   "Missing or invalid argument to '%s'",  \
                                    a->argv[1]);                           \
            }                                                              \
            ch++;                                                          \
        }                                                                  \
        *ch = '\0';                                                        \
        ch++;                                                              \
    } while(0)

                char *strName;
                char *strProto;
                char *strHostIp;
                char *strHostPort;
                char *strGuestIp;
                char *strGuestPort;
                char *strRaw = RTStrDup(a->argv[2]);
                char *ch = strRaw;
                strName = RTStrStrip(ch);
                ITERATE_TO_NEXT_TERM(ch);
                strProto = RTStrStrip(ch);
                ITERATE_TO_NEXT_TERM(ch);
                strHostIp = RTStrStrip(ch);
                ITERATE_TO_NEXT_TERM(ch);
                strHostPort = RTStrStrip(ch);
                ITERATE_TO_NEXT_TERM(ch);
                strGuestIp = RTStrStrip(ch);
                ITERATE_TO_NEXT_TERM(ch);
                strGuestPort = RTStrStrip(ch);
                NATProtocol_T proto;
                if (RTStrICmp(strProto, "udp") == 0)
                    proto = NATProtocol_UDP;
                else if (RTStrICmp(strProto, "tcp") == 0)
                    proto = NATProtocol_TCP;
                else
                {
                    return errorSyntax(USAGE_CONTROLVM,
                                       "Wrong rule proto '%s' specified -- only 'udp' and 'tcp' are allowed.",
                                       strProto);
                }
                CHECK_ERROR(engine, AddRedirect(Bstr(strName).raw(), proto, Bstr(strHostIp).raw(),
                        RTStrToUInt16(strHostPort), Bstr(strGuestIp).raw(), RTStrToUInt16(strGuestPort)));
#undef ITERATE_TO_NEXT_TERM
            }
            /* commit changes */
            if (SUCCEEDED(rc))
                CHECK_ERROR(sessionMachine, SaveSettings());
        }
        else if (!strncmp(a->argv[1], "nicproperty", 11))
        {
            /* Get the number of network adapters */
            ULONG NetworkAdapterCount = getMaxNics(a->virtualBox,sessionMachine) ;
            unsigned n = parseNum(&a->argv[1][11], NetworkAdapterCount, "NIC");
            if (!n)
            {
                rc = E_FAIL;
                break;
            }
            if (a->argc <= 2)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            /* get the corresponding network adapter */
            ComPtr<INetworkAdapter> adapter;
            CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
            if (adapter)
            {
                BOOL fEnabled;
                adapter->COMGETTER(Enabled)(&fEnabled);
                if (fEnabled)
                {
                    /* Parse 'name=value' */
                    char *pszProperty = RTStrDup(a->argv[2]);
                    if (pszProperty)
                    {
                        char *pDelimiter = strchr(pszProperty, '=');
                        if (pDelimiter)
                        {
                            *pDelimiter = '\0';

                            Bstr bstrName = pszProperty;
                            Bstr bstrValue = &pDelimiter[1];
                            CHECK_ERROR(adapter, SetProperty(bstrName.raw(), bstrValue.raw()));
                        }
                        else
                        {
                            errorArgument("Invalid nicproperty%d argument '%s'", n, a->argv[2]);
                            rc = E_FAIL;
                        }
                        RTStrFree(pszProperty);
                    }
                    else
                    {
                        RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for nicproperty%d '%s'\n", n, a->argv[2]);
                        rc = E_FAIL;
                    }
                    if (FAILED(rc))
                        break;
                }
                else
                    RTMsgError("The NIC %d is currently disabled and thus its properties can't be changed", n);
            }
        }
        else if (!strncmp(a->argv[1], "nic", 3))
        {
            /* Get the number of network adapters */
            ULONG NetworkAdapterCount = getMaxNics(a->virtualBox,sessionMachine) ;
            unsigned n = parseNum(&a->argv[1][3], NetworkAdapterCount, "NIC");
            if (!n)
            {
                rc = E_FAIL;
                break;
            }
            if (a->argc <= 2)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            /* get the corresponding network adapter */
            ComPtr<INetworkAdapter> adapter;
            CHECK_ERROR_BREAK(sessionMachine, GetNetworkAdapter(n - 1, adapter.asOutParam()));
            if (adapter)
            {
                BOOL fEnabled;
                adapter->COMGETTER(Enabled)(&fEnabled);
                if (fEnabled)
                {
                    if (!strcmp(a->argv[2], "null"))
                    {
                        CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Null), 1);
                    }
                    else if (!strcmp(a->argv[2], "nat"))
                    {
                        CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
                        if (a->argc == 4)
                            CHECK_ERROR_RET(adapter, COMSETTER(NATNetwork)(Bstr(a->argv[3]).raw()), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_NAT), 1);
                    }
                    else if (  !strcmp(a->argv[2], "bridged")
                            || !strcmp(a->argv[2], "hostif")) /* backward compatibility */
                    {
                        if (a->argc <= 3)
                        {
                            errorArgument("Missing argument to '%s'", a->argv[2]);
                            rc = E_FAIL;
                            break;
                        }
                        CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(BridgedInterface)(Bstr(a->argv[3]).raw()), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Bridged), 1);
                    }
                    else if (!strcmp(a->argv[2], "intnet"))
                    {
                        if (a->argc <= 3)
                        {
                            errorArgument("Missing argument to '%s'", a->argv[2]);
                            rc = E_FAIL;
                            break;
                        }
                        CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(InternalNetwork)(Bstr(a->argv[3]).raw()), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Internal), 1);
                    }
#if defined(VBOX_WITH_NETFLT)
                    else if (!strcmp(a->argv[2], "hostonly"))
                    {
                        if (a->argc <= 3)
                        {
                            errorArgument("Missing argument to '%s'", a->argv[2]);
                            rc = E_FAIL;
                            break;
                        }
                        CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(HostOnlyInterface)(Bstr(a->argv[3]).raw()), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_HostOnly), 1);
                    }
#endif
                    else if (!strcmp(a->argv[2], "generic"))
                    {
                        if (a->argc <= 3)
                        {
                            errorArgument("Missing argument to '%s'", a->argv[2]);
                            rc = E_FAIL;
                            break;
                        }
                        CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(GenericDriver)(Bstr(a->argv[3]).raw()), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), 1);
                    }
                    /** @todo obsolete, remove eventually */
                    else if (!strcmp(a->argv[2], "vde"))
                    {
                        if (a->argc <= 3)
                        {
                            errorArgument("Missing argument to '%s'", a->argv[2]);
                            rc = E_FAIL;
                            break;
                        }
                        CHECK_ERROR_RET(adapter, COMSETTER(Enabled)(TRUE), 1);
                        CHECK_ERROR_RET(adapter, COMSETTER(AttachmentType)(NetworkAttachmentType_Generic), 1);
                        CHECK_ERROR_RET(adapter, SetProperty(Bstr("name").raw(), Bstr(a->argv[3]).raw()), 1);
                    }
                    else
                    {
                        errorArgument("Invalid type '%s' specfied for NIC %lu", Utf8Str(a->argv[2]).c_str(), n);
                        rc = E_FAIL;
                        break;
                    }
                }
                else
                    RTMsgError("The NIC %d is currently disabled and thus its attachment type can't be changed", n);
            }
        }
        else if (   !strcmp(a->argv[1], "vrde")
                 || !strcmp(a->argv[1], "vrdp"))
        {
            if (!strcmp(a->argv[1], "vrdp"))
                RTStrmPrintf(g_pStdErr, "Warning: 'vrdp' is deprecated. Use 'vrde'.\n");

            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }
            ComPtr<IVRDEServer> vrdeServer;
            sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
            ASSERT(vrdeServer);
            if (vrdeServer)
            {
                if (!strcmp(a->argv[2], "on"))
                {
                    CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(TRUE));
                }
                else if (!strcmp(a->argv[2], "off"))
                {
                    CHECK_ERROR_BREAK(vrdeServer, COMSETTER(Enabled)(FALSE));
                }
                else
                {
                    errorArgument("Invalid remote desktop server state '%s'", Utf8Str(a->argv[2]).c_str());
                    rc = E_FAIL;
                    break;
                }
            }
        }
        else if (   !strcmp(a->argv[1], "vrdeport")
                 || !strcmp(a->argv[1], "vrdpport"))
        {
            if (!strcmp(a->argv[1], "vrdpport"))
                RTStrmPrintf(g_pStdErr, "Warning: 'vrdpport' is deprecated. Use 'vrdeport'.\n");

            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }

            ComPtr<IVRDEServer> vrdeServer;
            sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
            ASSERT(vrdeServer);
            if (vrdeServer)
            {
                Bstr ports;

                if (!strcmp(a->argv[2], "default"))
                    ports = "0";
                else
                    ports = a->argv[2];

                CHECK_ERROR_BREAK(vrdeServer, SetVRDEProperty(Bstr("TCP/Ports").raw(), ports.raw()));
            }
        }
        else if (   !strcmp(a->argv[1], "vrdevideochannelquality")
                 || !strcmp(a->argv[1], "vrdpvideochannelquality"))
        {
            if (!strcmp(a->argv[1], "vrdpvideochannelquality"))
                RTStrmPrintf(g_pStdErr, "Warning: 'vrdpvideochannelquality' is deprecated. Use 'vrdevideochannelquality'.\n");

            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }
            ComPtr<IVRDEServer> vrdeServer;
            sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
            ASSERT(vrdeServer);
            if (vrdeServer)
            {
                Bstr value = a->argv[2];

                CHECK_ERROR(vrdeServer, SetVRDEProperty(Bstr("VideoChannel/Quality").raw(), value.raw()));
            }
        }
        else if (!strcmp(a->argv[1], "vrdeproperty"))
        {
            if (a->argc <= 1 + 1)
            {
                errorArgument("Missing argument to '%s'", a->argv[1]);
                rc = E_FAIL;
                break;
            }
            ComPtr<IVRDEServer> vrdeServer;
            sessionMachine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
            ASSERT(vrdeServer);
            if (vrdeServer)
            {
                /* Parse 'name=value' */
                char *pszProperty = RTStrDup(a->argv[2]);
                if (pszProperty)
                {
                    char *pDelimiter = strchr(pszProperty, '=');
                    if (pDelimiter)
                    {
                        *pDelimiter = '\0';

                        Bstr bstrName = pszProperty;
                        Bstr bstrValue = &pDelimiter[1];
                        CHECK_ERROR(vrdeServer, SetVRDEProperty(bstrName.raw(), bstrValue.raw()));
                    }
                    else
                    {
                        errorArgument("Invalid vrdeproperty argument '%s'", a->argv[2]);
                        rc = E_FAIL;
                    }
                    RTStrFree(pszProperty);
                }
                else
                {
                    RTStrmPrintf(g_pStdErr, "Error: Failed to allocate memory for VRDE property '%s'\n", a->argv[2]);
                    rc = E_FAIL;
                }
            }
            if (FAILED(rc))
            {
                break;
            }
        }
        else if (   !strcmp(a->argv[1], "usbattach")
                 || !strcmp(a->argv[1], "usbdetach"))
        {
            if (a->argc < 3)
            {
                errorSyntax(USAGE_CONTROLVM, "Not enough parameters");
                rc = E_FAIL;
                break;
            }

            bool attach = !strcmp(a->argv[1], "usbattach");

            Bstr usbId = a->argv[2];
            if (Guid(usbId).isEmpty())
            {
                // assume address
                if (attach)
                {
                    ComPtr <IHost> host;
                    CHECK_ERROR_BREAK(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
                    SafeIfaceArray <IHostUSBDevice> coll;
                    CHECK_ERROR_BREAK(host, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
                    ComPtr <IHostUSBDevice> dev;
                    CHECK_ERROR_BREAK(host, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
                                                                   dev.asOutParam()));
                    CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
                }
                else
                {
                    SafeIfaceArray <IUSBDevice> coll;
                    CHECK_ERROR_BREAK(console, COMGETTER(USBDevices)(ComSafeArrayAsOutParam(coll)));
                    ComPtr <IUSBDevice> dev;
                    CHECK_ERROR_BREAK(console, FindUSBDeviceByAddress(Bstr(a->argv[2]).raw(),
                                                                      dev.asOutParam()));
                    CHECK_ERROR_BREAK(dev, COMGETTER(Id)(usbId.asOutParam()));
                }
            }

            if (attach)
                CHECK_ERROR_BREAK(console, AttachUSBDevice(usbId.raw()));
            else
            {
                ComPtr <IUSBDevice> dev;
                CHECK_ERROR_BREAK(console, DetachUSBDevice(usbId.raw(),
                                                           dev.asOutParam()));
            }
        }
        else if (!strcmp(a->argv[1], "setvideomodehint"))
        {
            if (a->argc != 5 && a->argc != 6 && a->argc != 7 && a->argc != 9)
            {
                errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
                rc = E_FAIL;
                break;
            }
            bool fEnabled = true;
            uint32_t uXRes = RTStrToUInt32(a->argv[2]);
            uint32_t uYRes = RTStrToUInt32(a->argv[3]);
            uint32_t uBpp  = RTStrToUInt32(a->argv[4]);
            uint32_t uDisplayIdx = 0;
            bool fChangeOrigin = false;
            int32_t iOriginX = 0;
            int32_t iOriginY = 0;
            if (a->argc >= 6)
                uDisplayIdx = RTStrToUInt32(a->argv[5]);
            if (a->argc >= 7)
            {
                int vrc = parseBool(a->argv[6], &fEnabled);
                if (RT_FAILURE(vrc))
                {
                    errorSyntax(USAGE_CONTROLVM, "Either \"yes\" or \"no\" is expected");
                    rc = E_FAIL;
                    break;
                }
                fEnabled = !RTStrICmp(a->argv[6], "yes");
            }
            if (a->argc == 9)
            {
                iOriginX = RTStrToInt32(a->argv[7]);
                iOriginY = RTStrToInt32(a->argv[8]);
                fChangeOrigin = true;
            }

            ComPtr<IDisplay> display;
            CHECK_ERROR_BREAK(console, COMGETTER(Display)(display.asOutParam()));
            CHECK_ERROR_BREAK(display, SetVideoModeHint(uDisplayIdx, fEnabled,
                                                        fChangeOrigin, iOriginX, iOriginY,
                                                        uXRes, uYRes, uBpp));
        }
        else if (!strcmp(a->argv[1], "setcredentials"))
        {
            bool fAllowLocalLogon = true;
            if (   a->argc == 7
                || (   a->argc == 8
                    && (   !strcmp(a->argv[3], "-p")
                        || !strcmp(a->argv[3], "--passwordfile"))))
            {
                if (   strcmp(a->argv[5 + (a->argc - 7)], "--allowlocallogon")
                    && strcmp(a->argv[5 + (a->argc - 7)], "-allowlocallogon"))
                {
                    errorArgument("Invalid parameter '%s'", a->argv[5]);
                    rc = E_FAIL;
                    break;
                }
                if (!strcmp(a->argv[6 + (a->argc - 7)], "no"))
                    fAllowLocalLogon = false;
            }
            else if (   a->argc != 5
                     && (   a->argc != 6
                         || (   strcmp(a->argv[3], "-p")
                             && strcmp(a->argv[3], "--passwordfile"))))
            {
                errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
                rc = E_FAIL;
                break;
            }
            Utf8Str passwd, domain;
            if (a->argc == 5 || a->argc == 7)
            {
                passwd = a->argv[3];
                domain = a->argv[4];
            }
            else
            {
                RTEXITCODE rcExit = readPasswordFile(a->argv[4], &passwd);
                if (rcExit != RTEXITCODE_SUCCESS)
                {
                    rc = E_FAIL;
                    break;
                }
                domain = a->argv[5];
            }

            ComPtr<IGuest> guest;
            CHECK_ERROR_BREAK(console, COMGETTER(Guest)(guest.asOutParam()));
            CHECK_ERROR_BREAK(guest, SetCredentials(Bstr(a->argv[2]).raw(),
                                                    Bstr(passwd).raw(),
                                                    Bstr(domain).raw(),
                                                    fAllowLocalLogon));
        }
#if 0 /* TODO: review & remove */
        else if (!strcmp(a->argv[1], "dvdattach"))
        {
            Bstr uuid;
            if (a->argc != 3)
            {
                errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
                rc = E_FAIL;
                break;
            }

            ComPtr<IMedium> dvdMedium;

            /* unmount? */
            if (!strcmp(a->argv[2], "none"))
            {
                /* nothing to do, NULL object will cause unmount */
            }
            /* host drive? */
            else if (!strncmp(a->argv[2], "host:", 5))
            {
                ComPtr<IHost> host;
                CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));

                rc = host->FindHostDVDDrive(Bstr(a->argv[2] + 5), dvdMedium.asOutParam());
                if (!dvdMedium)
                {
                    errorArgument("Invalid host DVD drive name \"%s\"",
                                  a->argv[2] + 5);
                    rc = E_FAIL;
                    break;
                }
            }
            else
            {
                /* first assume it's a UUID */
                uuid = a->argv[2];
                rc = a->virtualBox->GetDVDImage(uuid, dvdMedium.asOutParam());
                if (FAILED(rc) || !dvdMedium)
                {
                    /* must be a filename, check if it's in the collection */
                    rc = a->virtualBox->FindDVDImage(Bstr(a->argv[2]), dvdMedium.asOutParam());
                    /* not registered, do that on the fly */
                    if (!dvdMedium)
                    {
                        Bstr emptyUUID;
                        CHECK_ERROR(a->virtualBox, OpenDVDImage(Bstr(a->argv[2]), emptyUUID, dvdMedium.asOutParam()));
                    }
                }
                if (!dvdMedium)
                {
                    rc = E_FAIL;
                    break;
                }
            }

            /** @todo generalize this, allow arbitrary number of DVD drives
             * and as a consequence multiple attachments and different
             * storage controllers. */
            if (dvdMedium)
                dvdMedium->COMGETTER(Id)(uuid.asOutParam());
            else
                uuid = Guid().toString();
            CHECK_ERROR(machine, MountMedium(Bstr("IDE Controller"), 1, 0, uuid, FALSE /* aForce */));
        }
        else if (!strcmp(a->argv[1], "floppyattach"))
        {
            Bstr uuid;
            if (a->argc != 3)
            {
                errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
                rc = E_FAIL;
                break;
            }

            ComPtr<IMedium> floppyMedium;

            /* unmount? */
            if (!strcmp(a->argv[2], "none"))
            {
                /* nothing to do, NULL object will cause unmount */
            }
            /* host drive? */
            else if (!strncmp(a->argv[2], "host:", 5))
            {
                ComPtr<IHost> host;
                CHECK_ERROR(a->virtualBox, COMGETTER(Host)(host.asOutParam()));
                host->FindHostFloppyDrive(Bstr(a->argv[2] + 5), floppyMedium.asOutParam());
                if (!floppyMedium)
                {
                    errorArgument("Invalid host floppy drive name \"%s\"",
                                  a->argv[2] + 5);
                    rc = E_FAIL;
                    break;
                }
            }
            else
            {
                /* first assume it's a UUID */
                uuid = a->argv[2];
                rc = a->virtualBox->GetFloppyImage(uuid, floppyMedium.asOutParam());
                if (FAILED(rc) || !floppyMedium)
                {
                    /* must be a filename, check if it's in the collection */
                    rc = a->virtualBox->FindFloppyImage(Bstr(a->argv[2]), floppyMedium.asOutParam());
                    /* not registered, do that on the fly */
                    if (!floppyMedium)
                    {
                        Bstr emptyUUID;
                        CHECK_ERROR(a->virtualBox, OpenFloppyImage(Bstr(a->argv[2]), emptyUUID, floppyMedium.asOutParam()));
                    }
                }
                if (!floppyMedium)
                {
                    rc = E_FAIL;
                    break;
                }
            }
            floppyMedium->COMGETTER(Id)(uuid.asOutParam());
            CHECK_ERROR(machine, MountMedium(Bstr("Floppy Controller"), 0, 0, uuid, FALSE /* aForce */));
        }
#endif /* obsolete dvdattach/floppyattach */
        else if (!strcmp(a->argv[1], "guestmemoryballoon"))
        {
            if (a->argc != 3)
            {
                errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
                rc = E_FAIL;
                break;
            }
            uint32_t uVal;
            int vrc;
            vrc = RTStrToUInt32Ex(a->argv[2], NULL, 0, &uVal);
            if (vrc != VINF_SUCCESS)
            {
                errorArgument("Error parsing guest memory balloon size '%s'", a->argv[2]);
                rc = E_FAIL;
                break;
            }
            /* guest is running; update IGuest */
            ComPtr <IGuest> guest;
            rc = console->COMGETTER(Guest)(guest.asOutParam());
            if (SUCCEEDED(rc))
                CHECK_ERROR(guest, COMSETTER(MemoryBalloonSize)(uVal));
        }
        else if (!strcmp(a->argv[1], "teleport"))
        {
            Bstr        bstrHostname;
            uint32_t    uMaxDowntime = 250 /*ms*/;
            uint32_t    uPort        = UINT32_MAX;
            uint32_t    cMsTimeout   = 0;
            Utf8Str     strPassword;
            static const RTGETOPTDEF s_aTeleportOptions[] =
            {
                { "--host",              'h', RTGETOPT_REQ_STRING }, /** @todo RTGETOPT_FLAG_MANDATORY */
                { "--hostname",          'h', RTGETOPT_REQ_STRING }, /** @todo remove this */
                { "--maxdowntime",       'd', RTGETOPT_REQ_UINT32 },
                { "--port",              'P', RTGETOPT_REQ_UINT32 }, /** @todo RTGETOPT_FLAG_MANDATORY */
                { "--passwordfile",      'p', RTGETOPT_REQ_STRING },
                { "--password",          'W', RTGETOPT_REQ_STRING },
                { "--timeout",           't', RTGETOPT_REQ_UINT32 },
                { "--detailed-progress", 'D', RTGETOPT_REQ_NOTHING }
            };
            RTGETOPTSTATE GetOptState;
            RTGetOptInit(&GetOptState, a->argc, a->argv, s_aTeleportOptions, RT_ELEMENTS(s_aTeleportOptions), 2, RTGETOPTINIT_FLAGS_NO_STD_OPTS);
            int ch;
            RTGETOPTUNION Value;
            while (   SUCCEEDED(rc)
                   && (ch = RTGetOpt(&GetOptState, &Value)))
            {
                switch (ch)
                {
                    case 'h': bstrHostname  = Value.psz; break;
                    case 'd': uMaxDowntime  = Value.u32; break;
                    case 'D': g_fDetailedProgress = true; break;
                    case 'P': uPort         = Value.u32; break;
                    case 'p':
                    {
                        RTEXITCODE rcExit = readPasswordFile(Value.psz, &strPassword);
                        if (rcExit != RTEXITCODE_SUCCESS)
                            rc = E_FAIL;
                        break;
                    }
                    case 'W': strPassword   = Value.psz; break;
                    case 't': cMsTimeout    = Value.u32; break;
                    default:
                        errorGetOpt(USAGE_CONTROLVM, ch, &Value);
                        rc = E_FAIL;
                        break;
                }
            }
            if (FAILED(rc))
                break;

            ComPtr<IProgress> progress;
            CHECK_ERROR_BREAK(console, Teleport(bstrHostname.raw(), uPort,
                                                Bstr(strPassword).raw(),
                                                uMaxDowntime,
                                                progress.asOutParam()));

            if (cMsTimeout)
            {
                rc = progress->COMSETTER(Timeout)(cMsTimeout);
                if (FAILED(rc) && rc != VBOX_E_INVALID_OBJECT_STATE)
                    CHECK_ERROR_BREAK(progress, COMSETTER(Timeout)(cMsTimeout)); /* lazyness */
            }

            rc = showProgress(progress);
            CHECK_PROGRESS_ERROR(progress, ("Teleportation failed"));
        }
        else if (!strcmp(a->argv[1], "screenshotpng"))
        {
            if (a->argc <= 2 || a->argc > 4)
            {
                errorSyntax(USAGE_CONTROLVM, "Incorrect number of parameters");
                rc = E_FAIL;
                break;
            }
            int vrc;
            uint32_t displayIdx = 0;
            if (a->argc == 4)
            {
                vrc = RTStrToUInt32Ex(a->argv[3], NULL, 0, &displayIdx);
                if (vrc != VINF_SUCCESS)
                {
                    errorArgument("Error parsing display number '%s'", a->argv[3]);
                    rc = E_FAIL;
                    break;
                }
            }
            ComPtr<IDisplay> pDisplay;
            CHECK_ERROR_BREAK(console, COMGETTER(Display)(pDisplay.asOutParam()));
            ULONG width, height, bpp;
            CHECK_ERROR_BREAK(pDisplay, GetScreenResolution(displayIdx, &width, &height, &bpp));
            com::SafeArray<BYTE> saScreenshot;
            CHECK_ERROR_BREAK(pDisplay, TakeScreenShotPNGToArray(displayIdx, width, height, ComSafeArrayAsOutParam(saScreenshot)));
            RTFILE pngFile = NIL_RTFILE;
            vrc = RTFileOpen(&pngFile, a->argv[2], RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_TRUNCATE | RTFILE_O_DENY_ALL);
            if (RT_FAILURE(vrc))
            {
                RTMsgError("Failed to create file '%s'. rc=%Rrc", a->argv[2], vrc);
                rc = E_FAIL;
                break;
            }
            vrc = RTFileWrite(pngFile, saScreenshot.raw(), saScreenshot.size(), NULL);
            if (RT_FAILURE(vrc))
            {
                RTMsgError("Failed to write screenshot to file '%s'. rc=%Rrc", a->argv[2], vrc);
                rc = E_FAIL;
            }
            RTFileClose(pngFile);
        }
        else
        {
            errorSyntax(USAGE_CONTROLVM, "Invalid parameter '%s'", a->argv[1]);
            rc = E_FAIL;
        }
    } while (0);

    a->session->UnlockMachine();

    return SUCCEEDED(rc) ? 0 : 1;
}
コード例 #7
0
    STDMETHOD(HandleEvent)(VBoxEventType_T aType, IEvent *aEvent)
    {
        switch (aType)
        {
            case VBoxEventType_OnGuestPropertyChanged:
            {
                ComPtr<IGuestPropertyChangedEvent> gpcev = aEvent;
                Assert(gpcev);

                Bstr aKey;
                gpcev->COMGETTER(Name)(aKey.asOutParam());

                if (aKey == Bstr("/VirtualBox/GuestInfo/OS/NoLoggedInUsers"))
                {
                    /* Check if this is our machine and the "disconnect on logout feature" is enabled. */
                    BOOL fProcessDisconnectOnGuestLogout = FALSE;
                    ComPtr <IMachine> machine;
                    HRESULT hrc = S_OK;

                    if (gConsole)
                    {
                        hrc = gConsole->COMGETTER(Machine)(machine.asOutParam());
                        if (SUCCEEDED(hrc) && machine)
                        {
                            Bstr id, machineId;
                            hrc = machine->COMGETTER(Id)(id.asOutParam());
                            gpcev->COMGETTER(MachineId)(machineId.asOutParam());
                            if (id == machineId)
                            {
                                Bstr value1;
                                hrc = machine->GetExtraData(Bstr("VRDP/DisconnectOnGuestLogout").raw(),
                                                            value1.asOutParam());
                                if (SUCCEEDED(hrc) && value1 == "1")
                                {
                                    fProcessDisconnectOnGuestLogout = TRUE;
                                }
                            }
                        }
                    }

                    if (fProcessDisconnectOnGuestLogout)
                    {
                        bool fDropConnection = false;

                        Bstr value;
                        gpcev->COMGETTER(Value)(value.asOutParam());
                        Utf8Str utf8Value = value;

                        if (!mfNoLoggedInUsers) /* Only if the property really changes. */
                        {
                            if (   utf8Value == "true"
                                /* Guest property got deleted due to reset,
                                 * so it has no value anymore. */
                                || utf8Value.isEmpty())
                            {
                                mfNoLoggedInUsers = true;
                                fDropConnection = true;
                            }
                        }
                        else if (utf8Value == "false")
                            mfNoLoggedInUsers = false;
                        /* Guest property got deleted due to reset,
                         * take the shortcut without touching the mfNoLoggedInUsers
                         * state. */
                        else if (utf8Value.isEmpty())
                            fDropConnection = true;

                        if (fDropConnection)
                        {
                            /* If there is a connection, drop it. */
                            ComPtr<IVRDEServerInfo> info;
                            hrc = gConsole->COMGETTER(VRDEServerInfo)(info.asOutParam());
                            if (SUCCEEDED(hrc) && info)
                            {
                                ULONG cClients = 0;
                                hrc = info->COMGETTER(NumberOfClients)(&cClients);
                                if (SUCCEEDED(hrc) && cClients > 0)
                                {
                                    ComPtr <IVRDEServer> vrdeServer;
                                    hrc = machine->COMGETTER(VRDEServer)(vrdeServer.asOutParam());
                                    if (SUCCEEDED(hrc) && vrdeServer)
                                    {
                                        LogRel(("VRDE: the guest user has logged out, disconnecting remote clients.\n"));
                                        vrdeServer->COMSETTER(Enabled)(FALSE);
                                        vrdeServer->COMSETTER(Enabled)(TRUE);
                                    }
                                }
                            }
                        }
                    }
                }
                break;
            }
            default:
                AssertFailed();
        }

        return S_OK;
    }