/** * 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; }
/** * 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; }
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; }
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; }
/** * 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; }
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; }
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; }