diff options
author | Felix Geyer <debfx-pkg@fobos.de> | 2011-07-29 17:55:18 +0200 |
---|---|---|
committer | Felix Geyer <debfx-pkg@fobos.de> | 2011-07-29 17:55:18 +0200 |
commit | cba113ca2826bc4814be2f69a7704c865a37d4ea (patch) | |
tree | 511123b10dd1e58e56958520534f5c50e6f570fc /src/VBox/Main/src-client/ConsoleImpl.cpp | |
parent | 6a16f6900dd884e07125b51c9625f6be0a1f9b70 (diff) | |
download | virtualbox-cba113ca2826bc4814be2f69a7704c865a37d4ea.tar.gz |
Imported Upstream version 4.1.0-dfsgupstream/4.1.0-dfsg
Diffstat (limited to 'src/VBox/Main/src-client/ConsoleImpl.cpp')
-rw-r--r-- | src/VBox/Main/src-client/ConsoleImpl.cpp | 1631 |
1 files changed, 1229 insertions, 402 deletions
diff --git a/src/VBox/Main/src-client/ConsoleImpl.cpp b/src/VBox/Main/src-client/ConsoleImpl.cpp index c3d2025b7..126edde3a 100644 --- a/src/VBox/Main/src-client/ConsoleImpl.cpp +++ b/src/VBox/Main/src-client/ConsoleImpl.cpp @@ -1,4 +1,4 @@ -/* $Id: ConsoleImpl.cpp $ */ +/* $Id: ConsoleImpl.cpp 37851 2011-07-08 17:04:03Z vboxsync $ */ /** @file * VBox Console COM Class implementation */ @@ -56,6 +56,9 @@ #include "RemoteUSBDeviceImpl.h" #include "SharedFolderImpl.h" #include "AudioSnifferInterface.h" +#ifdef VBOX_WITH_USB_VIDEO +# include "UsbWebcamInterface.h" +#endif #include "ProgressCombinedImpl.h" #include "ConsoleVRDPServer.h" #include "VMMDev.h" @@ -136,7 +139,7 @@ * callers. * * If \a aUsesVMPtr parameter is true, the task structure will also add itself - * as a Console::mpVM caller with the same meaning as above. See + * as a Console::mpUVM caller with the same meaning as above. See * Console::addVMCaller() for more info. */ struct VMTask @@ -149,7 +152,9 @@ struct VMTask mConsoleCaller(aConsole), mProgress(aProgress), mServerProgress(aServerProgress), - mVMCallerAdded(false) + mpVM(NULL), + mRC(E_FAIL), + mpSafeVMPtr(NULL) { AssertReturnVoid(aConsole); mRC = mConsoleCaller.rc(); @@ -157,16 +162,17 @@ struct VMTask return; if (aUsesVMPtr) { - mRC = aConsole->addVMCaller(); - if (SUCCEEDED(mRC)) - mVMCallerAdded = true; + mpSafeVMPtr = new Console::SafeVMPtr(aConsole); + if (mpSafeVMPtr->isOk()) + mpVM = mpSafeVMPtr->raw(); + else + mRC = mpSafeVMPtr->rc(); } } ~VMTask() { - if (mVMCallerAdded) - mConsole->releaseVMCaller(); + releaseVMCaller(); } HRESULT rc() const { return mRC; } @@ -175,21 +181,23 @@ struct VMTask /** Releases the VM caller before destruction. Not normally necessary. */ void releaseVMCaller() { - AssertReturnVoid(mVMCallerAdded); - mConsole->releaseVMCaller(); - mVMCallerAdded = false; + if (mpSafeVMPtr) + { + delete mpSafeVMPtr; + mpSafeVMPtr = NULL; + } } - const ComObjPtr<Console> mConsole; - AutoCaller mConsoleCaller; - const ComObjPtr<Progress> mProgress; - Utf8Str mErrorMsg; - const ComPtr<IProgress> mServerProgress; + const ComObjPtr<Console> mConsole; + AutoCaller mConsoleCaller; + const ComObjPtr<Progress> mProgress; + Utf8Str mErrorMsg; + const ComPtr<IProgress> mServerProgress; + PVM mpVM; private: - - HRESULT mRC; - bool mVMCallerAdded : 1; + HRESULT mRC; + Console::SafeVMPtr *mpSafeVMPtr; }; struct VMTakeSnapshotTask : public VMTask @@ -265,9 +273,18 @@ inline static const char *networkAdapterTypeToName(NetworkAdapterType_T adapterT class VmEventListener { public: - VmEventListener(Console *aConsole) + VmEventListener() + {} + + + HRESULT init(Console *aConsole) { mConsole = aConsole; + return S_OK; + } + + void uninit() + { } virtual ~VmEventListener() @@ -313,8 +330,15 @@ public: mConsole->onNATRedirectRuleChange(ulSlot, fRemove, proto, hostIp.raw(), hostPort, guestIp.raw(), guestPort); } break; + + case VBoxEventType_OnHostPciDevicePlug: + { + // handle if needed + break; + } + default: - AssertFailed(); + AssertFailed(); } return S_OK; } @@ -334,7 +358,7 @@ VBOX_LISTENER_DECLARE(VmEventListenerImpl) Console::Console() : mSavedStateDataLoaded(false) , mConsoleVRDPServer(NULL) - , mpVM(NULL) + , mpUVM(NULL) , mVMCallers(0) , mVMZeroCallersSem(NIL_RTSEMEVENT) , mVMDestroying(false) @@ -346,8 +370,12 @@ Console::Console() , mpVmm2UserMethods(NULL) , m_pVMMDev(NULL) , mAudioSniffer(NULL) +#ifdef VBOX_WITH_USB_VIDEO + , mUsbWebcamInterface(NULL) +#endif , mBusMgr(NULL) , mVMStateChangeCallbackDisabled(false) + , mfUseHostClipboard(true) , mMachineState(MachineState_PoweredOff) { for (ULONG slot = 0; slot < SchemaDefs::NetworkAdapterCount; ++slot) @@ -383,7 +411,7 @@ HRESULT Console::FinalConstruct() pVmm2UserMethods->pConsole = this; mpVmm2UserMethods = pVmm2UserMethods; - return S_OK; + return BaseFinalConstruct(); } void Console::FinalRelease() @@ -391,6 +419,8 @@ void Console::FinalRelease() LogFlowThisFunc(("\n")); uninit(); + + BaseFinalRelease(); } // public initializer/uninitializer for internal purposes only @@ -480,6 +510,10 @@ HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl) unconst(mAudioSniffer) = new AudioSniffer(this); AssertReturn(mAudioSniffer, E_FAIL); +#ifdef VBOX_WITH_USB_VIDEO + unconst(mUsbWebcamInterface) = new UsbWebcamInterface(this); + AssertReturn(mUsbWebcamInterface, E_FAIL); +#endif /* VirtualBox events registration. */ { @@ -490,10 +524,14 @@ HRESULT Console::init(IMachine *aMachine, IInternalMachineControl *aControl) ComPtr<IEventSource> pES; rc = pVirtualBox->COMGETTER(EventSource)(pES.asOutParam()); AssertComRC(rc); - mVmListner = new VmEventListenerImpl(this); + ComObjPtr<VmEventListenerImpl> aVmListener; + aVmListener.createObject(); + aVmListener->init(new VmEventListener(), this); + mVmListener = aVmListener; com::SafeArray<VBoxEventType_T> eventTypes; eventTypes.push_back(VBoxEventType_OnNATRedirect); - rc = pES->RegisterListener(mVmListner, ComSafeArrayAsInParam(eventTypes), true); + eventTypes.push_back(VBoxEventType_OnHostPciDevicePlug); + rc = pES->RegisterListener(aVmListener, ComSafeArrayAsInParam(eventTypes), true); AssertComRC(rc); } @@ -529,7 +567,7 @@ void Console::uninit() } LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed())); - if (mVmListner) + if (mVmListener) { ComPtr<IEventSource> pES; ComPtr<IVirtualBox> pVirtualBox; @@ -541,18 +579,18 @@ void Console::uninit() AssertComRC(rc); if (!pES.isNull()) { - rc = pES->UnregisterListener(mVmListner); + rc = pES->UnregisterListener(mVmListener); AssertComRC(rc); } } - mVmListner->Release(); + mVmListener.setNull(); } /* power down the VM if necessary */ - if (mpVM) + if (mpUVM) { powerDown(); - Assert(mpVM == NULL); + Assert(mpUVM == NULL); } if (mVMZeroCallersSem != NIL_RTSEMEVENT) @@ -567,6 +605,14 @@ void Console::uninit() mpVmm2UserMethods = NULL; } +#ifdef VBOX_WITH_USB_VIDEO + if (mUsbWebcamInterface) + { + delete mUsbWebcamInterface; + unconst(mUsbWebcamInterface) = NULL; + } +#endif + if (mAudioSniffer) { delete mAudioSniffer; @@ -652,8 +698,13 @@ void Console::uninit() /** * Handles guest properties on a VM reset. - * At the moment we only delete properties which have the flag - * "TRANSRESET". + * + * We must delete properties that are flagged TRANSRESET. + * + * @todo r=bird: Would be more efficient if we added a request to the HGCM + * service to do this instead of detouring thru VBoxSVC. + * (IMachine::SetGuestProperty ends up in VBoxSVC, which in turns calls + * back into the VM process and the HGCM service.) */ void Console::guestPropertiesHandleVMReset(void) { @@ -1745,7 +1796,39 @@ STDMETHODIMP Console::COMGETTER(AttachedPciDevices)(ComSafeArrayOut(IPciDeviceAt AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - mBusMgr->listAttachedPciDevices(ComSafeArrayOutArg(aAttachments)); + if (mBusMgr) + mBusMgr->listAttachedPciDevices(ComSafeArrayOutArg(aAttachments)); + else + { + com::SafeIfaceArray<IPciDeviceAttachment> result((size_t)0); + result.detachTo(ComSafeArrayOutArg(aAttachments)); + } + + return S_OK; +} + +STDMETHODIMP Console::COMGETTER(UseHostClipboard)(BOOL *aUseHostClipboard) +{ + CheckComArgOutPointerValid(aUseHostClipboard); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + *aUseHostClipboard = mfUseHostClipboard; + + return S_OK; +} + +STDMETHODIMP Console::COMSETTER(UseHostClipboard)(BOOL aUseHostClipboard) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + mfUseHostClipboard = aUseHostClipboard; return S_OK; } @@ -1913,14 +1996,15 @@ STDMETHODIMP Console::Reset() ) return setInvalidMachineStateError(); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* protect mpUVM */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); /* leave the lock before a VMR3* call (EMT will call us back)! */ alock.leave(); - int vrc = VMR3Reset(mpVM); + int vrc = VMR3Reset(ptrVM); HRESULT rc = RT_SUCCESS(vrc) ? S_OK : setError(VBOX_E_VM_ERROR, @@ -1932,19 +2016,19 @@ STDMETHODIMP Console::Reset() return rc; } -DECLCALLBACK(int) Console::unplugCpu(Console *pThis, unsigned uCpu) +/*static*/ DECLCALLBACK(int) Console::unplugCpu(Console *pThis, PVM pVM, unsigned uCpu) { - LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu)); + LogFlowFunc(("pThis=%p pVM=%p uCpu=%u\n", pThis, pVM, uCpu)); AssertReturn(pThis, VERR_INVALID_PARAMETER); - int vrc = PDMR3DeviceDetach(pThis->mpVM, "acpi", 0, uCpu, 0); + int vrc = PDMR3DeviceDetach(pVM, "acpi", 0, uCpu, 0); Log(("UnplugCpu: rc=%Rrc\n", vrc)); return vrc; } -HRESULT Console::doCPURemove(ULONG aCpu) +HRESULT Console::doCPURemove(ULONG aCpu, PVM pVM) { HRESULT rc = S_OK; @@ -1957,8 +2041,8 @@ HRESULT Console::doCPURemove(ULONG aCpu) AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); AssertReturn(m_pVMMDev, E_FAIL); - PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort(); - AssertReturn(pDevPort, E_FAIL); + PPDMIVMMDEVPORT pVmmDevPort = m_pVMMDev->getVMMDevPort(); + AssertReturn(pVmmDevPort, E_FAIL); if ( mMachineState != MachineState_Running && mMachineState != MachineState_Teleporting @@ -1966,48 +2050,38 @@ HRESULT Console::doCPURemove(ULONG aCpu) ) return setInvalidMachineStateError(); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - /* Check if the CPU is present */ BOOL fCpuAttached; rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached); - if (FAILED(rc)) return rc; - + if (FAILED(rc)) + return rc; if (!fCpuAttached) - return setError(E_FAIL, - tr("CPU %d is not attached"), aCpu); + return setError(E_FAIL, tr("CPU %d is not attached"), aCpu); /* Leave the lock before any EMT/VMMDev call. */ alock.release(); + bool fLocked = true; /* Check if the CPU is unlocked */ PPDMIBASE pBase; - int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, aCpu, &pBase); - bool fLocked = true; + int vrc = PDMR3QueryDeviceLun(pVM, "acpi", 0, aCpu, &pBase); if (RT_SUCCESS(vrc)) { - uint32_t idCpuCore, idCpuPackage; - - /* Notify the guest if possible. */ - vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(mpVM, aCpu, &idCpuCore, &idCpuPackage); - AssertRC(vrc); - Assert(pBase); + PPDMIACPIPORT pApicPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT); - PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT); - - vrc = pDevPort->pfnCpuHotUnplug(pDevPort, idCpuCore, idCpuPackage); + /* Notify the guest if possible. */ + uint32_t idCpuCore, idCpuPackage; + vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc); + if (RT_SUCCESS(vrc)) + vrc = pVmmDevPort->pfnCpuHotUnplug(pVmmDevPort, idCpuCore, idCpuPackage); if (RT_SUCCESS(vrc)) { unsigned cTries = 100; - do { - /* It will take some time until the event is processed in the guest. Wait */ - vrc = pPort ? pPort->pfnGetCpuStatus(pPort, aCpu, &fLocked) : VERR_INVALID_POINTER; - + /* It will take some time until the event is processed in the guest. Wait... */ + vrc = pApicPort ? pApicPort->pfnGetCpuStatus(pApicPort, aCpu, &fLocked) : VERR_INVALID_POINTER; if (RT_SUCCESS(vrc) && !fLocked) break; @@ -2018,7 +2092,7 @@ HRESULT Console::doCPURemove(ULONG aCpu) else if (vrc == VERR_CPU_HOTPLUG_NOT_MONITORED_BY_GUEST) { /* Query one time. It is possible that the user ejected the CPU. */ - vrc = pPort ? pPort->pfnGetCpuStatus(pPort, aCpu, &fLocked) : VERR_INVALID_POINTER; + vrc = pApicPort ? pApicPort->pfnGetCpuStatus(pApicPort, aCpu, &fLocked) : VERR_INVALID_POINTER; } } @@ -2030,10 +2104,9 @@ HRESULT Console::doCPURemove(ULONG aCpu) * using VMR3ReqCall. */ PVMREQ pReq; - vrc = VMR3ReqCall(mpVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS, - (PFNRT)Console::unplugCpu, 2, - this, aCpu); - + vrc = VMR3ReqCall(pVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS, + (PFNRT)Console::unplugCpu, 3, + this, pVM, aCpu); if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc)) { vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT); @@ -2046,7 +2119,7 @@ HRESULT Console::doCPURemove(ULONG aCpu) if (RT_SUCCESS(vrc)) { /* Detach it from the VM */ - vrc = VMR3HotUnplugCpu(mpVM, aCpu); + vrc = VMR3HotUnplugCpu(pVM, aCpu); AssertRC(vrc); } else @@ -2062,16 +2135,16 @@ HRESULT Console::doCPURemove(ULONG aCpu) return rc; } -DECLCALLBACK(int) Console::plugCpu(Console *pThis, unsigned uCpu) +/*static*/ DECLCALLBACK(int) Console::plugCpu(Console *pThis, PVM pVM, unsigned uCpu) { LogFlowFunc(("pThis=%p uCpu=%u\n", pThis, uCpu)); AssertReturn(pThis, VERR_INVALID_PARAMETER); - int rc = VMR3HotPlugCpu(pThis->mpVM, uCpu); + int rc = VMR3HotPlugCpu(pVM, uCpu); AssertRC(rc); - PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRoot(pThis->mpVM), "Devices/acpi/0/"); + PCFGMNODE pInst = CFGMR3GetChild(CFGMR3GetRoot(pVM), "Devices/acpi/0/"); AssertRelease(pInst); /* nuke anything which might have been left behind. */ CFGMR3RemoveNode(CFGMR3GetChildF(pInst, "LUN#%d", uCpu)); @@ -2088,7 +2161,7 @@ DECLCALLBACK(int) Console::plugCpu(Console *pThis, unsigned uCpu) * Attach the driver. */ PPDMIBASE pBase; - rc = PDMR3DeviceAttach(pThis->mpVM, "acpi", 0, uCpu, 0, &pBase); RC_CHECK(); + rc = PDMR3DeviceAttach(pVM, "acpi", 0, uCpu, 0, &pBase); RC_CHECK(); Log(("PlugCpu: rc=%Rrc\n", rc)); @@ -2099,7 +2172,7 @@ DECLCALLBACK(int) Console::plugCpu(Console *pThis, unsigned uCpu) return VINF_SUCCESS; } -HRESULT Console::doCPUAdd(ULONG aCpu) +HRESULT Console::doCPUAdd(ULONG aCpu, PVM pVM) { HRESULT rc = S_OK; @@ -2122,10 +2195,6 @@ HRESULT Console::doCPUAdd(ULONG aCpu) PPDMIVMMDEVPORT pDevPort = m_pVMMDev->getVMMDevPort(); AssertReturn(pDevPort, E_FAIL); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - /* Check if the CPU is present */ BOOL fCpuAttached; rc = mMachine->GetCPUStatus(aCpu, &fCpuAttached); @@ -2141,9 +2210,9 @@ HRESULT Console::doCPUAdd(ULONG aCpu) * here to make requests from under the lock in order to serialize them. */ PVMREQ pReq; - int vrc = VMR3ReqCall(mpVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS, - (PFNRT)Console::plugCpu, 2, - this, aCpu); + int vrc = VMR3ReqCall(pVM, 0, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS, + (PFNRT)Console::plugCpu, 3, + this, pVM, aCpu); /* leave the lock before a VMR3* call (EMT will call us back)! */ alock.release(); @@ -2164,13 +2233,11 @@ HRESULT Console::doCPUAdd(ULONG aCpu) if (RT_SUCCESS(vrc)) { - uint32_t idCpuCore, idCpuPackage; - /* Notify the guest if possible. */ - vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(mpVM, aCpu, &idCpuCore, &idCpuPackage); - AssertRC(vrc); - - vrc = pDevPort->pfnCpuHotPlug(pDevPort, idCpuCore, idCpuPackage); + uint32_t idCpuCore, idCpuPackage; + vrc = VMR3GetCpuCoreAndPackageIdFromCpuId(pVM, aCpu, &idCpuCore, &idCpuPackage); AssertRC(vrc); + if (RT_SUCCESS(vrc)) + vrc = pDevPort->pfnCpuHotPlug(pDevPort, idCpuCore, idCpuPackage); /** @todo warning if the guest doesn't support it */ } @@ -2204,16 +2271,17 @@ STDMETHODIMP Console::Pause() return setInvalidMachineStateError(); } - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); LogFlowThisFunc(("Sending PAUSE request...\n")); /* leave the lock before a VMR3* call (EMT will call us back)! */ alock.leave(); - int vrc = VMR3Suspend(mpVM); + int vrc = VMR3Suspend(ptrVM); HRESULT hrc = S_OK; if (RT_FAILURE(vrc)) @@ -2238,9 +2306,10 @@ STDMETHODIMP Console::Resume() tr("Cannot resume the machine as it is not paused (machine state: %s)"), Global::stringifyMachineState(mMachineState)); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); LogFlowThisFunc(("Sending RESUME request...\n")); @@ -2248,16 +2317,16 @@ STDMETHODIMP Console::Resume() alock.leave(); #ifdef VBOX_WITH_EXTPACK - int vrc = mptrExtPackManager->callAllVmPowerOnHooks(this, mpVM); /** @todo called a few times too many... */ + int vrc = mptrExtPackManager->callAllVmPowerOnHooks(this, ptrVM); /** @todo called a few times too many... */ #else int vrc = VINF_SUCCESS; #endif if (RT_SUCCESS(vrc)) { - if (VMR3GetState(mpVM) == VMSTATE_CREATED) - vrc = VMR3PowerOn(mpVM); /* (PowerUpPaused) */ + if (VMR3GetState(ptrVM) == VMSTATE_CREATED) + vrc = VMR3PowerOn(ptrVM); /* (PowerUpPaused) */ else - vrc = VMR3Resume(mpVM); + vrc = VMR3Resume(ptrVM); } HRESULT rc = RT_SUCCESS(vrc) ? S_OK : @@ -2285,17 +2354,23 @@ STDMETHODIMP Console::PowerButton() ) return setInvalidMachineStateError(); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); +/** @todo leave the console lock? */ + /* get the acpi device interface and press the button. */ PPDMIBASE pBase; - int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase); + int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase); if (RT_SUCCESS(vrc)) { Assert(pBase); PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT); - vrc = pPort ? pPort->pfnPowerButtonPress(pPort) : VERR_INVALID_POINTER; + if (pPort) + vrc = pPort->pfnPowerButtonPress(pPort); + else + vrc = VERR_PDM_MISSING_INTERFACE; } HRESULT rc = RT_SUCCESS(vrc) ? S_OK : @@ -2326,18 +2401,28 @@ STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled) ) return setInvalidMachineStateError(); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); +/** @todo leave the console lock? */ + /* get the acpi device interface and check if the button press was handled. */ PPDMIBASE pBase; - int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase); - bool handled = false; + int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase); if (RT_SUCCESS(vrc)) { Assert(pBase); PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT); - vrc = pPort ? pPort->pfnGetPowerButtonHandled(pPort, &handled) : VERR_INVALID_POINTER; + if (pPort) + { + bool fHandled = false; + vrc = pPort->pfnGetPowerButtonHandled(pPort, &fHandled); + if (RT_SUCCESS(vrc)) + *aHandled = fHandled; + } + else + vrc = VERR_PDM_MISSING_INTERFACE; } HRESULT rc = RT_SUCCESS(vrc) ? S_OK : @@ -2345,8 +2430,6 @@ STDMETHODIMP Console::GetPowerButtonHandled(BOOL *aHandled) tr("Checking if the ACPI Power Button event was handled by the guest OS failed (%Rrc)"), vrc); - *aHandled = handled; - LogFlowThisFunc(("rc=%Rhrc\n", rc)); LogFlowThisFuncLeave(); return rc; @@ -2372,22 +2455,31 @@ STDMETHODIMP Console::GetGuestEnteredACPIMode(BOOL *aEntered) tr("Invalid machine state %s when checking if the guest entered the ACPI mode)"), Global::stringifyMachineState(mMachineState)); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); +/** @todo leave the console lock? */ + + /* get the acpi device interface and query the information. */ PPDMIBASE pBase; - int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase); - bool entered = false; + int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase); if (RT_SUCCESS(vrc)) { Assert(pBase); PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT); - vrc = pPort ? pPort->pfnGetGuestEnteredACPIMode(pPort, &entered) : VERR_INVALID_POINTER; + if (pPort) + { + bool fEntered = false; + vrc = pPort->pfnGetGuestEnteredACPIMode(pPort, &fEntered); + if (RT_SUCCESS(vrc)) + *aEntered = fEntered; + } + else + vrc = VERR_PDM_MISSING_INTERFACE; } - *aEntered = RT_SUCCESS(vrc) ? entered : false; - LogFlowThisFuncLeave(); return S_OK; } @@ -2404,17 +2496,24 @@ STDMETHODIMP Console::SleepButton() if (mMachineState != MachineState_Running) /** @todo Live Migration: ??? */ return setInvalidMachineStateError(); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); + +/** @todo leave the console lock? */ + /* get the acpi device interface and press the sleep button. */ PPDMIBASE pBase; - int vrc = PDMR3QueryDeviceLun(mpVM, "acpi", 0, 0, &pBase); + int vrc = PDMR3QueryDeviceLun(ptrVM, "acpi", 0, 0, &pBase); if (RT_SUCCESS(vrc)) { Assert(pBase); PPDMIACPIPORT pPort = PDMIBASE_QUERY_INTERFACE(pBase, PDMIACPIPORT); - vrc = pPort ? pPort->pfnSleepButtonPress(pPort) : VERR_INVALID_POINTER; + if (pPort) + vrc = pPort->pfnSleepButtonPress(pPort); + else + vrc = VERR_PDM_MISSING_INTERFACE; } HRESULT rc = RT_SUCCESS(vrc) ? S_OK : @@ -2453,7 +2552,8 @@ STDMETHODIMP Console::SaveState(IProgress **aProgress) if (mMachineState == MachineState_Running) { HRESULT rc = Pause(); - if (FAILED(rc)) return rc; + if (FAILED(rc)) + return rc; } HRESULT rc = S_OK; @@ -2472,7 +2572,8 @@ STDMETHODIMP Console::SaveState(IProgress **aProgress) */ rc = mControl->BeginSavingState(pProgress.asOutParam(), stateFilePath.asOutParam()); - if (FAILED(rc)) break; + if (FAILED(rc)) + break; fBeganSavingState = true; @@ -2513,7 +2614,7 @@ STDMETHODIMP Console::SaveState(IProgress **aProgress) } /* create a thread to wait until the VM state is saved */ - int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *) task.get(), + int vrc = RTThreadCreate(NULL, Console::saveStateThread, (void *)task.get(), 0, RTTHREADTYPE_MAIN_WORKER, 0, "VMSave"); if (RT_FAILURE(vrc)) { @@ -2526,8 +2627,7 @@ STDMETHODIMP Console::SaveState(IProgress **aProgress) /* return the progress to the caller */ pProgress.queryInterfaceTo(aProgress); - } - while (0); + } while (0); if (FAILED(rc) && !fTaskCreationFailed) { @@ -2699,13 +2799,14 @@ STDMETHODIMP Console::AttachUSBDevice(IN_BSTR aId) tr("Cannot attach a USB device to the machine which is not running or paused (machine state: %s)"), Global::stringifyMachineState(mMachineState)); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* Get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); /* Don't proceed unless we've found the usb controller. */ PPDMIBASE pBase = NULL; - int vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase); + int vrc = PDMR3QueryLun(ptrVM, "usb-ohci", 0, 0, &pBase); if (RT_FAILURE(vrc)) return setError(VBOX_E_PDM_ERROR, tr("The virtual machine does not have a USB controller")); @@ -2715,10 +2816,7 @@ STDMETHODIMP Console::AttachUSBDevice(IN_BSTR aId) alock.leave(); /* Request the device capture */ - HRESULT rc = mControl->CaptureUSBDevice(aId); - if (FAILED(rc)) return rc; - - return rc; + return mControl->CaptureUSBDevice(aId); #else /* !VBOX_WITH_USB */ return setError(VBOX_E_PDM_ERROR, @@ -2903,29 +3001,27 @@ Console::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BO true /* fFailOnError */); if (FAILED(rc)) return rc; - /* protect mpVM (if not NULL) */ - AutoVMCallerQuietWeak autoVMCaller(this); - - if ( mpVM - && autoVMCaller.isOk() + /* If the VM is online and supports shared folders, share this folder + * under the specified name. (Ignore any failure to obtain the VM handle.) */ + SafeVMPtrQuiet ptrVM(this); + if ( ptrVM.isOk() && m_pVMMDev && m_pVMMDev->isShFlActive() ) { - /* If the VM is online and supports shared folders, share this folder - * under the specified name. */ - /* first, remove the machine or the global folder if there is any */ SharedFolderDataMap::const_iterator it; if (findOtherSharedFolder(aName, it)) { rc = removeSharedFolder(aName); - if (FAILED(rc)) return rc; + if (FAILED(rc)) + return rc; } /* second, create the given folder */ rc = createSharedFolder(aName, SharedFolderData(aHostPath, aWritable, aAutoMount)); - if (FAILED(rc)) return rc; + if (FAILED(rc)) + return rc; } m_mapSharedFolders.insert(std::make_pair(aName, pSharedFolder)); @@ -2969,11 +3065,9 @@ STDMETHODIMP Console::RemoveSharedFolder(IN_BSTR aName) HRESULT rc = findSharedFolder(aName, pSharedFolder, true /* aSetError */); if (FAILED(rc)) return rc; - /* protect mpVM (if not NULL) */ - AutoVMCallerQuietWeak autoVMCaller(this); - - if ( mpVM - && autoVMCaller.isOk() + /* protect the VM handle (if not NULL) */ + SafeVMPtrQuiet ptrVM(this); + if ( ptrVM.isOk() && m_pVMMDev && m_pVMMDev->isShFlActive() ) @@ -3061,16 +3155,13 @@ STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName, } // b) one extra sub-operations for online snapshots OR offline snapshots that have a saved state (needs to be copied) - bool fTakingSnapshotOnline = ((mMachineState == MachineState_Running) || (mMachineState == MachineState_Paused)); + bool const fTakingSnapshotOnline = Global::IsOnline(mMachineState); LogFlowFunc(("fTakingSnapshotOnline = %d, mMachineState = %d\n", fTakingSnapshotOnline, mMachineState)); - if ( fTakingSnapshotOnline - || mMachineState == MachineState_Saved - ) + if (fTakingSnapshotOnline) { ++cOperations; - ulTotalOperationsWeight += ulMemSize; } @@ -3115,7 +3206,7 @@ STDMETHODIMP Console::TakeSnapshot(IN_BSTR aName, int vrc = RTThreadCreate(NULL, Console::fntTakeSnapshotWorker, - (void*)pTask, + (void *)pTask, 0, RTTHREADTYPE_MAIN_WORKER, 0, @@ -3154,9 +3245,55 @@ STDMETHODIMP Console::DeleteSnapshot(IN_BSTR aId, IProgress **aProgress) tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"), Global::stringifyMachineState(mMachineState)); + MachineState_T machineState = MachineState_Null; + HRESULT rc = mControl->DeleteSnapshot(this, aId, aId, FALSE /* fDeleteAllChildren */, &machineState, aProgress); + if (FAILED(rc)) return rc; + + setMachineStateLocally(machineState); + return S_OK; +} + +STDMETHODIMP Console::DeleteSnapshotAndAllChildren(IN_BSTR aId, IProgress **aProgress) +{ + CheckComArgExpr(aId, Guid(aId).isEmpty() == false); + CheckComArgOutPointerValid(aProgress); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (Global::IsTransient(mMachineState)) + return setError(VBOX_E_INVALID_VM_STATE, + tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"), + Global::stringifyMachineState(mMachineState)); + + MachineState_T machineState = MachineState_Null; + HRESULT rc = mControl->DeleteSnapshot(this, aId, aId, TRUE /* fDeleteAllChildren */, &machineState, aProgress); + if (FAILED(rc)) return rc; + + setMachineStateLocally(machineState); + return S_OK; +} + +STDMETHODIMP Console::DeleteSnapshotRange(IN_BSTR aStartId, IN_BSTR aEndId, IProgress **aProgress) +{ + CheckComArgExpr(aStartId, Guid(aStartId).isEmpty() == false); + CheckComArgExpr(aEndId, Guid(aEndId).isEmpty() == false); + CheckComArgOutPointerValid(aProgress); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (Global::IsTransient(mMachineState)) + return setError(VBOX_E_INVALID_VM_STATE, + tr("Cannot delete a snapshot of the machine while it is changing the state (machine state: %s)"), + Global::stringifyMachineState(mMachineState)); MachineState_T machineState = MachineState_Null; - HRESULT rc = mControl->DeleteSnapshot(this, aId, &machineState, aProgress); + HRESULT rc = mControl->DeleteSnapshot(this, aStartId, aEndId, FALSE /* fDeleteAllChildren */, &machineState, aProgress); if (FAILED(rc)) return rc; setMachineStateLocally(machineState); @@ -3296,10 +3433,11 @@ HRESULT Console::convertBusPortDeviceToLun(StorageBus_T enmBus, LONG port, LONG * * @param aMediumAttachment The medium attachment with the new medium state. * @param fForce Force medium chance, if it is locked or not. + * @param pVM Safe VM handle. * * @note Locks this object for writing. */ -HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce) +HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForce, PVM pVM) { AutoCaller autoCaller(this); AssertComRCReturnRC(autoCaller.rc()); @@ -3357,24 +3495,21 @@ HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForc rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache); AssertComRC(rc); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - AssertComRCReturnRC(autoVMCaller.rc()); - /* * Call worker in EMT, that's faster and safer than doing everything * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait * here to make requests from under the lock in order to serialize them. */ PVMREQ pReq; - int vrc = VMR3ReqCall(mpVM, + int vrc = VMR3ReqCall(pVM, VMCPUID_ANY, -&pReq, + &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS, (PFNRT)Console::changeRemovableMedium, - 7, + 8, this, + pVM, pszDevice, uInstance, enmBus, @@ -3400,7 +3535,7 @@ HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForc return S_OK; } - if (!pMedium) + if (pMedium) return setError(E_FAIL, tr("Could not mount the media/drive '%ls' (%Rrc)"), mediumLocation.raw(), vrc); @@ -3416,6 +3551,7 @@ HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForc * @returns VBox status code. * * @param pThis Pointer to the Console object. + * @param pVM The VM handle. * @param pcszDevice The PDM device name. * @param uInstance The PDM device instance. * @param uLun The PDM LUN number of the drive. @@ -3430,6 +3566,7 @@ HRESULT Console::doMediumChange(IMediumAttachment *aMediumAttachment, bool fForc * @thread EMT */ DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole, + PVM pVM, const char *pcszDevice, unsigned uInstance, StorageBus_T enmBus, @@ -3445,8 +3582,6 @@ DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole, AutoCaller autoCaller(pConsole); AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED); - PVM pVM = pConsole->mpVM; - /* * Suspend the VM first. * @@ -3511,6 +3646,7 @@ DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole, NULL /* phrc */, true /* fAttachDetach */, fForce /* fForceUnmount */, + false /* fHotplug */, pVM, NULL /* paLedDevType */); /** @todo this dumps everything attached to this device instance, which @@ -3548,6 +3684,504 @@ DECLCALLBACK(int) Console::changeRemovableMedium(Console *pConsole, /** + * Attach a new storage device to the VM. + * + * @param aMediumAttachment The medium attachment which is added. + * @param pVM Safe VM handle. + * + * @note Locks this object for writing. + */ +HRESULT Console::doStorageDeviceAttach(IMediumAttachment *aMediumAttachment, PVM pVM) +{ + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + /* We will need to release the write lock before calling EMT */ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + HRESULT rc = S_OK; + const char *pszDevice = NULL; + + SafeIfaceArray<IStorageController> ctrls; + rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls)); + AssertComRC(rc); + IMedium *pMedium; + rc = aMediumAttachment->COMGETTER(Medium)(&pMedium); + AssertComRC(rc); + Bstr mediumLocation; + if (pMedium) + { + rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam()); + AssertComRC(rc); + } + + Bstr attCtrlName; + rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam()); + AssertComRC(rc); + ComPtr<IStorageController> pStorageController; + for (size_t i = 0; i < ctrls.size(); ++i) + { + Bstr ctrlName; + rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam()); + AssertComRC(rc); + if (attCtrlName == ctrlName) + { + pStorageController = ctrls[i]; + break; + } + } + if (pStorageController.isNull()) + return setError(E_FAIL, + tr("Could not find storage controller '%ls'"), attCtrlName.raw()); + + StorageControllerType_T enmCtrlType; + rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType); + AssertComRC(rc); + pszDevice = convertControllerTypeToDev(enmCtrlType); + + StorageBus_T enmBus; + rc = pStorageController->COMGETTER(Bus)(&enmBus); + AssertComRC(rc); + ULONG uInstance; + rc = pStorageController->COMGETTER(Instance)(&uInstance); + AssertComRC(rc); + BOOL fUseHostIOCache; + rc = pStorageController->COMGETTER(UseHostIOCache)(&fUseHostIOCache); + AssertComRC(rc); + + /* + * Call worker in EMT, that's faster and safer than doing everything + * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait + * here to make requests from under the lock in order to serialize them. + */ + PVMREQ pReq; + int vrc = VMR3ReqCall(pVM, + VMCPUID_ANY, + &pReq, + 0 /* no wait! */, + VMREQFLAGS_VBOX_STATUS, + (PFNRT)Console::attachStorageDevice, + 7, + this, + pVM, + pszDevice, + uInstance, + enmBus, + fUseHostIOCache, + aMediumAttachment); + + /* leave the lock before waiting for a result (EMT will call us back!) */ + alock.leave(); + + if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc)) + { + vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT); + AssertRC(vrc); + if (RT_SUCCESS(vrc)) + vrc = pReq->iStatus; + } + VMR3ReqFree(pReq); + + if (RT_SUCCESS(vrc)) + { + LogFlowThisFunc(("Returns S_OK\n")); + return S_OK; + } + + if (!pMedium) + return setError(E_FAIL, + tr("Could not mount the media/drive '%ls' (%Rrc)"), + mediumLocation.raw(), vrc); + + return setError(E_FAIL, + tr("Could not unmount the currently mounted media/drive (%Rrc)"), + vrc); +} + + +/** + * Performs the storage attach operation in EMT. + * + * @returns VBox status code. + * + * @param pThis Pointer to the Console object. + * @param pVM The VM handle. + * @param pcszDevice The PDM device name. + * @param uInstance The PDM device instance. + * + * @thread EMT + */ +DECLCALLBACK(int) Console::attachStorageDevice(Console *pConsole, + PVM pVM, + const char *pcszDevice, + unsigned uInstance, + StorageBus_T enmBus, + bool fUseHostIOCache, + IMediumAttachment *aMediumAtt) +{ + LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, aMediumAtt=%p\n", + pConsole, uInstance, pcszDevice, pcszDevice, enmBus, aMediumAtt)); + + AssertReturn(pConsole, VERR_INVALID_PARAMETER); + + AutoCaller autoCaller(pConsole); + AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED); + + /* + * Suspend the VM first. + * + * The VM must not be running since it might have pending I/O to + * the drive which is being changed. + */ + bool fResume; + VMSTATE enmVMState = VMR3GetState(pVM); + switch (enmVMState) + { + case VMSTATE_RESETTING: + case VMSTATE_RUNNING: + { + LogFlowFunc(("Suspending the VM...\n")); + /* disable the callback to prevent Console-level state change */ + pConsole->mVMStateChangeCallbackDisabled = true; + int rc = VMR3Suspend(pVM); + pConsole->mVMStateChangeCallbackDisabled = false; + AssertRCReturn(rc, rc); + fResume = true; + break; + } + + case VMSTATE_SUSPENDED: + case VMSTATE_CREATED: + case VMSTATE_OFF: + fResume = false; + break; + + case VMSTATE_RUNNING_LS: + case VMSTATE_RUNNING_FT: + return setErrorInternal(VBOX_E_INVALID_VM_STATE, + COM_IIDOF(IConsole), + getStaticComponentName(), + (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")), + false /*aWarning*/, + true /*aLogIt*/); + + default: + AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED); + } + + /* Determine the base path for the device instance. */ + PCFGMNODE pCtlInst; + pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance); + AssertReturn(pCtlInst, VERR_INTERNAL_ERROR); + + int rc = VINF_SUCCESS; + int rcRet = VINF_SUCCESS; + + rcRet = pConsole->configMediumAttachment(pCtlInst, + pcszDevice, + uInstance, + enmBus, + fUseHostIOCache, + false /* fSetupMerge */, + false /* fBuiltinIoCache */, + 0 /* uMergeSource */, + 0 /* uMergeTarget */, + aMediumAtt, + pConsole->mMachineState, + NULL /* phrc */, + true /* fAttachDetach */, + false /* fForceUnmount */, + true /* fHotplug */, + pVM, + NULL /* paLedDevType */); + /** @todo this dumps everything attached to this device instance, which + * is more than necessary. Dumping the changed LUN would be enough. */ + CFGMR3Dump(pCtlInst); + + /* + * Resume the VM if necessary. + */ + if (fResume) + { + LogFlowFunc(("Resuming the VM...\n")); + /* disable the callback to prevent Console-level state change */ + pConsole->mVMStateChangeCallbackDisabled = true; + rc = VMR3Resume(pVM); + pConsole->mVMStateChangeCallbackDisabled = false; + AssertRC(rc); + if (RT_FAILURE(rc)) + { + /* too bad, we failed. try to sync the console state with the VMM state */ + vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole); + } + /** @todo: if we failed with drive mount, then the VMR3Resume + * error (if any) will be hidden from the caller. For proper reporting + * of such multiple errors to the caller we need to enhance the + * IVirtualBoxError interface. For now, give the first error the higher + * priority. + */ + if (RT_SUCCESS(rcRet)) + rcRet = rc; + } + + LogFlowFunc(("Returning %Rrc\n", rcRet)); + return rcRet; +} + +/** + * Attach a new storage device to the VM. + * + * @param aMediumAttachment The medium attachment which is added. + * @param pVM Safe VM handle. + * + * @note Locks this object for writing. + */ +HRESULT Console::doStorageDeviceDetach(IMediumAttachment *aMediumAttachment, PVM pVM) +{ + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + /* We will need to release the write lock before calling EMT */ + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + HRESULT rc = S_OK; + const char *pszDevice = NULL; + + SafeIfaceArray<IStorageController> ctrls; + rc = mMachine->COMGETTER(StorageControllers)(ComSafeArrayAsOutParam(ctrls)); + AssertComRC(rc); + IMedium *pMedium; + rc = aMediumAttachment->COMGETTER(Medium)(&pMedium); + AssertComRC(rc); + Bstr mediumLocation; + if (pMedium) + { + rc = pMedium->COMGETTER(Location)(mediumLocation.asOutParam()); + AssertComRC(rc); + } + + Bstr attCtrlName; + rc = aMediumAttachment->COMGETTER(Controller)(attCtrlName.asOutParam()); + AssertComRC(rc); + ComPtr<IStorageController> pStorageController; + for (size_t i = 0; i < ctrls.size(); ++i) + { + Bstr ctrlName; + rc = ctrls[i]->COMGETTER(Name)(ctrlName.asOutParam()); + AssertComRC(rc); + if (attCtrlName == ctrlName) + { + pStorageController = ctrls[i]; + break; + } + } + if (pStorageController.isNull()) + return setError(E_FAIL, + tr("Could not find storage controller '%ls'"), attCtrlName.raw()); + + StorageControllerType_T enmCtrlType; + rc = pStorageController->COMGETTER(ControllerType)(&enmCtrlType); + AssertComRC(rc); + pszDevice = convertControllerTypeToDev(enmCtrlType); + + StorageBus_T enmBus; + rc = pStorageController->COMGETTER(Bus)(&enmBus); + AssertComRC(rc); + ULONG uInstance; + rc = pStorageController->COMGETTER(Instance)(&uInstance); + AssertComRC(rc); + + /* + * Call worker in EMT, that's faster and safer than doing everything + * using VMR3ReqCall. Note that we separate VMR3ReqCall from VMR3ReqWait + * here to make requests from under the lock in order to serialize them. + */ + PVMREQ pReq; + int vrc = VMR3ReqCall(pVM, + VMCPUID_ANY, + &pReq, + 0 /* no wait! */, + VMREQFLAGS_VBOX_STATUS, + (PFNRT)Console::detachStorageDevice, + 6, + this, + pVM, + pszDevice, + uInstance, + enmBus, + aMediumAttachment); + + /* leave the lock before waiting for a result (EMT will call us back!) */ + alock.leave(); + + if (vrc == VERR_TIMEOUT || RT_SUCCESS(vrc)) + { + vrc = VMR3ReqWait(pReq, RT_INDEFINITE_WAIT); + AssertRC(vrc); + if (RT_SUCCESS(vrc)) + vrc = pReq->iStatus; + } + VMR3ReqFree(pReq); + + if (RT_SUCCESS(vrc)) + { + LogFlowThisFunc(("Returns S_OK\n")); + return S_OK; + } + + if (!pMedium) + return setError(E_FAIL, + tr("Could not mount the media/drive '%ls' (%Rrc)"), + mediumLocation.raw(), vrc); + + return setError(E_FAIL, + tr("Could not unmount the currently mounted media/drive (%Rrc)"), + vrc); +} + +/** + * Performs the storage detach operation in EMT. + * + * @returns VBox status code. + * + * @param pThis Pointer to the Console object. + * @param pVM The VM handle. + * @param pcszDevice The PDM device name. + * @param uInstance The PDM device instance. + * + * @thread EMT + */ +DECLCALLBACK(int) Console::detachStorageDevice(Console *pConsole, + PVM pVM, + const char *pcszDevice, + unsigned uInstance, + StorageBus_T enmBus, + IMediumAttachment *pMediumAtt) +{ + LogFlowFunc(("pConsole=%p uInstance=%u pszDevice=%p:{%s} enmBus=%u, pMediumAtt=%p\n", + pConsole, uInstance, pcszDevice, pcszDevice, enmBus, pMediumAtt)); + + AssertReturn(pConsole, VERR_INVALID_PARAMETER); + + AutoCaller autoCaller(pConsole); + AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED); + + /* + * Suspend the VM first. + * + * The VM must not be running since it might have pending I/O to + * the drive which is being changed. + */ + bool fResume; + VMSTATE enmVMState = VMR3GetState(pVM); + switch (enmVMState) + { + case VMSTATE_RESETTING: + case VMSTATE_RUNNING: + { + LogFlowFunc(("Suspending the VM...\n")); + /* disable the callback to prevent Console-level state change */ + pConsole->mVMStateChangeCallbackDisabled = true; + int rc = VMR3Suspend(pVM); + pConsole->mVMStateChangeCallbackDisabled = false; + AssertRCReturn(rc, rc); + fResume = true; + break; + } + + case VMSTATE_SUSPENDED: + case VMSTATE_CREATED: + case VMSTATE_OFF: + fResume = false; + break; + + case VMSTATE_RUNNING_LS: + case VMSTATE_RUNNING_FT: + return setErrorInternal(VBOX_E_INVALID_VM_STATE, + COM_IIDOF(IConsole), + getStaticComponentName(), + (enmVMState == VMSTATE_RUNNING_LS) ? Utf8Str(tr("Cannot change drive during live migration")) : Utf8Str(tr("Cannot change drive during fault tolerant syncing")), + false /*aWarning*/, + true /*aLogIt*/); + + default: + AssertMsgFailedReturn(("enmVMState=%d\n", enmVMState), VERR_ACCESS_DENIED); + } + + /* Determine the base path for the device instance. */ + PCFGMNODE pCtlInst; + pCtlInst = CFGMR3GetChildF(CFGMR3GetRoot(pVM), "Devices/%s/%u/", pcszDevice, uInstance); + AssertReturn(pCtlInst, VERR_INTERNAL_ERROR); + +#define H() AssertMsgReturn(!FAILED(hrc), ("hrc=%Rhrc\n", hrc), VERR_GENERAL_FAILURE) + + HRESULT hrc; + int rc = VINF_SUCCESS; + int rcRet = VINF_SUCCESS; + unsigned uLUN; + LONG lDev; + LONG lPort; + DeviceType_T lType; + PCFGMNODE pLunL0 = NULL; + PCFGMNODE pCfg = NULL; + + hrc = pMediumAtt->COMGETTER(Device)(&lDev); H(); + hrc = pMediumAtt->COMGETTER(Port)(&lPort); H(); + hrc = pMediumAtt->COMGETTER(Type)(&lType); H(); + hrc = Console::convertBusPortDeviceToLun(enmBus, lPort, lDev, uLUN); H(); + +#undef H + + /* First check if the LUN really exists. */ + pLunL0 = CFGMR3GetChildF(pCtlInst, "LUN#%u", uLUN); + if (pLunL0) + { + rc = PDMR3DeviceDetach(pVM, pcszDevice, uInstance, uLUN, 0); + if (rc == VERR_PDM_NO_DRIVER_ATTACHED_TO_LUN) + rc = VINF_SUCCESS; + AssertRCReturn(rc, rc); + CFGMR3RemoveNode(pLunL0); + + Utf8Str devicePath = Utf8StrFmt("%s/%u/LUN#%u", pcszDevice, uInstance, uLUN); + pConsole->mapMediumAttachments.erase(devicePath); + + } + else + AssertFailedReturn(VERR_INTERNAL_ERROR); + + CFGMR3Dump(pCtlInst); + + /* + * Resume the VM if necessary. + */ + if (fResume) + { + LogFlowFunc(("Resuming the VM...\n")); + /* disable the callback to prevent Console-level state change */ + pConsole->mVMStateChangeCallbackDisabled = true; + rc = VMR3Resume(pVM); + pConsole->mVMStateChangeCallbackDisabled = false; + AssertRC(rc); + if (RT_FAILURE(rc)) + { + /* too bad, we failed. try to sync the console state with the VMM state */ + vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, pConsole); + } + /** @todo: if we failed with drive mount, then the VMR3Resume + * error (if any) will be hidden from the caller. For proper reporting + * of such multiple errors to the caller we need to enhance the + * IVirtualBoxError interface. For now, give the first error the higher + * priority. + */ + if (RT_SUCCESS(rcRet)) + rcRet = rc; + } + + LogFlowFunc(("Returning %Rrc\n", rcRet)); + return rcRet; +} + +/** * Called by IInternalSessionControl::OnNetworkAdapterChange(). * * @note Locks this object for writing. @@ -3564,12 +4198,9 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c HRESULT rc = S_OK; /* don't trigger network change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - /* Get the properties we need from the adapter */ BOOL fCableConnected, fTraceEnabled; rc = aNetworkAdapter->COMGETTER(CableConnected)(&fCableConnected); @@ -3595,7 +4226,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c AssertComRC(rc); const char *pszAdapterName = networkAdapterTypeToName(adapterType); PPDMIBASE pBase; - int vrc = PDMR3QueryDeviceLun(mpVM, pszAdapterName, ulInstance, 0, &pBase); + int vrc = PDMR3QueryDeviceLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase); if (RT_SUCCESS(vrc)) { Assert(pBase); @@ -3612,7 +4243,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c } if (RT_SUCCESS(vrc) && changeAdapter) { - VMSTATE enmVMState = VMR3GetState(mpVM); + VMSTATE enmVMState = VMR3GetState(ptrVM); if ( enmVMState == VMSTATE_RUNNING /** @todo LiveMigration: Forbid or deal correctly with the _LS variants */ || enmVMState == VMSTATE_SUSPENDED) { @@ -3622,7 +4253,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c ComAssertRC(vrc); } - rc = doNetworkAdapterChange(pszAdapterName, ulInstance, 0, aNetworkAdapter); + rc = doNetworkAdapterChange(ptrVM, pszAdapterName, ulInstance, 0, aNetworkAdapter); if (fTraceEnabled && fCableConnected && pINetCfg) { @@ -3633,10 +4264,8 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c } } else if (vrc == VERR_PDM_DEVICE_INSTANCE_NOT_FOUND) - { return setError(E_FAIL, tr("The network adapter #%u is not enabled"), ulInstance); - } else ComAssertRC(vrc); @@ -3644,6 +4273,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c rc = E_FAIL; } } + ptrVM.release(); } /* notify console callbacks on success */ @@ -3660,7 +4290,7 @@ HRESULT Console::onNetworkAdapterChange(INetworkAdapter *aNetworkAdapter, BOOL c * @note Locks this object for writing. */ HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove, - NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort) + NATProtocol_T aProto, IN_BSTR aHostIp, LONG aHostPort, IN_BSTR aGuestIp, LONG aGuestPort) { LogFlowThisFunc(("\n")); @@ -3670,71 +4300,75 @@ HRESULT Console::onNATRedirectRuleChange(ULONG ulInstance, BOOL aNatRuleRemove, AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); HRESULT rc = S_OK; - int vrc = VINF_SUCCESS; - PPDMINETWORKNATCONFIG pNetNatCfg = NULL; + /* don't trigger nat engine change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - - ComPtr<INetworkAdapter> pNetworkAdapter; - rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam()); - if ( FAILED(rc) - || pNetworkAdapter.isNull()) - goto done; - - /* - * Find the adapter instance, get the config interface and update - * the link state. - */ - NetworkAdapterType_T adapterType; - rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType); - AssertComRC(rc); - if (FAILED(rc)) + do { - rc = E_FAIL; - goto done; - } + ComPtr<INetworkAdapter> pNetworkAdapter; + rc = machine()->GetNetworkAdapter(ulInstance, pNetworkAdapter.asOutParam()); + if ( FAILED(rc) + || pNetworkAdapter.isNull()) + break; - const char *pszAdapterName = networkAdapterTypeToName(adapterType); - PPDMIBASE pBase; - vrc = PDMR3QueryLun(mpVM, pszAdapterName, ulInstance, 0, &pBase); - ComAssertRC(vrc); - if (RT_FAILURE(vrc)) - { - rc = E_FAIL; - goto done; - } - NetworkAttachmentType_T attachmentType; - vrc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType); + /* + * Find the adapter instance, get the config interface and update + * the link state. + */ + NetworkAdapterType_T adapterType; + rc = pNetworkAdapter->COMGETTER(AdapterType)(&adapterType); + if (FAILED(rc)) + { + AssertComRC(rc); + rc = E_FAIL; + break; + } - if ( RT_FAILURE(vrc) - || attachmentType != NetworkAttachmentType_NAT) - { - rc = (RT_FAILURE(vrc)) ? E_FAIL: rc; - goto done; - } + const char *pszAdapterName = networkAdapterTypeToName(adapterType); + PPDMIBASE pBase; + int vrc = PDMR3QueryLun(ptrVM, pszAdapterName, ulInstance, 0, &pBase); + if (RT_FAILURE(vrc)) + { + ComAssertRC(vrc); + rc = E_FAIL; + break; + } - /* look down for PDMINETWORKNATCONFIG interface */ - while (pBase) - { - if ((pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID))) + NetworkAttachmentType_T attachmentType; + rc = pNetworkAdapter->COMGETTER(AttachmentType)(&attachmentType); + if ( FAILED(rc) + || attachmentType != NetworkAttachmentType_NAT) + { + rc = E_FAIL; break; - PPDMDRVINS drvins = PDMIBASE_2_PDMDRV(pBase); - pBase = drvins->pDownBase; - } - if (!pNetNatCfg) - goto done; - bool fUdp = (aProto == NATProtocol_UDP); - vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, aNatRuleRemove, fUdp, - Utf8Str(aHostIp).c_str(), aHostPort, Utf8Str(aGuestIp).c_str(), - aGuestPort); - if (RT_FAILURE(vrc)) - rc = E_FAIL; + } + + /* look down for PDMINETWORKNATCONFIG interface */ + PPDMINETWORKNATCONFIG pNetNatCfg = NULL; + while (pBase) + { + pNetNatCfg = (PPDMINETWORKNATCONFIG)pBase->pfnQueryInterface(pBase, PDMINETWORKNATCONFIG_IID); + if (pNetNatCfg) + break; + /** @todo r=bird: This stinks! */ + PPDMDRVINS pDrvIns = PDMIBASE_2_PDMDRV(pBase); + pBase = pDrvIns->pDownBase; + } + if (!pNetNatCfg) + break; + + bool fUdp = aProto == NATProtocol_UDP; + vrc = pNetNatCfg->pfnRedirectRuleCommand(pNetNatCfg, aNatRuleRemove, fUdp, + Utf8Str(aHostIp).c_str(), aHostPort, Utf8Str(aGuestIp).c_str(), + aGuestPort); + if (RT_FAILURE(vrc)) + rc = E_FAIL; + } while (0); /* break loop */ + ptrVM.release(); } -done: + LogFlowThisFunc(("Leaving rc=%#x\n", rc)); return rc; } @@ -3745,6 +4379,7 @@ done: * * @returns COM status code. * + * @parma pVM The VM handle (caller hold this safely). * @param pszDevice The PDM device name. * @param uInstance The PDM device instance. * @param uLun The PDM LUN number of the drive. @@ -3752,7 +4387,8 @@ done: * * @note Locks this object for writing. */ -HRESULT Console::doNetworkAdapterChange(const char *pszDevice, +HRESULT Console::doNetworkAdapterChange(PVM pVM, + const char *pszDevice, unsigned uInstance, unsigned uLun, INetworkAdapter *aNetworkAdapter) @@ -3766,9 +4402,10 @@ HRESULT Console::doNetworkAdapterChange(const char *pszDevice, /* We will need to release the write lock before calling EMT */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* Get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); /* * Call worker in EMT, that's faster and safer than doing everything @@ -3776,9 +4413,9 @@ HRESULT Console::doNetworkAdapterChange(const char *pszDevice, * here to make requests from under the lock in order to serialize them. */ PVMREQ pReq; - int vrc = VMR3ReqCall(mpVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS, - (PFNRT) Console::changeNetworkAttachment, 5, - this, pszDevice, uInstance, uLun, aNetworkAdapter); + int vrc = VMR3ReqCall(pVM, 0 /*idDstCpu*/, &pReq, 0 /* no wait! */, VMREQFLAGS_VBOX_STATUS, + (PFNRT) Console::changeNetworkAttachment, 6, + this, ptrVM.raw(), pszDevice, uInstance, uLun, aNetworkAdapter); /* leave the lock before waiting for a result (EMT will call us back!) */ alock.leave(); @@ -3810,6 +4447,7 @@ HRESULT Console::doNetworkAdapterChange(const char *pszDevice, * @returns VBox status code. * * @param pThis Pointer to the Console object. + * @param pVM The VM handle. * @param pszDevice The PDM device name. * @param uInstance The PDM device instance. * @param uLun The PDM LUN number of the drive. @@ -3819,6 +4457,7 @@ HRESULT Console::doNetworkAdapterChange(const char *pszDevice, * @note Locks the Console object for writing. */ DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis, + PVM pVM, const char *pszDevice, unsigned uInstance, unsigned uLun, @@ -3832,20 +4471,14 @@ DECLCALLBACK(int) Console::changeNetworkAttachment(Console *pThis, AssertMsg( ( !strcmp(pszDevice, "pcnet") || !strcmp(pszDevice, "e1000") || !strcmp(pszDevice, "virtio-net")) - && (uLun == 0) - && (uInstance < SchemaDefs::NetworkAdapterCount), + && uLun == 0 + && uInstance < SchemaDefs::NetworkAdapterCount, ("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance)); Log(("pszDevice=%s uLun=%d uInstance=%d\n", pszDevice, uLun, uInstance)); AutoCaller autoCaller(pThis); AssertComRCReturn(autoCaller.rc(), VERR_ACCESS_DENIED); - /* protect mpVM */ - AutoVMCaller autoVMCaller(pThis); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - - PVM pVM = pThis->mpVM; - /* * Suspend the VM first. * @@ -3937,13 +4570,11 @@ HRESULT Console::onSerialPortChange(ISerialPort *aSerialPort) HRESULT rc = S_OK; /* don't trigger serial port change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - /* nothing to do so far */ + ptrVM.release(); } /* notify console callbacks on success */ @@ -3971,13 +4602,11 @@ HRESULT Console::onParallelPortChange(IParallelPort *aParallelPort) HRESULT rc = S_OK; /* don't trigger parallel port change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - /* nothing to do so far */ + ptrVM.release(); } /* notify console callbacks on success */ @@ -4005,13 +4634,11 @@ HRESULT Console::onStorageControllerChange() HRESULT rc = S_OK; /* don't trigger storage controller change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - /* nothing to do so far */ + ptrVM.release(); } /* notify console callbacks on success */ @@ -4039,13 +4666,11 @@ HRESULT Console::onMediumChange(IMediumAttachment *aMediumAttachment, BOOL aForc HRESULT rc = S_OK; /* don't trigger medium change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - - rc = doMediumChange(aMediumAttachment, !!aForce); + rc = doMediumChange(aMediumAttachment, !!aForce, ptrVM); + ptrVM.release(); } /* notify console callbacks on success */ @@ -4071,12 +4696,14 @@ HRESULT Console::onCPUChange(ULONG aCPU, BOOL aRemove) HRESULT rc = S_OK; /* don't trigger CPU change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { if (aRemove) - rc = doCPURemove(aCPU); + rc = doCPURemove(aCPU, ptrVM); else - rc = doCPUAdd(aCPU); + rc = doCPUAdd(aCPU, ptrVM); + ptrVM.release(); } /* notify console callbacks on success */ @@ -4104,22 +4731,20 @@ HRESULT Console::onCPUExecutionCapChange(ULONG aExecutionCap) HRESULT rc = S_OK; /* don't trigger the CPU priority change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - if ( mMachineState == MachineState_Running || mMachineState == MachineState_Teleporting || mMachineState == MachineState_LiveSnapshotting ) { /* No need to call in the EMT thread. */ - rc = VMR3SetCpuExecutionCap(mpVM, aExecutionCap); + rc = VMR3SetCpuExecutionCap(ptrVM, aExecutionCap); } else rc = setInvalidMachineStateError(); + ptrVM.release(); } /* notify console callbacks on success */ @@ -4220,7 +4845,8 @@ HRESULT Console::onUSBControllerChange() HRESULT rc = S_OK; /* don't trigger USB controller change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { /// @todo implement one day. // Anyway, if we want to query the machine's USB Controller we need @@ -4229,11 +4855,8 @@ HRESULT Console::onUSBControllerChange() // bird: While the VM supports hot-plugging, I doubt any guest can // handle it at this time... :-) - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - /* nothing to do so far */ + ptrVM.release(); } /* notify console callbacks on success */ @@ -4293,16 +4916,16 @@ HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aE AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - /* protect mpVM (we don't need error info, since it's a callback) */ - AutoVMCallerQuiet autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) + /* Get the VM pointer (we don't need error info, since it's a callback). */ + SafeVMPtrQuiet ptrVM(this); + if (!ptrVM.isOk()) { /* The VM may be no more operational when this message arrives * (e.g. it may be Saving or Stopping or just PoweredOff) -- * autoVMCaller.rc() will return a failure in this case. */ LogFlowThisFunc(("Attach request ignored (mMachineState=%d).\n", mMachineState)); - return autoVMCaller.rc(); + return ptrVM.rc(); } if (aError != NULL) @@ -4313,7 +4936,7 @@ HRESULT Console::onUSBDeviceAttach(IUSBDevice *aDevice, IVirtualBoxErrorInfo *aE } /* Don't proceed unless there's at least one USB hub. */ - if (!PDMR3USBHasHub(mpVM)) + if (!PDMR3USBHasHub(ptrVM)) { LogFlowThisFunc(("Attach request ignored (no USB controller).\n")); return E_FAIL; @@ -4442,12 +5065,9 @@ HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup) HRESULT rc = S_OK; /* don't trigger the CPU priority change if the VM isn't running */ - if (mpVM) + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) { - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); - if ( mMachineState == MachineState_Running || mMachineState == MachineState_Teleporting || mMachineState == MachineState_LiveSnapshotting @@ -4463,13 +5083,14 @@ HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup) if (SUCCEEDED(rc)) { int vrc; - vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(mpVM, Utf8Str(strName).c_str(), + vrc = PDMR3AsyncCompletionBwMgrSetMaxForFile(ptrVM, Utf8Str(strName).c_str(), cMax * _1M); AssertRC(vrc); } } else rc = setInvalidMachineStateError(); + ptrVM.release(); } /* notify console callbacks on success */ @@ -4481,6 +5102,41 @@ HRESULT Console::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup) } /** + * Called by IInternalSessionControl::OnStorageDeviceChange(). + * + * @note Locks this object for writing. + */ +HRESULT Console::onStorageDeviceChange(IMediumAttachment *aMediumAttachment, BOOL aRemove) +{ + LogFlowThisFunc(("\n")); + + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + HRESULT rc = S_OK; + + /* don't trigger medium change if the VM isn't running */ + SafeVMPtrQuiet ptrVM(this); + if (ptrVM.isOk()) + { + if (aRemove) + rc = doStorageDeviceDetach(aMediumAttachment, ptrVM); + else + rc = doStorageDeviceAttach(aMediumAttachment, ptrVM); + ptrVM.release(); + } + + /* notify console callbacks on success */ + if (SUCCEEDED(rc)) + fireStorageDeviceChangedEvent(mEventSource, aMediumAttachment, aRemove); + + LogFlowThisFunc(("Leaving rc=%#x\n", rc)); + return rc; +} + +/** * @note Temporarily locks this object for writing. */ HRESULT Console::getGuestProperty(IN_BSTR aName, BSTR *aValue, @@ -4688,7 +5344,7 @@ static int onlineMergeMediumProgress(void *pvUser, unsigned uPercentage) } /** - * @note Temporarily locks this object for writing. + * @note Temporarily locks this object for writing. bird: And/or reading? */ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, ULONG aSourceIdx, ULONG aTargetIdx, @@ -4703,7 +5359,11 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, HRESULT rc = S_OK; int vrc = VINF_SUCCESS; - PVM pVM = mpVM; + + /* Get the VM - must be done before the read-locking. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); /* We will need to release the lock before doing the actual merge */ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); @@ -4719,6 +5379,8 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, return setInvalidMachineStateError(); } + /** @todo AssertComRC -> AssertComRCReturn! Could potentially end up + * using uninitialized variables here. */ BOOL fBuiltinIoCache; rc = mMachine->COMGETTER(IoCacheEnabled)(&fBuiltinIoCache); AssertComRC(rc); @@ -4783,23 +5445,23 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, alock.release(); /* Pause the VM, as it might have pending IO on this drive */ - VMSTATE enmVMState = VMR3GetState(pVM); + VMSTATE enmVMState = VMR3GetState(ptrVM); if (mMachineState == MachineState_DeletingSnapshotOnline) { LogFlowFunc(("Suspending the VM...\n")); /* disable the callback to prevent Console-level state change */ mVMStateChangeCallbackDisabled = true; - int vrc2 = VMR3Suspend(pVM); + int vrc2 = VMR3Suspend(ptrVM); mVMStateChangeCallbackDisabled = false; AssertRCReturn(vrc2, E_FAIL); } - vrc = VMR3ReqCallWait(pVM, + vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY, (PFNRT)reconfigureMediumAttachment, 13, this, - pVM, + ptrVM.raw(), pcszDevice, uInstance, enmBus, @@ -4818,13 +5480,13 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, LogFlowFunc(("Resuming the VM...\n")); /* disable the callback to prevent Console-level state change */ mVMStateChangeCallbackDisabled = true; - int vrc2 = VMR3Resume(pVM); + int vrc2 = VMR3Resume(ptrVM); mVMStateChangeCallbackDisabled = false; if (RT_FAILURE(vrc2)) { /* too bad, we failed. try to sync the console state with the VMM state */ AssertLogRelRC(vrc2); - vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this); + vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this); } } @@ -4835,7 +5497,7 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, PPDMIBASE pIBase = NULL; PPDMIMEDIA pIMedium = NULL; - vrc = PDMR3QueryDriverOnLun(pVM, pcszDevice, uInstance, uLUN, "VD", &pIBase); + vrc = PDMR3QueryDriverOnLun(ptrVM, pcszDevice, uInstance, uLUN, "VD", &pIBase); if (RT_SUCCESS(vrc)) { if (pIBase) @@ -4854,13 +5516,13 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, return setError(E_FAIL, tr("Failed to perform an online medium merge (%Rrc)"), vrc); /* Pause the VM, as it might have pending IO on this drive */ - enmVMState = VMR3GetState(pVM); + enmVMState = VMR3GetState(ptrVM); if (mMachineState == MachineState_DeletingSnapshotOnline) { LogFlowFunc(("Suspending the VM...\n")); /* disable the callback to prevent Console-level state change */ mVMStateChangeCallbackDisabled = true; - int vrc2 = VMR3Suspend(pVM); + int vrc2 = VMR3Suspend(ptrVM); mVMStateChangeCallbackDisabled = false; AssertRCReturn(vrc2, E_FAIL); } @@ -4870,12 +5532,12 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, aMergeForward, aParentForTarget, ComSafeArrayInArg(aChildrenToReparent)); - vrc = VMR3ReqCallWait(pVM, + vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY, (PFNRT)reconfigureMediumAttachment, 13, this, - pVM, + ptrVM.raw(), pcszDevice, uInstance, enmBus, @@ -4894,13 +5556,13 @@ HRESULT Console::onlineMergeMedium(IMediumAttachment *aMediumAttachment, LogFlowFunc(("Resuming the VM...\n")); /* disable the callback to prevent Console-level state change */ mVMStateChangeCallbackDisabled = true; - int vrc2 = VMR3Resume(pVM); + int vrc2 = VMR3Resume(ptrVM); mVMStateChangeCallbackDisabled = false; AssertRC(vrc2); if (RT_FAILURE(vrc2)) { /* too bad, we failed. try to sync the console state with the VMM state */ - vmstateChangeCallback(pVM, VMSTATE_SUSPENDED, enmVMState, this); + vmstateChangeCallback(ptrVM, VMSTATE_SUSPENDED, enmVMState, this); } } @@ -5202,7 +5864,7 @@ HRESULT Console::addVMCaller(bool aQuiet /* = false */, tr("The virtual machine is being powered down")); } - if (mpVM == NULL) + if (mpUVM == NULL) { Assert(aAllowNullVM == true); @@ -5229,7 +5891,7 @@ void Console::releaseVMCaller() AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - AssertReturnVoid(mpVM != NULL); + AssertReturnVoid(mpUVM != NULL); Assert(mVMCallers > 0); --mVMCallers; @@ -5241,6 +5903,62 @@ void Console::releaseVMCaller() } } + +HRESULT Console::safeVMPtrRetainer(PVM *a_ppVM, PUVM *a_ppUVM, bool a_Quiet) +{ + *a_ppVM = NULL; + *a_ppUVM = NULL; + + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + /* + * Repeat the checks done by addVMCaller. + */ + if (mVMDestroying) /* powerDown() is waiting for all callers to finish */ + return a_Quiet + ? E_ACCESSDENIED + : setError(E_ACCESSDENIED, tr("The virtual machine is being powered down")); + PUVM pUVM = mpUVM; + if (!pUVM) + return a_Quiet + ? E_ACCESSDENIED + : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off")); + + /* + * Retain a reference to the user mode VM handle and get the global handle. + */ + uint32_t cRefs = VMR3RetainUVM(pUVM); + if (cRefs == UINT32_MAX) + return a_Quiet + ? E_ACCESSDENIED + : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off")); + + PVM pVM = VMR3GetVM(pUVM); + if (!pVM) + { + VMR3ReleaseUVM(pUVM); + return a_Quiet + ? E_ACCESSDENIED + : setError(E_ACCESSDENIED, tr("The virtual machine is was powered off")); + } + + /* done */ + *a_ppVM = pVM; + *a_ppUVM = pUVM; + return S_OK; +} + +void Console::safeVMPtrReleaser(PVM *a_ppVM, PUVM *a_ppUVM) +{ + if (*a_ppVM && *a_ppUVM) + VMR3ReleaseUVM(*a_ppUVM); + *a_ppVM = NULL; + *a_ppUVM = NULL; +} + + /** * Initialize the release logging facility. In case something * goes wrong, there will be no release logging. Maybe in the future @@ -5302,24 +6020,26 @@ HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine) } } - PRTLOGGER loggerRelease; - static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; - RTUINT fFlags = RTLOGFLAGS_PREFIX_TIME_PROG; + static const char * const s_apszGroups[] = VBOX_LOGGROUP_NAMES; + char szError[RTPATH_MAX + 128] = ""; + PRTLOGGER pReleaseLogger; + uint32_t fFlags = RTLOGFLAGS_PREFIX_TIME_PROG | RTLOGFLAGS_RESTRICT_GROUPS; #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) fFlags |= RTLOGFLAGS_USECRLF; #endif - char szError[RTPATH_MAX + 128] = ""; - int vrc = RTLogCreateEx(&loggerRelease, fFlags, "all", + int vrc = RTLogCreateEx(&pReleaseLogger, fFlags, "all all.restrict default.unrestricted", "VBOX_RELEASE_LOG", RT_ELEMENTS(s_apszGroups), s_apszGroups, RTLOGDEST_FILE, NULL /* pfnBeginEnd */, 0 /* cHistory */, 0 /* cbHistoryFileMax */, 0 /* uHistoryTimeMax */, szError, sizeof(szError), logFile.c_str()); if (RT_SUCCESS(vrc)) { + RTLogSetGroupLimit(pReleaseLogger, 32768); + /* some introductory information */ RTTIMESPEC timeSpec; char szTmp[256]; RTTimeSpecToString(RTTimeNow(&timeSpec), szTmp, sizeof(szTmp)); - RTLogRelLogger(loggerRelease, 0, ~0U, + RTLogRelLogger(pReleaseLogger, 0, ~0U, "VirtualBox %s r%u %s (%s %s) release log\n" #ifdef VBOX_BLEEDING_EDGE "EXPERIMENTAL build " VBOX_BLEEDING_EDGE "\n" @@ -5330,22 +6050,22 @@ HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine) vrc = RTSystemQueryOSInfo(RTSYSOSINFO_PRODUCT, szTmp, sizeof(szTmp)); if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) - RTLogRelLogger(loggerRelease, 0, ~0U, "OS Product: %s\n", szTmp); + RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Product: %s\n", szTmp); vrc = RTSystemQueryOSInfo(RTSYSOSINFO_RELEASE, szTmp, sizeof(szTmp)); if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) - RTLogRelLogger(loggerRelease, 0, ~0U, "OS Release: %s\n", szTmp); + RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Release: %s\n", szTmp); vrc = RTSystemQueryOSInfo(RTSYSOSINFO_VERSION, szTmp, sizeof(szTmp)); if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) - RTLogRelLogger(loggerRelease, 0, ~0U, "OS Version: %s\n", szTmp); + RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Version: %s\n", szTmp); vrc = RTSystemQueryOSInfo(RTSYSOSINFO_SERVICE_PACK, szTmp, sizeof(szTmp)); if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) - RTLogRelLogger(loggerRelease, 0, ~0U, "OS Service Pack: %s\n", szTmp); + RTLogRelLogger(pReleaseLogger, 0, ~0U, "OS Service Pack: %s\n", szTmp); vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_NAME, szTmp, sizeof(szTmp)); if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) - RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Name: %s\n", szTmp); + RTLogRelLogger(pReleaseLogger, 0, ~0U, "DMI Product Name: %s\n", szTmp); vrc = RTSystemQueryDmiString(RTSYSDMISTR_PRODUCT_VERSION, szTmp, sizeof(szTmp)); if (RT_SUCCESS(vrc) || vrc == VERR_BUFFER_OVERFLOW) - RTLogRelLogger(loggerRelease, 0, ~0U, "DMI Product Version: %s\n", szTmp); + RTLogRelLogger(pReleaseLogger, 0, ~0U, "DMI Product Version: %s\n", szTmp); ComPtr<IHost> pHost; pVirtualBox->COMGETTER(Host)(pHost.asOutParam()); @@ -5353,13 +6073,13 @@ HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine) ULONG cMbHostRamAvail = 0; pHost->COMGETTER(MemorySize)(&cMbHostRam); pHost->COMGETTER(MemoryAvailable)(&cMbHostRamAvail); - RTLogRelLogger(loggerRelease, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n", + RTLogRelLogger(pReleaseLogger, 0, ~0U, "Host RAM: %uMB RAM, available: %uMB\n", cMbHostRam, cMbHostRamAvail); /* the package type is interesting for Linux distributions */ char szExecName[RTPATH_MAX]; char *pszExecName = RTProcGetExecutablePath(szExecName, sizeof(szExecName)); - RTLogRelLogger(loggerRelease, 0, ~0U, + RTLogRelLogger(pReleaseLogger, 0, ~0U, "Executable: %s\n" "Process ID: %u\n" "Package type: %s" @@ -5372,11 +6092,11 @@ HRESULT Console::consoleInitReleaseLog(const ComPtr<IMachine> aMachine) VBOX_PACKAGE_STRING); /* register this logger as the release logger */ - RTLogRelSetDefaultInstance(loggerRelease); + RTLogRelSetDefaultInstance(pReleaseLogger); hrc = S_OK; /* Explicitly flush the log in case of VBOX_RELEASE_LOG=buffered. */ - RTLogFlush(loggerRelease); + RTLogFlush(pReleaseLogger); } else hrc = setError(E_FAIL, @@ -5562,8 +6282,6 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused) /* Check all types of shared folders and compose a single list */ SharedFolderDataMap sharedFolders; { - // @todo umoeller - /* first, insert global folders */ for (SharedFolderDataMap::const_iterator it = m_mapGlobalSharedFolders.begin(); it != m_mapGlobalSharedFolders.end(); @@ -5709,6 +6427,7 @@ HRESULT Console::powerUp(IProgress **aProgress, bool aPaused) rc = consoleInitReleaseLog(mMachine); if (FAILED(rc)) throw rc; + mptrExtPackManager->dumpAllToReleaseLog(); #ifdef RT_OS_SOLARIS /* setup host core dumper for the VM */ @@ -5878,7 +6597,8 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/) /* sanity */ Assert(mVMDestroying == false); - Assert(mpVM != NULL); + PUVM pUVM = mpUVM; Assert(pUVM != NULL); + uint32_t cRefs = VMR3RetainUVM(pUVM); Assert(cRefs != UINT32_MAX); AssertMsg( mMachineState == MachineState_Running || mMachineState == MachineState_Paused @@ -5994,9 +6714,9 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/) { LogFlowThisFunc(("Powering off the VM...\n")); alock.leave(); - vrc = VMR3PowerOff(mpVM); + vrc = VMR3PowerOff(VMR3GetVM(pUVM)); #ifdef VBOX_WITH_EXTPACK - mptrExtPackManager->callAllVmPowerOffHooks(this, mpVM); + mptrExtPackManager->callAllVmPowerOffHooks(this, VMR3GetVM(pUVM)); #endif alock.enter(); } @@ -6036,7 +6756,7 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/) bool fHasUSBController = false; { PPDMIBASE pBase; - vrc = PDMR3QueryLun(mpVM, "usb-ohci", 0, 0, &pBase); + vrc = PDMR3QueryLun(VMR3GetVM(pUVM), "usb-ohci", 0, 0, &pBase); if (RT_SUCCESS(vrc)) { fHasUSBController = true; @@ -6051,16 +6771,16 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/) * instantiating SafeVMPtr to access mpVM). It's safe here because * mVMDestroying is set which should prevent any activity. */ - /* Set mpVM to NULL early just in case if some old code is not using - * addVMCaller()/releaseVMCaller(). */ - PVM pVM = mpVM; - mpVM = NULL; + /* Set mpUVM to NULL early just in case if some old code is not using + * addVMCaller()/releaseVMCaller(). (We have our own ref on pUVM.) */ + VMR3ReleaseUVM(mpUVM); + mpUVM = NULL; LogFlowThisFunc(("Destroying the VM...\n")); alock.leave(); - vrc = VMR3Destroy(pVM); + vrc = VMR3Destroy(VMR3GetVM(pUVM)); /* take the lock again */ alock.enter(); @@ -6083,8 +6803,9 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/) } else { - /* bad bad bad, but what to do? */ - mpVM = pVM; + /* bad bad bad, but what to do? (Give Console our UVM ref.) */ + mpUVM = pUVM; + pUVM = NULL; rc = setError(VBOX_E_VM_ERROR, tr("Could not destroy the machine. (Error: %Rrc)"), vrc); @@ -6105,11 +6826,17 @@ HRESULT Console::powerDown(IProgress *aProgress /*= NULL*/) vrc); } - /* Finished with destruction. Note that if something impossible happened and - * we've failed to destroy the VM, mVMDestroying will remain true and - * mMachineState will be something like Stopping, so most Console methods - * will return an error to the caller. */ - if (mpVM == NULL) + /* + * Finished with the destruction. + * + * Note that if something impossible happened and we've failed to destroy + * the VM, mVMDestroying will remain true and mMachineState will be + * something like Stopping, so most Console methods will return an error + * to the caller. + */ + if (mpUVM != NULL) + VMR3ReleaseUVM(pUVM); + else mVMDestroying = false; if (SUCCEEDED(rc)) @@ -6223,16 +6950,15 @@ HRESULT Console::fetchSharedFolders(BOOL aGlobal) LogFlowThisFunc(("Entering\n")); - /* protect mpVM (if not NULL) */ + /* Check if we're online and keep it that way. */ + SafeVMPtrQuiet ptrVM(this); AutoVMCallerQuietWeak autoVMCaller(this); + bool const online = ptrVM.isOk() + && m_pVMMDev + && m_pVMMDev->isShFlActive(); HRESULT rc = S_OK; - bool online = mpVM - && autoVMCaller.isOk() - && m_pVMMDev - && m_pVMMDev->isShFlActive(); - try { if (aGlobal) @@ -6278,7 +7004,6 @@ HRESULT Console::fetchSharedFolders(BOOL aGlobal) SharedFolderData(strHostPath, writable, autoMount))); /* send changes to HGCM if the VM is running */ - /// @todo umoeller report errors as runtime warnings through VMSetError if (online) { SharedFolderDataMap::iterator it = oldFolders.find(strName); @@ -6345,7 +7070,7 @@ HRESULT Console::fetchSharedFolders(BOOL aGlobal) catch (HRESULT rc2) { if (online) - setVMRuntimeErrorCallbackF(mpVM, this, 0, "BrokenSharedFolder", + setVMRuntimeErrorCallbackF(ptrVM, this, 0, "BrokenSharedFolder", N_("Broken shared folder!")); } @@ -6398,7 +7123,7 @@ HRESULT Console::createSharedFolder(const Utf8Str &strName, const SharedFolderDa ComAssertRet(aData.m_strHostPath.isNotEmpty(), E_FAIL); /* sanity checks */ - AssertReturn(mpVM, E_FAIL); + AssertReturn(mpUVM, E_FAIL); AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL); VBOXHGCMSVCPARM parms[SHFL_CPARMS_ADD_MAPPING2]; @@ -6503,7 +7228,7 @@ HRESULT Console::removeSharedFolder(const Utf8Str &strName) ComAssertRet(strName.isNotEmpty(), E_FAIL); /* sanity checks */ - AssertReturn(mpVM, E_FAIL); + AssertReturn(mpUVM, E_FAIL); AssertReturn(m_pVMMDev && m_pVMMDev->isShFlActive(), E_FAIL); VBOXHGCMSVCPARM parms; @@ -6715,10 +7440,10 @@ DECLCALLBACK(void) Console::vmstateChangeCallback(PVM aVM, case VMSTATE_RESETTING: { - #ifdef VBOX_WITH_GUEST_PROPS +#ifdef VBOX_WITH_GUEST_PROPS /* Do not take any read/write locks here! */ that->guestPropertiesHandleVMReset(); - #endif +#endif break; } @@ -6907,9 +7632,10 @@ HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs) hrc = aHostDevice->COMGETTER(Remote)(&fRemote); ComAssertComRCRetRC(hrc); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* Get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); LogFlowThisFunc(("Proxying USB device '%s' {%RTuuid}...\n", Address.c_str(), uuid.raw())); @@ -6918,8 +7644,9 @@ HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs) alock.leave(); /** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */ - int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY, - (PFNRT)usbAttachCallback, 6, this, aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs); + int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY, + (PFNRT)usbAttachCallback, 7, + this, ptrVM.raw(), aHostDevice, uuid.raw(), fRemote, Address.c_str(), aMaskedIfs); /* restore the lock */ alock.enter(); @@ -6929,7 +7656,7 @@ HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs) if (RT_FAILURE(vrc)) { LogWarningThisFunc(("Failed to create proxy device for '%s' {%RTuuid} (%Rrc)\n", - Address.c_str(), uuid.raw(), vrc)); + Address.c_str(), uuid.raw(), vrc)); switch (vrc) { @@ -6963,7 +7690,7 @@ HRESULT Console::attachUSBDevice(IUSBDevice *aHostDevice, ULONG aMaskedIfs) */ //static DECLCALLBACK(int) -Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs) +Console::usbAttachCallback(Console *that, PVM pVM, IUSBDevice *aHostDevice, PCRTUUID aUuid, bool aRemote, const char *aAddress, ULONG aMaskedIfs) { LogFlowFuncEnter(); LogFlowFunc(("that={%p}\n", that)); @@ -6986,7 +7713,7 @@ Console::usbAttachCallback(Console *that, IUSBDevice *aHostDevice, PCRTUUID aUui AssertComRCReturn(hrc, VERR_GENERAL_FAILURE); Assert(portVersion == 1 || portVersion == 2); - int vrc = PDMR3USBCreateProxyDevice(that->mpVM, aUuid, aRemote, aAddress, pvRemoteBackend, + int vrc = PDMR3USBCreateProxyDevice(pVM, aUuid, aRemote, aAddress, pvRemoteBackend, portVersion == 1 ? VUSB_STDVER_11 : VUSB_STDVER_20, aMaskedIfs); if (RT_SUCCESS(vrc)) { @@ -7026,22 +7753,24 @@ HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt) /* still want a lock object because we need to leave it */ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - /* protect mpVM */ - AutoVMCaller autoVMCaller(this); - if (FAILED(autoVMCaller.rc())) return autoVMCaller.rc(); + /* Get the VM handle. */ + SafeVMPtr ptrVM(this); + if (!ptrVM.isOk()) + return ptrVM.rc(); /* if the device is attached, then there must at least one USB hub. */ - AssertReturn(PDMR3USBHasHub(mpVM), E_FAIL); + AssertReturn(PDMR3USBHasHub(ptrVM), E_FAIL); LogFlowThisFunc(("Detaching USB proxy device {%RTuuid}...\n", - (*aIt)->id().raw())); + (*aIt)->id().raw())); /* leave the lock before a VMR3* call (EMT will call us back)! */ alock.leave(); /** @todo just do everything here and only wrap the PDMR3Usb call. That'll offload some notification stuff from the EMT thread. */ - int vrc = VMR3ReqCallWait(mpVM, VMCPUID_ANY, - (PFNRT) usbDetachCallback, 4, this, &aIt, (*aIt)->id().raw()); + int vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY, + (PFNRT)usbDetachCallback, 5, + this, ptrVM.raw(), &aIt, (*aIt)->id().raw()); ComAssertRCRet(vrc, E_FAIL); return S_OK; @@ -7058,7 +7787,7 @@ HRESULT Console::detachUSBDevice(USBDeviceList::iterator &aIt) */ //static DECLCALLBACK(int) -Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID aUuid) +Console::usbDetachCallback(Console *that, PVM pVM, USBDeviceList::iterator *aIt, PCRTUUID aUuid) { LogFlowFuncEnter(); LogFlowFunc(("that={%p}\n", that)); @@ -7082,7 +7811,7 @@ Console::usbDetachCallback(Console *that, USBDeviceList::iterator *aIt, PCRTUUID that->consoleVRDPServer()->USBBackendReleasePointer(&guid); } - int vrc = PDMR3USBDetachDevice(that->mpVM, aUuid); + int vrc = PDMR3USBDetachDevice(pVM, aUuid); if (RT_SUCCESS(vrc)) { @@ -7148,7 +7877,7 @@ HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter) memset(&IfReq, 0, sizeof(IfReq)); /* The name of the TAP interface we are using */ Bstr tapDeviceName; - rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam()); + rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam()); if (FAILED(rc)) tapDeviceName.setNull(); /* Is this necessary? */ if (tapDeviceName.isEmpty()) @@ -7226,7 +7955,7 @@ HRESULT Console::attachToTapInterface(INetworkAdapter *networkAdapter) */ /* The name of the TAP interface we are using */ Bstr tapDeviceName; - rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam()); + rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam()); if (FAILED(rc)) tapDeviceName.setNull(); /* Is this necessary? */ if (tapDeviceName.isEmpty()) @@ -7313,7 +8042,7 @@ HRESULT Console::detachFromTapInterface(INetworkAdapter *networkAdapter) */ Bstr tapDeviceName, tapTerminateApplication; bool isStatic = true; - rc = networkAdapter->COMGETTER(HostInterface)(tapDeviceName.asOutParam()); + rc = networkAdapter->COMGETTER(BridgedInterface)(tapDeviceName.asOutParam()); if (FAILED(rc) || tapDeviceName.isEmpty()) { /* If the name is empty, this is a dynamic TAP device, so close it now, @@ -7749,7 +8478,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser) AutoWriteLock alock(pConsole COMMA_LOCKVAL_SRC_POS); /* sanity */ - Assert(pConsole->mpVM == NULL); + Assert(pConsole->mpUVM == NULL); try { @@ -7972,7 +8701,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser) { /* -> ConsoleImplTeleporter.cpp */ bool fPowerOffOnFailure; - rc = pConsole->teleporterTrg(pVM, pMachine, &task->mErrorMsg, task->mStartPaused, + rc = pConsole->teleporterTrg(VMR3GetUVM(pVM), pMachine, &task->mErrorMsg, task->mStartPaused, task->mProgress, &fPowerOffOnFailure); if (FAILED(rc) && fPowerOffOnFailure) { @@ -8076,7 +8805,8 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser) /* * If VMR3Create() failed it has released the VM memory. */ - pConsole->mpVM = NULL; + VMR3ReleaseUVM(pConsole->mpUVM); + pConsole->mpUVM = NULL; } if (SUCCEEDED(rc) && RT_FAILURE(vrc)) @@ -8123,7 +8853,7 @@ DECLCALLBACK(int) Console::powerUpThread(RTTHREAD Thread, void *pvUser) /* preserve existing error info */ ErrorInfoKeeper eik; - Assert(pConsole->mpVM == NULL); + Assert(pConsole->mpUVM == NULL); vmstateChangeCallback(NULL, VMSTATE_TERMINATED, VMSTATE_CREATING, pConsole); } @@ -8229,6 +8959,7 @@ DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole, phrc, true /* fAttachDetach */, false /* fForceUnmount */, + false /* fHotplug */, pVM, NULL /* paLedDevType */); /** @todo this dumps everything attached to this device instance, which @@ -8248,8 +8979,8 @@ DECLCALLBACK(int) Console::reconfigureMediumAttachment(Console *pConsole, */ static void takesnapshotProgressCancelCallback(void *pvUser) { - PVM pVM = (PVM)pvUser; - SSMR3Cancel(pVM); + PUVM pUVM = (PUVM)pvUser; + SSMR3Cancel(VMR3GetVM(pUVM)); } /** @@ -8324,13 +9055,17 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser) { Utf8Str strSavedStateFile(pTask->bstrSavedStateFile); + SafeVMPtr ptrVM(that); + if (!ptrVM.isOk()) + throw ptrVM.rc(); + pTask->mProgress->SetNextOperation(Bstr(tr("Saving the machine state")).raw(), pTask->ulMemSize); // operation weight, same as computed when setting up progress object - pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, that->mpVM); + pTask->mProgress->setCancelCallback(takesnapshotProgressCancelCallback, ptrVM.rawUVM()); alock.leave(); LogFlowFunc(("VMR3Save...\n")); - int vrc = VMR3Save(that->mpVM, + int vrc = VMR3Save(ptrVM, strSavedStateFile.c_str(), true /*fContinueAfterwards*/, Console::stateProgressCallback, @@ -8407,12 +9142,12 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser) * don't leave the lock since reconfigureMediumAttachment * isn't going to need the Console lock. */ - vrc = VMR3ReqCallWait(that->mpVM, + vrc = VMR3ReqCallWait(ptrVM, VMCPUID_ANY, (PFNRT)reconfigureMediumAttachment, 13, that, - that->mpVM, + ptrVM.raw(), pcszDevice, lInstance, enmBus, @@ -8478,8 +9213,9 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser) if (pTask->lastMachineState == MachineState_Running) { LogFlowFunc(("VMR3Resume...\n")); + SafeVMPtr ptrVM(that); alock.leave(); - int vrc = VMR3Resume(that->mpVM); + int vrc = VMR3Resume(ptrVM); alock.enter(); if (RT_FAILURE(vrc)) { @@ -8496,7 +9232,7 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser) { /** @todo this could probably be made more generic and reused elsewhere. */ /* paranoid cleanup on for a failed online snapshot. */ - VMSTATE enmVMState = VMR3GetState(that->mpVM); + VMSTATE enmVMState = VMR3GetStateU(that->mpUVM); switch (enmVMState) { case VMSTATE_RUNNING: @@ -8535,8 +9271,9 @@ DECLCALLBACK(int) Console::fntTakeSnapshotWorker(RTTHREAD Thread, void *pvUser) { Assert(pTask->lastMachineState == MachineState_Running); LogFlowFunc(("VMR3Resume (on failure)...\n")); + SafeVMPtr ptrVM(that); alock.leave(); - int vrc = VMR3Resume(that->mpVM); AssertLogRelRC(vrc); + int vrc = VMR3Resume(ptrVM); AssertLogRelRC(vrc); alock.enter(); if (RT_FAILURE(vrc)) that->setMachineState(MachineState_Paused); @@ -8596,7 +9333,7 @@ DECLCALLBACK(int) Console::saveStateThread(RTTHREAD Thread, void *pvUser) LogFlowFunc(("Saving the state to '%s'...\n", task->mSavedStateFile.c_str())); bool fSuspenededBySave; - int vrc = VMR3Save(that->mpVM, + int vrc = VMR3Save(task->mpVM, task->mSavedStateFile.c_str(), false, /*fContinueAfterwards*/ Console::stateProgressCallback, @@ -8757,6 +9494,17 @@ typedef struct DRVMAINSTATUS /** The unit number corresponding to the last entry in the LED array. * (The size of the LED array is iLastLUN - iFirstLUN + 1.) */ RTUINT iLastLUN; + /** Pointer to the driver instance. */ + PPDMDRVINS pDrvIns; + /** The Media Notify interface. */ + PDMIMEDIANOTIFY IMediaNotify; + /** Map for translating PDM storage controller/LUN information to + * IMediumAttachment references. */ + Console::MediumAttachmentMap *pmapMediumAttachments; + /** Device name+instance for mapping */ + char *pszDeviceInstance; + /** Pointer to the Console object, for driver triggered activities. */ + Console *pConsole; } DRVMAINSTATUS, *PDRVMAINSTATUS; @@ -8771,7 +9519,7 @@ typedef struct DRVMAINSTATUS */ DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, unsigned iLUN) { - PDRVMAINSTATUS pData = (PDRVMAINSTATUS)(void *)pInterface; + PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, ILedConnectors)); if (iLUN >= pData->iFirstLUN && iLUN <= pData->iLastLUN) { PPDMLED pLed; @@ -8785,6 +9533,59 @@ DECLCALLBACK(void) Console::drvStatus_UnitChanged(PPDMILEDCONNECTORS pInterface, /** + * Notification about a medium eject. + * + * @returns VBox status. + * @param pInterface Pointer to the interface structure containing the called function pointer. + * @param uLUN The unit number. + */ +DECLCALLBACK(int) Console::drvStatus_MediumEjected(PPDMIMEDIANOTIFY pInterface, unsigned uLUN) +{ + PDRVMAINSTATUS pData = (PDRVMAINSTATUS)((uintptr_t)pInterface - RT_OFFSETOF(DRVMAINSTATUS, IMediaNotify)); + PPDMDRVINS pDrvIns = pData->pDrvIns; + LogFunc(("uLUN=%d\n", uLUN)); + if (pData->pmapMediumAttachments) + { + AutoWriteLock alock(pData->pConsole COMMA_LOCKVAL_SRC_POS); + + ComPtr<IMediumAttachment> pMediumAtt; + Utf8Str devicePath = Utf8StrFmt("%s/LUN#%u", pData->pszDeviceInstance, uLUN); + Console::MediumAttachmentMap::const_iterator end = pData->pmapMediumAttachments->end(); + Console::MediumAttachmentMap::const_iterator it = pData->pmapMediumAttachments->find(devicePath); + if (it != end) + pMediumAtt = it->second; + Assert(!pMediumAtt.isNull()); + if (!pMediumAtt.isNull()) + { + IMedium *pMedium; + HRESULT rc = pMediumAtt->COMGETTER(Medium)(&pMedium); + AssertComRC(rc); + BOOL fHostDrive = FALSE; + rc = pMedium->COMGETTER(HostDrive)(&fHostDrive); + AssertComRC(rc); + if (!fHostDrive) + { + alock.release(); + + ComPtr<IMediumAttachment> pNewMediumAtt; + rc = pData->pConsole->mControl->EjectMedium(pMediumAtt, pNewMediumAtt.asOutParam()); + if (SUCCEEDED(rc)) + fireMediumChangedEvent(pData->pConsole->mEventSource, pNewMediumAtt); + + alock.acquire(); + if (pNewMediumAtt != pMediumAtt) + { + pData->pmapMediumAttachments->erase(devicePath); + pData->pmapMediumAttachments->insert(std::make_pair(devicePath, pNewMediumAtt)); + } + } + } + } + return VINF_SUCCESS; +} + + +/** * @interface_method_impl{PDMIBASE,pfnQueryInterface} */ DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, const char *pszIID) @@ -8793,6 +9594,7 @@ DECLCALLBACK(void *) Console::drvStatus_QueryInterface(PPDMIBASE pInterface, co PDRVMAINSTATUS pThis = PDMINS_2_DATA(pDrvIns, PDRVMAINSTATUS); PDMIBASE_RETURN_INTERFACE(pszIID, PDMIBASE, &pDrvIns->IBase); PDMIBASE_RETURN_INTERFACE(pszIID, PDMILEDCONNECTORS, &pThis->ILedConnectors); + PDMIBASE_RETURN_INTERFACE(pszIID, PDMIMEDIANOTIFY, &pThis->IMediaNotify); return NULL; } @@ -8832,7 +9634,7 @@ DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCf /* * Validate configuration. */ - if (!CFGMR3AreValuesValid(pCfg, "papLeds\0First\0Last\0")) + if (!CFGMR3AreValuesValid(pCfg, "papLeds\0pmapMediumAttachments\0DeviceInstance\0pConsole\0First\0Last\0")) return VERR_PDM_DRVINS_UNKNOWN_CFG_VALUES; AssertMsgReturn(PDMDrvHlpNoAttach(pDrvIns) == VERR_PDM_NO_ATTACHED_DRIVER, ("Configuration error: Not possible to attach anything to this driver!\n"), @@ -8843,6 +9645,9 @@ DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCf */ pDrvIns->IBase.pfnQueryInterface = Console::drvStatus_QueryInterface; pData->ILedConnectors.pfnUnitChanged = Console::drvStatus_UnitChanged; + pData->IMediaNotify.pfnEjected = Console::drvStatus_MediumEjected; + pData->pDrvIns = pDrvIns; + pData->pszDeviceInstance = NULL; /* * Read config. @@ -8854,6 +9659,28 @@ DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCf return rc; } + rc = CFGMR3QueryPtrDef(pCfg, "pmapMediumAttachments", (void **)&pData->pmapMediumAttachments, NULL); + if (RT_FAILURE(rc)) + { + AssertMsgFailed(("Configuration error: Failed to query the \"pmapMediumAttachments\" value! rc=%Rrc\n", rc)); + return rc; + } + if (pData->pmapMediumAttachments) + { + rc = CFGMR3QueryStringAlloc(pCfg, "DeviceInstance", &pData->pszDeviceInstance); + if (RT_FAILURE(rc)) + { + AssertMsgFailed(("Configuration error: Failed to query the \"DeviceInstance\" value! rc=%Rrc\n", rc)); + return rc; + } + rc = CFGMR3QueryPtr(pCfg, "pConsole", (void **)&pData->pConsole); + if (RT_FAILURE(rc)) + { + AssertMsgFailed(("Configuration error: Failed to query the \"pConsole\" value! rc=%Rrc\n", rc)); + return rc; + } + } + rc = CFGMR3QueryU32(pCfg, "First", &pData->iFirstLUN); if (rc == VERR_CFGM_VALUE_NOT_FOUND) pData->iFirstLUN = 0; @@ -8893,7 +9720,7 @@ DECLCALLBACK(int) Console::drvStatus_Construct(PPDMDRVINS pDrvIns, PCFGMNODE pCf /** - * Keyboard driver registration record. + * Console status driver (LED) registration record. */ const PDMDRVREG Console::DrvStatusReg = { |