summaryrefslogtreecommitdiff
path: root/src/VBox/Main/src-server/MachineImpl.cpp
diff options
context:
space:
mode:
authorFelix Geyer <debfx-pkg@fobos.de>2011-07-29 17:55:18 +0200
committerFelix Geyer <debfx-pkg@fobos.de>2011-07-29 17:55:18 +0200
commitcba113ca2826bc4814be2f69a7704c865a37d4ea (patch)
tree511123b10dd1e58e56958520534f5c50e6f570fc /src/VBox/Main/src-server/MachineImpl.cpp
parent6a16f6900dd884e07125b51c9625f6be0a1f9b70 (diff)
downloadvirtualbox-cba113ca2826bc4814be2f69a7704c865a37d4ea.tar.gz
Imported Upstream version 4.1.0-dfsgupstream/4.1.0-dfsg
Diffstat (limited to 'src/VBox/Main/src-server/MachineImpl.cpp')
-rw-r--r--src/VBox/Main/src-server/MachineImpl.cpp1109
1 files changed, 881 insertions, 228 deletions
diff --git a/src/VBox/Main/src-server/MachineImpl.cpp b/src/VBox/Main/src-server/MachineImpl.cpp
index ae2b4c76e..8a6f3b139 100644
--- a/src/VBox/Main/src-server/MachineImpl.cpp
+++ b/src/VBox/Main/src-server/MachineImpl.cpp
@@ -1,4 +1,4 @@
-/* $Id: MachineImpl.cpp $ */
+/* $Id: MachineImpl.cpp 38056 2011-07-19 09:03:26Z vboxsync $ */
/** @file
* Implementation of IMachine in VBoxSVC.
*/
@@ -49,6 +49,10 @@
#include "DisplayImpl.h"
#include "DisplayUtils.h"
#include "BandwidthControlImpl.h"
+#include "MachineImplCloneVM.h"
+
+// generated header
+#include "VBoxEvents.h"
#ifdef VBOX_WITH_USB
# include "USBProxyService.h"
@@ -68,6 +72,7 @@
#include <iprt/string.h>
#include <VBox/com/array.h>
+#include <VBox/com/list.h>
#include <VBox/err.h>
#include <VBox/param.h>
@@ -153,7 +158,7 @@ Machine::HWData::HWData()
mMonitorCount = 1;
mHWVirtExEnabled = true;
mHWVirtExNestedPagingEnabled = true;
-#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX) && !defined(RT_OS_SOLARIS)
+#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
mHWVirtExLargePagesEnabled = true;
#else
/* Not supported on 32 bits hosts. */
@@ -223,7 +228,7 @@ Machine::MediaData::~MediaData()
/////////////////////////////////////////////////////////////////////////////
Machine::Machine()
- : mGuestHAL(NULL),
+ : mCollectorGuest(NULL),
mPeer(NULL),
mParent(NULL)
{}
@@ -234,13 +239,14 @@ Machine::~Machine()
HRESULT Machine::FinalConstruct()
{
LogFlowThisFunc(("\n"));
- return S_OK;
+ return BaseFinalConstruct();
}
void Machine::FinalRelease()
{
LogFlowThisFunc(("\n"));
uninit();
+ BaseFinalRelease();
}
/**
@@ -787,6 +793,11 @@ void Machine::uninit()
// uninit media from this machine's media registry, if they're still there
Guid uuidMachine(getId());
+
+ /* XXX This will fail with
+ * "cannot be closed because it is still attached to 1 virtual machines"
+ * because at this point we did not call uninitDataAndChildObjects() yet
+ * and therefore also removeBackReference() for all these mediums was not called! */
if (!uuidMachine.isEmpty()) // can be empty if we're called from a failure of Machine::init
mParent->unregisterMachineMedia(uuidMachine);
@@ -933,6 +944,12 @@ STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName)
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
+ // prohibit setting a UUID only as the machine name, or else it can
+ // never be found by findMachine()
+ Guid test(aName);
+ if (test.isNotEmpty())
+ return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name"));
+
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
HRESULT rc = checkStateDependency(MutableStateDep);
@@ -1305,14 +1322,15 @@ STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount)
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- /* We cant go below the current number of CPUs if hotplug is enabled*/
+ /* We cant go below the current number of CPUs attached if hotplug is enabled*/
if (mHWData->mCPUHotPlugEnabled)
{
for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++)
{
if (mHWData->mCPUAttached[idx])
return setError(E_INVALIDARG,
- tr(": %lu (must be higher than or equal to %lu)"),
+ tr("There is still a CPU attached to socket %lu."
+ "Detach the CPU before removing the socket"),
CPUCount, idx+1);
}
}
@@ -1437,7 +1455,7 @@ STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
if ( (cCpusAttached != mHWData->mCPUCount)
|| (iHighestId >= mHWData->mCPUCount))
return setError(E_INVALIDARG,
- tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n"));
+ tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached"));
setModified(IsModified_MachineData);
mHWData.backup();
@@ -1449,6 +1467,30 @@ STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled)
return rc;
}
+STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled)
+{
+ NOREF(enabled);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled)
+{
+ NOREF(enabled);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP Machine::COMGETTER(EmulatedUSBWebcameraEnabled)(BOOL *enabled)
+{
+ NOREF(enabled);
+ return E_NOTIMPL;
+}
+
+STDMETHODIMP Machine::COMSETTER(EmulatedUSBWebcameraEnabled)(BOOL enabled)
+{
+ NOREF(enabled);
+ return E_NOTIMPL;
+}
+
STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled)
{
CheckComArgOutPointerValid(enabled);
@@ -1804,7 +1846,7 @@ STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, UL
case 0x9:
case 0xA:
if (mHWData->mCpuIdStdLeafs[aId].ulId != aId)
- return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
+ return E_INVALIDARG;
*aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax;
*aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx;
@@ -1824,7 +1866,7 @@ STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, UL
case 0x80000009:
case 0x8000000A:
if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId)
- return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId);
+ return E_INVALIDARG;
*aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax;
*aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx;
@@ -2010,6 +2052,9 @@ STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL
case HWVirtExPropertyType_LargePages:
*aVal = mHWData->mHWVirtExLargePagesEnabled;
+#if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */
+ *aVal = FALSE;
+#endif
break;
case HWVirtExPropertyType_Force:
@@ -2323,7 +2368,7 @@ STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath)
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- mSSData->mStateFilePath.cloneTo(aStateFilePath);
+ mSSData->strStateFilePath.cloneTo(aStateFilePath);
return S_OK;
}
@@ -3389,16 +3434,32 @@ STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
- if (Global::IsOnlineOrTransient(mData->mMachineState))
- return setError(VBOX_E_INVALID_VM_STATE,
- tr("Invalid machine state: %s"),
- Global::stringifyMachineState(mData->mMachineState));
-
/* Check for an existing controller. */
ComObjPtr<StorageController> ctl;
rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
if (FAILED(rc)) return rc;
+ StorageControllerType_T ctrlType;
+ rc = ctl->COMGETTER(ControllerType)(&ctrlType);
+ if (FAILED(rc))
+ return setError(E_FAIL,
+ tr("Could not get type of controller '%ls'"),
+ aControllerName);
+
+ /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
+ bool fHotplug = false;
+ if (Global::IsOnlineOrTransient(mData->mMachineState))
+ fHotplug = true;
+
+ if (fHotplug && !isControllerHotplugCapable(ctrlType))
+ return setError(VBOX_E_INVALID_VM_STATE,
+ tr("Controller '%ls' does not support hotplugging"),
+ aControllerName);
+
+ if (fHotplug && aType == DeviceType_DVD)
+ return setError(VBOX_E_INVALID_VM_STATE,
+ tr("Attaching a DVD drive while the VM is running is not supported"));
+
// check that the port and device are not out of range
rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice);
if (FAILED(rc)) return rc;
@@ -3745,6 +3806,9 @@ STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
aDevice,
aType,
fIndirect,
+ false /* fPassthrough */,
+ false /* fTempEject */,
+ false /* fNonRotational */,
Utf8Str::Empty);
if (FAILED(rc)) return rc;
@@ -3769,6 +3833,9 @@ STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName,
treeLock.leave();
alock.release();
+ if (fHotplug)
+ rc = onStorageDeviceChange(attachment, FALSE /* aRemove */);
+
mParent->saveRegistries(llRegistriesThatNeedSaving);
return rc;
@@ -3779,7 +3846,7 @@ STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort
{
CheckComArgStrNotEmptyOrNull(aControllerName);
- LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
+ LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
aControllerName, aControllerPort, aDevice));
AutoCaller autoCaller(this);
@@ -3794,10 +3861,27 @@ STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort
AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
+ /* Check for an existing controller. */
+ ComObjPtr<StorageController> ctl;
+ rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */);
+ if (FAILED(rc)) return rc;
+
+ StorageControllerType_T ctrlType;
+ rc = ctl->COMGETTER(ControllerType)(&ctrlType);
+ if (FAILED(rc))
+ return setError(E_FAIL,
+ tr("Could not get type of controller '%ls'"),
+ aControllerName);
+
+ /* Check that the controller can do hotplugging if we detach the device while the VM is running. */
+ bool fHotplug = false;
if (Global::IsOnlineOrTransient(mData->mMachineState))
+ fHotplug = true;
+
+ if (fHotplug && !isControllerHotplugCapable(ctrlType))
return setError(VBOX_E_INVALID_VM_STATE,
- tr("Invalid machine state: %s"),
- Global::stringifyMachineState(mData->mMachineState));
+ tr("Controller '%ls' does not support hotplugging"),
+ aControllerName);
MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
aControllerName,
@@ -3808,6 +3892,23 @@ STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort
tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
aDevice, aControllerPort, aControllerName);
+ if (fHotplug && pAttach->getType() == DeviceType_DVD)
+ return setError(VBOX_E_INVALID_VM_STATE,
+ tr("Detaching a DVD drive while the VM is running is not supported"));
+
+ /*
+ * The VM has to detach the device before we delete any implicit diffs.
+ * If this fails we can roll back without loosing data.
+ */
+ if (fHotplug)
+ {
+ alock.leave();
+ rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */);
+ alock.enter();
+ }
+ if (FAILED(rc)) return rc;
+
+ /* If we are here everything went well and we can delete the implicit now. */
rc = detachDevice(pAttach, alock, NULL /* pSnapshot */, &llRegistriesThatNeedSaving);
alock.release();
@@ -3823,7 +3924,7 @@ STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControlle
{
CheckComArgStrNotEmptyOrNull(aControllerName);
- LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n",
+ LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aPassthrough=%d\n",
aControllerName, aControllerPort, aDevice, aPassthrough));
AutoCaller autoCaller(this);
@@ -3865,12 +3966,99 @@ STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControlle
return S_OK;
}
+STDMETHODIMP Machine::TemporaryEjectDevice(IN_BSTR aControllerName, LONG aControllerPort,
+ LONG aDevice, BOOL aTemporaryEject)
+{
+ CheckComArgStrNotEmptyOrNull(aControllerName);
+
+ LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aTemporaryEject=%d\n",
+ aControllerName, aControllerPort, aDevice, aTemporaryEject));
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT rc = checkStateDependency(MutableStateDep);
+ if (FAILED(rc)) return rc;
+
+ MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
+ aControllerName,
+ aControllerPort,
+ aDevice);
+ if (!pAttach)
+ return setError(VBOX_E_OBJECT_NOT_FOUND,
+ tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
+ aDevice, aControllerPort, aControllerName);
+
+
+ setModified(IsModified_Storage);
+ mMediaData.backup();
+
+ AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
+
+ if (pAttach->getType() != DeviceType_DVD)
+ return setError(E_INVALIDARG,
+ tr("Setting temporary eject flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"),
+ aDevice, aControllerPort, aControllerName);
+ pAttach->updateTempEject(!!aTemporaryEject);
+
+ return S_OK;
+}
+
+STDMETHODIMP Machine::NonRotationalDevice(IN_BSTR aControllerName, LONG aControllerPort,
+ LONG aDevice, BOOL aNonRotational)
+{
+ CheckComArgStrNotEmptyOrNull(aControllerName);
+
+ LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aNonRotational=%d\n",
+ aControllerName, aControllerPort, aDevice, aNonRotational));
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT rc = checkStateDependency(MutableStateDep);
+ if (FAILED(rc)) return rc;
+
+ AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL);
+
+ if (Global::IsOnlineOrTransient(mData->mMachineState))
+ return setError(VBOX_E_INVALID_VM_STATE,
+ tr("Invalid machine state: %s"),
+ Global::stringifyMachineState(mData->mMachineState));
+
+ MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments,
+ aControllerName,
+ aControllerPort,
+ aDevice);
+ if (!pAttach)
+ return setError(VBOX_E_OBJECT_NOT_FOUND,
+ tr("No storage device attached to device slot %d on port %d of controller '%ls'"),
+ aDevice, aControllerPort, aControllerName);
+
+
+ setModified(IsModified_Storage);
+ mMediaData.backup();
+
+ AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
+
+ if (pAttach->getType() != DeviceType_HardDisk)
+ return setError(E_INVALIDARG,
+ tr("Setting the non-rotational medium flag rejected as the device attached to device slot %d on port %d of controller '%ls' is not a hard disk"),
+ aDevice, aControllerPort, aControllerName);
+ pAttach->updateNonRotational(!!aNonRotational);
+
+ return S_OK;
+}
+
STDMETHODIMP Machine::SetBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort,
LONG aDevice, IBandwidthGroup *aBandwidthGroup)
{
CheckComArgStrNotEmptyOrNull(aControllerName);
- LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
+ LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
aControllerName, aControllerPort, aDevice));
AutoCaller autoCaller(this);
@@ -3936,7 +4124,7 @@ STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName,
BOOL aForce)
{
int rc = S_OK;
- LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n",
+ LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aForce=%d\n",
aControllerName, aControllerPort, aDevice, aForce));
CheckComArgStrNotEmptyOrNull(aControllerName);
@@ -4054,7 +4242,7 @@ STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName,
LONG aDevice,
IMedium **aMedium)
{
- LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n",
+ LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n",
aControllerName, aControllerPort, aDevice));
CheckComArgStrNotEmptyOrNull(aControllerName);
@@ -4356,10 +4544,10 @@ STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
if (mData->mMachineState == MachineState_Saved)
{
// add the saved state file to the list of files the caller should delete
- Assert(!mSSData->mStateFilePath.isEmpty());
- mData->llFilesToDelete.push_back(mSSData->mStateFilePath);
+ Assert(!mSSData->strStateFilePath.isEmpty());
+ mData->llFilesToDelete.push_back(mSSData->strStateFilePath);
- mSSData->mStateFilePath.setNull();
+ mSSData->strStateFilePath.setNull();
// unconditionally set the machine state to powered off, we now
// know no session has locked the machine
@@ -4450,6 +4638,7 @@ STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode,
struct Machine::DeleteTask
{
ComObjPtr<Machine> pMachine;
+ RTCList< ComPtr<IMedium> > llMediums;
std::list<Utf8Str> llFilesToDelete;
ComObjPtr<Progress> pProgress;
GuidList llRegistriesThatNeedSaving;
@@ -4481,20 +4670,17 @@ STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProg
for (size_t i = 0; i < sfaMedia.size(); ++i)
{
IMedium *pIMedium(sfaMedia[i]);
- Medium *pMedium = static_cast<Medium*>(pIMedium);
- AutoCaller mediumAutoCaller(pMedium);
- if (FAILED(mediumAutoCaller.rc())) return mediumAutoCaller.rc();
-
- Utf8Str bstrLocation = pMedium->getLocationFull();
-
- bool fDoesMediumNeedFileDeletion = pMedium->isMediumFormatFile();
-
- // close the medium now; if that succeeds, then that means the medium is no longer
- // in use and we can add it to the list of files to delete
- rc = pMedium->close(&pTask->llRegistriesThatNeedSaving,
- mediumAutoCaller);
- if (SUCCEEDED(rc) && fDoesMediumNeedFileDeletion)
- pTask->llFilesToDelete.push_back(bstrLocation);
+ ComObjPtr<Medium> pMedium = static_cast<Medium*>(pIMedium);
+ if (pMedium.isNull())
+ return setError(E_INVALIDARG, "The given medium pointer with index %d is invalid", i);
+ SafeArray<BSTR> ids;
+ rc = pMedium->COMGETTER(MachineIds)(ComSafeArrayAsOutParam(ids));
+ if (FAILED(rc)) return rc;
+ /* At this point the medium should not have any back references
+ * anymore. If it has it is attached to another VM and *must* not
+ * deleted. */
+ if (ids.size() < 1)
+ pTask->llMediums.append(pMedium);
}
if (mData->pMachineConfigFile->fileExists())
pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull);
@@ -4504,7 +4690,7 @@ STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProg
static_cast<IMachine*>(this) /* aInitiator */,
Bstr(tr("Deleting files")).raw(),
true /* fCancellable */,
- pTask->llFilesToDelete.size() + 1, // cOperations
+ pTask->llFilesToDelete.size() + pTask->llMediums.size() + 1, // cOperations
BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str()).raw());
int vrc = RTThreadCreate(NULL,
@@ -4569,96 +4755,147 @@ HRESULT Machine::deleteTaskWorker(DeleteTask &task)
AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- ULONG uLogHistoryCount = 3;
- ComPtr<ISystemProperties> systemProperties;
- mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
- if (!systemProperties.isNull())
- systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
+ HRESULT rc = S_OK;
- // delete the files pushed on the task list by Machine::Delete()
- // (this includes saved states of the machine and snapshots and
- // medium storage files from the IMedium list passed in, and the
- // machine XML file)
- std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin();
- while (it != task.llFilesToDelete.end())
+ try
{
- const Utf8Str &strFile = *it;
- LogFunc(("Deleting file %s\n", strFile.c_str()));
- RTFileDelete(strFile.c_str());
+ ULONG uLogHistoryCount = 3;
+ ComPtr<ISystemProperties> systemProperties;
+ rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam());
+ if (FAILED(rc)) throw rc;
- ++it;
- if (it == task.llFilesToDelete.end())
+ if (!systemProperties.isNull())
{
- task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
- break;
+ rc = systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount);
+ if (FAILED(rc)) throw rc;
}
- task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
- }
+ MachineState_T oldState = mData->mMachineState;
+ setMachineState(MachineState_SettingUp);
+ alock.release();
+ for (size_t i = 0; i < task.llMediums.size(); ++i)
+ {
+ ComObjPtr<Medium> pMedium = (Medium*)(IMedium*)task.llMediums.at(i);
+ {
+ AutoCaller mac(pMedium);
+ if (FAILED(mac.rc())) throw mac.rc();
+ Utf8Str strLocation = pMedium->getLocationFull();
+ rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), strLocation.c_str()).raw(), 1);
+ if (FAILED(rc)) throw rc;
+ LogFunc(("Deleting file %s\n", strLocation.c_str()));
+ }
+ ComPtr<IProgress> pProgress2;
+ rc = pMedium->DeleteStorage(pProgress2.asOutParam());
+ if (FAILED(rc)) throw rc;
+ rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2);
+ if (FAILED(rc)) throw rc;
+ /* Check the result of the asynchrony process. */
+ LONG iRc;
+ rc = pProgress2->COMGETTER(ResultCode)(&iRc);
+ if (FAILED(rc)) throw rc;
+ if (FAILED(iRc))
+ {
+ /* If the thread of the progress object has an error, then
+ * retrieve the error info from there, or it'll be lost. */
+ ProgressErrorInfo info(pProgress2);
+ throw setError(iRc, Utf8Str(info.getText()).c_str());
+ }
+ }
+ setMachineState(oldState);
+ alock.acquire();
- /* delete the settings only when the file actually exists */
- if (mData->pMachineConfigFile->fileExists())
- {
- /* Delete any backup or uncommitted XML files. Ignore failures.
- See the fSafe parameter of xml::XmlFileWriter::write for details. */
- /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
- Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
- RTFileDelete(otherXml.c_str());
- otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
- RTFileDelete(otherXml.c_str());
-
- /* delete the Logs folder, nothing important should be left
- * there (we don't check for errors because the user might have
- * some private files there that we don't want to delete) */
- Utf8Str logFolder;
- getLogFolder(logFolder);
- Assert(logFolder.length());
- if (RTDirExists(logFolder.c_str()))
- {
- /* Delete all VBox.log[.N] files from the Logs folder
- * (this must be in sync with the rotation logic in
- * Console::powerUpThread()). Also, delete the VBox.png[.N]
- * files that may have been created by the GUI. */
- Utf8Str log = Utf8StrFmt("%s%cVBox.log",
- logFolder.c_str(), RTPATH_DELIMITER);
- RTFileDelete(log.c_str());
- log = Utf8StrFmt("%s%cVBox.png",
- logFolder.c_str(), RTPATH_DELIMITER);
- RTFileDelete(log.c_str());
- for (int i = uLogHistoryCount; i > 0; i--)
+ // delete the files pushed on the task list by Machine::Delete()
+ // (this includes saved states of the machine and snapshots and
+ // medium storage files from the IMedium list passed in, and the
+ // machine XML file)
+ std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin();
+ while (it != task.llFilesToDelete.end())
+ {
+ const Utf8Str &strFile = *it;
+ LogFunc(("Deleting file %s\n", strFile.c_str()));
+ int vrc = RTFileDelete(strFile.c_str());
+ if (RT_FAILURE(vrc))
+ throw setError(VBOX_E_IPRT_ERROR,
+ tr("Could not delete file '%s' (%Rrc)"), strFile.c_str(), vrc);
+
+ ++it;
+ if (it == task.llFilesToDelete.end())
{
- log = Utf8StrFmt("%s%cVBox.log.%d",
- logFolder.c_str(), RTPATH_DELIMITER, i);
+ rc = task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")).raw(), 1);
+ if (FAILED(rc)) throw rc;
+ break;
+ }
+
+ rc = task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()).raw(), 1);
+ if (FAILED(rc)) throw rc;
+ }
+
+ /* delete the settings only when the file actually exists */
+ if (mData->pMachineConfigFile->fileExists())
+ {
+ /* Delete any backup or uncommitted XML files. Ignore failures.
+ See the fSafe parameter of xml::XmlFileWriter::write for details. */
+ /** @todo Find a way to avoid referring directly to iprt/xml.h here. */
+ Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff);
+ RTFileDelete(otherXml.c_str());
+ otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff);
+ RTFileDelete(otherXml.c_str());
+
+ /* delete the Logs folder, nothing important should be left
+ * there (we don't check for errors because the user might have
+ * some private files there that we don't want to delete) */
+ Utf8Str logFolder;
+ getLogFolder(logFolder);
+ Assert(logFolder.length());
+ if (RTDirExists(logFolder.c_str()))
+ {
+ /* Delete all VBox.log[.N] files from the Logs folder
+ * (this must be in sync with the rotation logic in
+ * Console::powerUpThread()). Also, delete the VBox.png[.N]
+ * files that may have been created by the GUI. */
+ Utf8Str log = Utf8StrFmt("%s%cVBox.log",
+ logFolder.c_str(), RTPATH_DELIMITER);
RTFileDelete(log.c_str());
- log = Utf8StrFmt("%s%cVBox.png.%d",
- logFolder.c_str(), RTPATH_DELIMITER, i);
+ log = Utf8StrFmt("%s%cVBox.png",
+ logFolder.c_str(), RTPATH_DELIMITER);
RTFileDelete(log.c_str());
+ for (int i = uLogHistoryCount; i > 0; i--)
+ {
+ log = Utf8StrFmt("%s%cVBox.log.%d",
+ logFolder.c_str(), RTPATH_DELIMITER, i);
+ RTFileDelete(log.c_str());
+ log = Utf8StrFmt("%s%cVBox.png.%d",
+ logFolder.c_str(), RTPATH_DELIMITER, i);
+ RTFileDelete(log.c_str());
+ }
+
+ RTDirRemove(logFolder.c_str());
}
- RTDirRemove(logFolder.c_str());
+ /* delete the Snapshots folder, nothing important should be left
+ * there (we don't check for errors because the user might have
+ * some private files there that we don't want to delete) */
+ Utf8Str strFullSnapshotFolder;
+ calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
+ Assert(!strFullSnapshotFolder.isEmpty());
+ if (RTDirExists(strFullSnapshotFolder.c_str()))
+ RTDirRemove(strFullSnapshotFolder.c_str());
+
+ // delete the directory that contains the settings file, but only
+ // if it matches the VM name
+ Utf8Str settingsDir;
+ if (isInOwnDir(&settingsDir))
+ RTDirRemove(settingsDir.c_str());
}
- /* delete the Snapshots folder, nothing important should be left
- * there (we don't check for errors because the user might have
- * some private files there that we don't want to delete) */
- Utf8Str strFullSnapshotFolder;
- calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
- Assert(!strFullSnapshotFolder.isEmpty());
- if (RTDirExists(strFullSnapshotFolder.c_str()))
- RTDirRemove(strFullSnapshotFolder.c_str());
+ alock.release();
- // delete the directory that contains the settings file, but only
- // if it matches the VM name
- Utf8Str settingsDir;
- if (isInOwnDir(&settingsDir))
- RTDirRemove(settingsDir.c_str());
+ rc = mParent->saveRegistries(task.llRegistriesThatNeedSaving);
+ if (FAILED(rc)) throw rc;
}
+ catch (HRESULT aRC) { rc = aRC; }
- alock.release();
-
- mParent->saveRegistries(task.llRegistriesThatNeedSaving);
-
- return S_OK;
+ return rc;
}
STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot)
@@ -5025,30 +5262,22 @@ HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue,
HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue,
IN_BSTR aFlags)
{
- CheckComArgStrNotEmptyOrNull(aName);
- if ((aValue != NULL) && !VALID_PTR(aValue))
- return E_INVALIDARG;
- if ((aFlags != NULL) && !VALID_PTR(aFlags))
- return E_INVALIDARG;
-
HRESULT rc;
- try {
- ComPtr<IInternalSessionControl> directControl =
- mData->mSession.mDirectControl;
+ try
+ {
+ ComPtr<IInternalSessionControl> directControl = mData->mSession.mDirectControl;
BSTR dummy = NULL; /* will not be changed (setter) */
LONG64 dummy64;
if (!directControl)
rc = E_ACCESSDENIED;
else
- rc = directControl->AccessGuestProperty
- (aName,
- /** @todo Fix when adding DeleteGuestProperty(),
- see defect. */
- aValue, aFlags,
- true /* isSetter */,
- &dummy, &dummy64, &dummy);
+ /** @todo Fix when adding DeleteGuestProperty(),
+ see defect. */
+ rc = directControl->AccessGuestProperty(aName, aValue, aFlags,
+ true /* isSetter */,
+ &dummy, &dummy64, &dummy);
}
catch (std::bad_alloc &)
{
@@ -5066,10 +5295,12 @@ STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue,
ReturnComNotImplemented();
#else // VBOX_WITH_GUEST_PROPS
CheckComArgStrNotEmptyOrNull(aName);
- if ((aFlags != NULL) && !VALID_PTR(aFlags))
- return E_INVALIDARG;
+ CheckComArgMaybeNull(aFlags);
+ CheckComArgMaybeNull(aValue);
+
AutoCaller autoCaller(this);
- if (FAILED(autoCaller.rc())) return autoCaller.rc();
+ if (FAILED(autoCaller.rc()))
+ return autoCaller.rc();
HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags);
if (rc == E_ACCESSDENIED)
@@ -5180,9 +5411,7 @@ STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns,
#ifndef VBOX_WITH_GUEST_PROPS
ReturnComNotImplemented();
#else // VBOX_WITH_GUEST_PROPS
- if (!VALID_PTR(aPatterns) && (aPatterns != NULL))
- return E_POINTER;
-
+ CheckComArgMaybeNull(aPatterns);
CheckComArgOutSafeArrayPointerValid(aNames);
CheckComArgOutSafeArrayPointerValid(aValues);
CheckComArgOutSafeArrayPointerValid(aTimestamps);
@@ -5468,7 +5697,7 @@ STDMETHODIMP Machine::QuerySavedGuestSize(ULONG uScreenId, ULONG *puWidth, ULONG
uint32_t u32Width = 0;
uint32_t u32Height = 0;
- int vrc = readSavedGuestSize(mSSData->mStateFilePath, uScreenId, &u32Width, &u32Height);
+ int vrc = readSavedGuestSize(mSSData->strStateFilePath, uScreenId, &u32Width, &u32Height);
if (RT_FAILURE(vrc))
return setError(VBOX_E_IPRT_ERROR,
tr("Saved guest size is not available (%Rrc)"),
@@ -5501,7 +5730,7 @@ STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULO
uint32_t u32Width = 0;
uint32_t u32Height = 0;
- int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
+ int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
if (RT_FAILURE(vrc))
return setError(VBOX_E_IPRT_ERROR,
@@ -5538,7 +5767,7 @@ STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULON
uint32_t u32Width = 0;
uint32_t u32Height = 0;
- int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
+ int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
if (RT_FAILURE(vrc))
return setError(VBOX_E_IPRT_ERROR,
@@ -5601,7 +5830,7 @@ STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidt
uint32_t u32Width = 0;
uint32_t u32Height = 0;
- int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
+ int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
if (RT_FAILURE(vrc))
return setError(VBOX_E_IPRT_ERROR,
@@ -5650,7 +5879,7 @@ STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize,
uint32_t u32Width = 0;
uint32_t u32Height = 0;
- int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
+ int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
if (RT_FAILURE(vrc))
return setError(VBOX_E_IPRT_ERROR,
@@ -5687,7 +5916,7 @@ STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWid
uint32_t u32Width = 0;
uint32_t u32Height = 0;
- int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
+ int vrc = readSavedDisplayScreenshot(mSSData->strStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height);
if (RT_FAILURE(vrc))
return setError(VBOX_E_IPRT_ERROR,
@@ -5879,32 +6108,116 @@ STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeA
}
-STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, IEventContext * /*eventContext*/, BOOL /*tryToUnbind*/)
+/**
+ * Currently this method doesn't attach device to the running VM,
+ * just makes sure it's plugged on next VM start.
+ */
+STDMETHODIMP Machine::AttachHostPciDevice(LONG hostAddress, LONG desiredGuestAddress, BOOL /*tryToUnbind*/)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- ComObjPtr<PciDeviceAttachment> pda;
- char name[32];
+ // lock scope
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ HRESULT rc = checkStateDependency(MutableStateDep);
+ if (FAILED(rc)) return rc;
- pda.createObject();
- RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
- Bstr bname(name);
- pda.createObject();
- pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
+ ChipsetType_T aChipset = ChipsetType_PIIX3;
+ COMGETTER(ChipsetType)(&aChipset);
+
+ if (aChipset != ChipsetType_ICH9)
+ {
+ return setError(E_INVALIDARG,
+ tr("Host PCI attachment only supported with ICH9 chipset"));
+ }
+
+ // check if device with this host PCI address already attached
+ for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
+ it != mHWData->mPciDeviceAssignments.end();
+ ++it)
+ {
+ LONG iHostAddress = -1;
+ ComPtr<PciDeviceAttachment> pAttach;
+ pAttach = *it;
+ pAttach->COMGETTER(HostAddress)(&iHostAddress);
+ if (iHostAddress == hostAddress)
+ return setError(E_INVALIDARG,
+ tr("Device with host PCI address already attached to this VM"));
+ }
+
+ ComObjPtr<PciDeviceAttachment> pda;
+ char name[32];
+
+ RTStrPrintf(name, sizeof(name), "host%02x:%02x.%x", (hostAddress>>8) & 0xff, (hostAddress & 0xf8) >> 3, hostAddress & 7);
+ Bstr bname(name);
+ pda.createObject();
+ pda->init(this, bname, hostAddress, desiredGuestAddress, TRUE);
+ setModified(IsModified_MachineData);
+ mHWData.backup();
+ mHWData->mPciDeviceAssignments.push_back(pda);
+ }
- mPciDeviceAssignments.push_back(pda);
return S_OK;
}
-STDMETHODIMP Machine::DetachHostPciDevice(LONG /*hostAddress*/)
+/**
+ * Currently this method doesn't detach device from the running VM,
+ * just makes sure it's not plugged on next VM start.
+ */
+STDMETHODIMP Machine::DetachHostPciDevice(LONG hostAddress)
{
AutoCaller autoCaller(this);
if (FAILED(autoCaller.rc())) return autoCaller.rc();
- AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
- return E_NOTIMPL;
+ ComObjPtr<PciDeviceAttachment> pAttach;
+ bool fRemoved = false;
+ HRESULT rc;
+
+ // lock scope
+ {
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+
+ rc = checkStateDependency(MutableStateDep);
+ if (FAILED(rc)) return rc;
+
+ for (HWData::PciDeviceAssignmentList::iterator it = mHWData->mPciDeviceAssignments.begin();
+ it != mHWData->mPciDeviceAssignments.end();
+ ++it)
+ {
+ LONG iHostAddress = -1;
+ pAttach = *it;
+ pAttach->COMGETTER(HostAddress)(&iHostAddress);
+ if (iHostAddress != -1 && iHostAddress == hostAddress)
+ {
+ setModified(IsModified_MachineData);
+ mHWData.backup();
+ mHWData->mPciDeviceAssignments.remove(pAttach);
+ fRemoved = true;
+ break;
+ }
+ }
+ }
+
+
+ /* Fire event outside of the lock */
+ if (fRemoved)
+ {
+ Assert(!pAttach.isNull());
+ ComPtr<IEventSource> es;
+ rc = mParent->COMGETTER(EventSource)(es.asOutParam());
+ Assert(SUCCEEDED(rc));
+ Bstr mid;
+ rc = this->COMGETTER(Id)(mid.asOutParam());
+ Assert(SUCCEEDED(rc));
+ fireHostPciDevicePlugEvent(es, mid.raw(), false /* unplugged */, true /* success */, pAttach, NULL);
+ }
+
+ return fRemoved ? S_OK : setError(VBOX_E_OBJECT_NOT_FOUND,
+ tr("No host PCI device %08x attached"),
+ hostAddress
+ );
}
STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDeviceAttachment *, aAssignments))
@@ -5916,7 +6229,7 @@ STDMETHODIMP Machine::COMGETTER(PciDeviceAssignments)(ComSafeArrayOut(IPciDevice
AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
- SafeIfaceArray<IPciDeviceAttachment> assignments(mPciDeviceAssignments);
+ SafeIfaceArray<IPciDeviceAttachment> assignments(mHWData->mPciDeviceAssignments);
assignments.detachTo(ComSafeArrayOutArg(aAssignments));
return S_OK;
@@ -5934,6 +6247,48 @@ STDMETHODIMP Machine::COMGETTER(BandwidthControl)(IBandwidthControl **aBandwidth
return S_OK;
}
+STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress)
+{
+ LogFlowFuncEnter();
+
+ CheckComArgNotNull(pTarget);
+ CheckComArgOutPointerValid(pProgress);
+
+ /** @todo r=klaus disabled as there are apparently still cases which are
+ * not handled correctly */
+ if (mode == CloneMode_MachineAndChildStates)
+ return setError(VBOX_E_NOT_SUPPORTED,
+ tr("The clone mode \"Machine and child states\" is not yet supported. Please try again in the next VirtualBox maintenance update"));
+
+ /* Convert the options. */
+ RTCList<CloneOptions_T> optList;
+ if (options != NULL)
+ optList = com::SafeArray<CloneOptions_T>(ComSafeArrayInArg(options)).toList();
+
+ if (optList.contains(CloneOptions_Link))
+ {
+ if (!isSnapshotMachine())
+ return setError(E_INVALIDARG,
+ tr("Linked clone can only be created from a snapshot"));
+ if (mode != CloneMode_MachineState)
+ return setError(E_INVALIDARG,
+ tr("Linked clone can only be created for a single machine state"));
+ }
+ AssertReturn(!(optList.contains(CloneOptions_KeepAllMACs) && optList.contains(CloneOptions_KeepNATMACs)), E_INVALIDARG);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+
+ MachineCloneVM *pWorker = new MachineCloneVM(this, static_cast<Machine*>(pTarget), mode, optList);
+
+ HRESULT rc = pWorker->start(pProgress);
+
+ LogFlowFuncLeave();
+
+ return rc;
+}
+
// public methods for internal purposes
/////////////////////////////////////////////////////////////////////////////
@@ -5948,6 +6303,18 @@ void Machine::setModified(uint32_t fl)
}
/**
+ * Adds the given IsModified_* flag to the dirty flags of the machine, taking
+ * care of the write locking.
+ *
+ * @param fModifications The flag to add.
+ */
+void Machine::setModifiedLock(uint32_t fModification)
+{
+ AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);
+ mData->flModifications |= fModification;
+}
+
+/**
* Saves the registry entry of this machine to the given configuration node.
*
* @param aEntryNode Node to save the registry entry to.
@@ -6069,6 +6436,44 @@ Utf8Str Machine::queryLogFilename(ULONG idx)
}
/**
+ * Composes a unique saved state filename based on the current system time. The filename is
+ * granular to the second so this will work so long as no more than one snapshot is taken on
+ * a machine per second.
+ *
+ * Before version 4.1, we used this formula for saved state files:
+ * Utf8StrFmt("%s%c{%RTuuid}.sav", strFullSnapshotFolder.c_str(), RTPATH_DELIMITER, mData->mUuid.raw())
+ * which no longer works because saved state files can now be shared between the saved state of the
+ * "saved" machine and an online snapshot, and the following would cause problems:
+ * 1) save machine
+ * 2) create online snapshot from that machine state --> reusing saved state file
+ * 3) save machine again --> filename would be reused, breaking the online snapshot
+ *
+ * So instead we now use a timestamp.
+ *
+ * @param str
+ */
+void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath)
+{
+ AutoCaller autoCaller(this);
+ AssertComRCReturnVoid(autoCaller.rc());
+
+ {
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ calculateFullPath(mUserData->s.strSnapshotFolder, strStateFilePath);
+ }
+
+ RTTIMESPEC ts;
+ RTTimeNow(&ts);
+ RTTIME time;
+ RTTimeExplode(&time, &ts);
+
+ strStateFilePath += RTPATH_DELIMITER;
+ strStateFilePath += Utf8StrFmt("%04d-%02u-%02uT%02u-%02u-%02u-%09uZ.sav",
+ time.i32Year, time.u8Month, time.u8MonthDay,
+ time.u8Hour, time.u8Minute, time.u8Second, time.u32Nanosecond);
+}
+
+/**
* @note Locks this object for writing, calls the client process
* (inside the lock).
*/
@@ -6199,6 +6604,7 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl,
Utf8Str idStr = mData->mUuid.toString();
const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 };
+ fprintf(stderr, "SDL=%s\n", szPath);
vrc = RTProcCreate(szPath, args, env, 0, &pid);
}
#else /* !VBOX_WITH_VBOXSDL */
@@ -7078,6 +7484,8 @@ HRESULT Machine::findSharedFolder(const Utf8Str &aName,
* ensure that the media listed as attachments in the config (which have
* been imported from the OVF) receive the correct registry ID.
*
+ * -- During VM cloning.
+ *
* @param config Machine settings from XML.
* @param puuidRegistry If != NULL, Medium::setRegistryIdIfFirst() gets called with this registry ID for each attached medium in the config.
* @return
@@ -7096,7 +7504,7 @@ HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &
// stateFile (optional)
if (config.strStateFile.isEmpty())
- mSSData->mStateFilePath.setNull();
+ mSSData->strStateFilePath.setNull();
else
{
Utf8Str stateFilePathFull(config.strStateFile);
@@ -7106,13 +7514,18 @@ HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &
tr("Invalid saved state file path '%s' (%Rrc)"),
config.strStateFile.c_str(),
vrc);
- mSSData->mStateFilePath = stateFilePathFull;
+ mSSData->strStateFilePath = stateFilePathFull;
}
// snapshot folder needs special processing so set it again
rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder).raw());
if (FAILED(rc)) return rc;
+ /* Copy the extra data items (Not in any case config is already the same as
+ * mData->pMachineConfigFile, like when the xml files are read from disk. So
+ * make sure the extra data map is copied). */
+ mData->pMachineConfigFile->mapExtraDataItems = config.mapExtraDataItems;
+
/* currentStateModified (optional, default is true) */
mData->mCurrentStateModified = config.fCurrentStateModified;
@@ -7172,13 +7585,12 @@ HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &
/* set the machine state to Aborted or Saved when appropriate */
if (config.fAborted)
{
- Assert(!mSSData->mStateFilePath.isEmpty());
- mSSData->mStateFilePath.setNull();
+ mSSData->strStateFilePath.setNull();
/* no need to use setMachineState() during init() */
mData->mMachineState = MachineState_Aborted;
}
- else if (!mSSData->mStateFilePath.isEmpty())
+ else if (!mSSData->strStateFilePath.isEmpty())
{
/* no need to use setMachineState() during init() */
mData->mMachineState = MachineState_Saved;
@@ -7385,6 +7797,10 @@ HRESULT Machine::loadHardware(const settings::Hardware &data)
rc = mBIOSSettings->loadSettings(data.biosSettings);
if (FAILED(rc)) return rc;
+ // Bandwidth control (must come before network adapters)
+ rc = mBandwidthControl->loadSettings(data.ioSettings);
+ if (FAILED(rc)) return rc;
+
/* USB Controller */
rc = mUSBController->loadSettings(data.usbController);
if (FAILED(rc)) return rc;
@@ -7398,7 +7814,7 @@ HRESULT Machine::loadHardware(const settings::Hardware &data)
/* slot unicity is guaranteed by XML Schema */
AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters));
- rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic);
+ rc = mNetworkAdapters[nic.ulSlot]->loadSettings(mBandwidthControl, nic);
if (FAILED(rc)) return rc;
}
@@ -7451,9 +7867,18 @@ HRESULT Machine::loadHardware(const settings::Hardware &data)
mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled;
mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize;
- // Bandwidth control
- rc = mBandwidthControl->loadSettings(data.ioSettings);
- if (FAILED(rc)) return rc;
+ // Host PCI devices
+ for (settings::HostPciDeviceAttachmentList::const_iterator it = data.pciAttachments.begin();
+ it != data.pciAttachments.end();
+ ++it)
+ {
+ const settings::HostPciDeviceAttachment &hpda = *it;
+ ComObjPtr<PciDeviceAttachment> pda;
+
+ pda.createObject();
+ pda->loadSettings(this, hpda);
+ mHWData->mPciDeviceAssignments.push_back(pda);
+ }
#ifdef VBOX_WITH_GUEST_PROPS
/* Guest properties (optional) */
@@ -7742,7 +8167,10 @@ HRESULT Machine::loadStorageDevices(StorageController *aStorageController,
dev.lPort,
dev.lDevice,
dev.deviceType,
+ false,
dev.fPassThrough,
+ dev.fTempEject,
+ dev.fNonRotational,
pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName());
if (FAILED(rc)) break;
@@ -8030,16 +8458,11 @@ HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings)
*pfNeedsGlobalSaveSettings = true;
}
- /* update the saved state file path */
- Utf8Str path = mSSData->mStateFilePath;
- if (RTPathStartsWith(path.c_str(), configDir.c_str()))
- mSSData->mStateFilePath = Utf8StrFmt("%s%s",
- newConfigDir.c_str(),
- path.c_str() + configDir.length());
+ // in the saved state file path, replace the old directory with the new directory
+ if (RTPathStartsWith(mSSData->strStateFilePath.c_str(), configDir.c_str()))
+ mSSData->strStateFilePath = newConfigDir.append(mSSData->strStateFilePath.c_str() + configDir.length());
- /* Update saved state file paths of all online snapshots.
- * Note that saveSettings() will recognize name change
- * and will save all snapshots in this case. */
+ // and do the same thing for the saved state file paths of all the online snapshots
if (mData->mFirstSnapshot)
mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(),
newConfigDir.c_str());
@@ -8275,17 +8698,17 @@ void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config)
|| ( ( mData->mMachineState == MachineState_DeletingSnapshot
|| mData->mMachineState == MachineState_DeletingSnapshotOnline
|| mData->mMachineState == MachineState_DeletingSnapshotPaused)
- && (!mSSData->mStateFilePath.isEmpty())
+ && (!mSSData->strStateFilePath.isEmpty())
)
)
{
- Assert(!mSSData->mStateFilePath.isEmpty());
+ Assert(!mSSData->strStateFilePath.isEmpty());
/* try to make the file name relative to the settings file dir */
- copyPathRelativeToMachine(mSSData->mStateFilePath, config.strStateFile);
+ copyPathRelativeToMachine(mSSData->strStateFilePath, config.strStateFile);
}
else
{
- Assert(mSSData->mStateFilePath.isEmpty());
+ Assert(mSSData->strStateFilePath.isEmpty() || mData->mMachineState == MachineState_Saving);
config.strStateFile.setNull();
}
@@ -8382,7 +8805,7 @@ HRESULT Machine::saveHardware(settings::Hardware &data)
/* The hardware version attribute (optional).
Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */
if ( mHWData->mHWVersion == "1"
- && mSSData->mStateFilePath.isEmpty()
+ && mSSData->strStateFilePath.isEmpty()
)
mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */
@@ -8550,6 +8973,21 @@ HRESULT Machine::saveHardware(settings::Hardware &data)
rc = mBandwidthControl->saveSettings(data.ioSettings);
if (FAILED(rc)) throw rc;
+ /* Host PCI devices */
+ for (HWData::PciDeviceAssignmentList::const_iterator it = mHWData->mPciDeviceAssignments.begin();
+ it != mHWData->mPciDeviceAssignments.end();
+ ++it)
+ {
+ ComObjPtr<PciDeviceAttachment> pda = *it;
+ settings::HostPciDeviceAttachment hpda;
+
+ rc = pda->saveSettings(hpda);
+ if (FAILED(rc)) throw rc;
+
+ data.pciAttachments.push_back(hpda);
+ }
+
+
// guest properties
data.llGuestProperties.clear();
#ifdef VBOX_WITH_GUEST_PROPS
@@ -8679,6 +9117,8 @@ HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageControl
else
dev.uuid = pMedium->getId();
dev.fPassThrough = pAttach->getPassthrough();
+ dev.fTempEject = pAttach->getTempEject();
+ dev.fNonRotational = pAttach->getNonRotational();
}
dev.strBwGroup = pAttach->getBandwidthGroup();
@@ -8720,9 +9160,9 @@ HRESULT Machine::saveStateSettings(int aFlags)
if (aFlags & SaveSTS_StateFilePath)
{
- if (!mSSData->mStateFilePath.isEmpty())
+ if (!mSSData->strStateFilePath.isEmpty())
/* try to make the file name relative to the settings file dir */
- copyPathRelativeToMachine(mSSData->mStateFilePath, mData->pMachineConfigFile->strStateFile);
+ copyPathRelativeToMachine(mSSData->strStateFilePath, mData->pMachineConfigFile->strStateFile);
else
mData->pMachineConfigFile->strStateFile.setNull();
}
@@ -8730,7 +9170,7 @@ HRESULT Machine::saveStateSettings(int aFlags)
if (aFlags & SaveSTS_StateTimeStamp)
{
Assert( mData->mMachineState != MachineState_Aborted
- || mSSData->mStateFilePath.isEmpty());
+ || mSSData->strStateFilePath.isEmpty());
mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;
@@ -8780,7 +9220,7 @@ void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium,
if (pMedium->addRegistry(uuid, false /* fRecurse */))
// registry actually changed:
- mParent->addGuidToListUniquely(llRegistriesThatNeedSaving, uuid);
+ VirtualBox::addGuidToListUniquely(llRegistriesThatNeedSaving, uuid);
if (puuid)
*puuid = uuid;
@@ -9000,6 +9440,9 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
pAtt->getDevice(),
DeviceType_HardDisk,
true /* aImplicit */,
+ false /* aPassthrough */,
+ false /* aTempEject */,
+ false /* aNonRotational */,
pAtt->getBandwidthGroup());
if (FAILED(rc)) throw rc;
@@ -9031,9 +9474,9 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress,
/**
* Deletes implicit differencing hard disks created either by
- * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.
+ * #createImplicitDiffs() or by #AttachDevice() and rolls back mMediaData.
*
- * Note that to delete hard disks created by #AttachMedium() this method is
+ * Note that to delete hard disks created by #AttachDevice() this method is
* called from #fixupMedia() when the changes are rolled back.
*
* @param pllRegistriesThatNeedSaving Optional pointer to a list of UUIDs to receive the registry IDs that need saving
@@ -9329,12 +9772,41 @@ HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,
if (!pMedium.isNull())
{
+ AutoCaller mac(pMedium);
+ if (FAILED(mac.rc())) return mac.rc();
+ AutoReadLock lock(pMedium COMMA_LOCKVAL_SRC_POS);
DeviceType_T devType = pMedium->getDeviceType();
if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly
&& devType == DeviceType_HardDisk)
|| (cleanupMode == CleanupMode_Full)
)
+ {
llMedia.push_back(pMedium);
+ ComObjPtr<Medium> pParent = pMedium->getParent();
+ /*
+ * Search for medias which are not attached to any machine, but
+ * in the chain to an attached disk. Mediums are only consided
+ * if they are:
+ * - have only one child
+ * - no references to any machines
+ * - are of normal medium type
+ */
+ while (!pParent.isNull())
+ {
+ AutoCaller mac1(pParent);
+ if (FAILED(mac1.rc())) return mac1.rc();
+ AutoReadLock lock1(pParent COMMA_LOCKVAL_SRC_POS);
+ if (pParent->getChildren().size() == 1)
+ {
+ if ( pParent->getMachineBackRefCount() == 0
+ && pParent->getType() == MediumType_Normal
+ && find(llMedia.begin(), llMedia.end(), pParent) == llMedia.end())
+ llMedia.push_back(pParent);
+ }else
+ break;
+ pParent = pParent->getParent();
+ }
+ }
}
// real machine: then we need to use the proper method
@@ -9508,10 +9980,11 @@ void Machine::commitMedia(bool aOnline /*= false*/)
* This is necessary because the stored parent will point to the
* session machine otherwise and cause crashes or errors later
* when the session machine gets invalid.
- *
- * @todo: Change the MediumAttachment class to behave like any other class
- * in this regard by creating peer MediumAttachment objects for
- * session machines and share the data with the peer machine.
+ */
+ /** @todo Change the MediumAttachment class to behave like any other
+ * class in this regard by creating peer MediumAttachment
+ * objects for session machines and share the data with the peer
+ * machine.
*/
for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();
it != mMediaData->mAttachments.end();
@@ -9537,6 +10010,8 @@ void Machine::commitMedia(bool aOnline /*= false*/)
* by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.
*
* @note Locks this object for writing!
+ *
+ * @todo r=dj this needs a pllRegistriesThatNeedSaving as well
*/
void Machine::rollbackMedia()
{
@@ -9945,6 +10420,31 @@ void Machine::copyFrom(Machine *aThat)
mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);
}
+/**
+ * Returns whether the given storage controller is hotplug capable.
+ *
+ * @returns true if the controller supports hotplugging
+ * false otherwise.
+ * @param enmCtrlType The controller type to check for.
+ */
+bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType)
+{
+ switch (enmCtrlType)
+ {
+ case StorageControllerType_IntelAhci:
+ return true;
+ case StorageControllerType_LsiLogic:
+ case StorageControllerType_LsiLogicSas:
+ case StorageControllerType_BusLogic:
+ case StorageControllerType_PIIX3:
+ case StorageControllerType_PIIX4:
+ case StorageControllerType_ICH6:
+ case StorageControllerType_I82078:
+ default:
+ return false;
+ }
+}
+
#ifdef VBOX_WITH_RESOURCE_USAGE_API
void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)
@@ -9992,8 +10492,11 @@ void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachin
new pm::AggregateMax()));
- /* Guest metrics */
- mGuestHAL = new pm::CollectorGuestHAL(this, hal);
+ /* Guest metrics collector */
+ mCollectorGuest = new pm::CollectorGuest(aMachine, pid);
+ aCollector->registerGuest(mCollectorGuest);
+ LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
+ this, __PRETTY_FUNCTION__, mCollectorGuest));
/* Create sub metrics */
pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",
@@ -10013,10 +10516,13 @@ void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachin
pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");
/* Create and register base metrics */
- pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mGuestHAL, aMachine, guestLoadUser, guestLoadKernel, guestLoadIdle);
+ pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine,
+ guestLoadUser, guestLoadKernel, guestLoadIdle);
aCollector->registerBaseMetric(guestCpuLoad);
- pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mGuestHAL, aMachine, guestMemTotal, guestMemFree, guestMemBalloon, guestMemShared,
+ pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mCollectorGuest, aMachine,
+ guestMemTotal, guestMemFree,
+ guestMemBalloon, guestMemShared,
guestMemCache, guestPagedTotal);
aCollector->registerBaseMetric(guestCpuMem);
@@ -10075,12 +10581,6 @@ void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMach
aCollector->unregisterMetricsFor(aMachine);
aCollector->unregisterBaseMetricsFor(aMachine);
}
-
- if (mGuestHAL)
- {
- delete mGuestHAL;
- mGuestHAL = NULL;
- }
}
#endif /* VBOX_WITH_RESOURCE_USAGE_API */
@@ -10104,7 +10604,7 @@ HRESULT SessionMachine::FinalConstruct()
# error "Port me!"
#endif
- return S_OK;
+ return BaseFinalConstruct();
}
void SessionMachine::FinalRelease()
@@ -10112,6 +10612,8 @@ void SessionMachine::FinalRelease()
LogFlowThisFunc(("\n"));
uninit(Uninit::Unexpected);
+
+ BaseFinalRelease();
}
/**
@@ -10388,10 +10890,27 @@ void SessionMachine::uninit(Uninit::Reason aReason)
// and others need mParent lock, and USB needs host lock.
AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);
+ LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n",
+ this, __PRETTY_FUNCTION__, mCollectorGuest));
+ if (mCollectorGuest)
+ {
+ mParent->performanceCollector()->unregisterGuest(mCollectorGuest);
+ // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered()
+ mCollectorGuest = NULL;
+ }
+#if 0
// Trigger async cleanup tasks, avoid doing things here which are not
// vital to be done immediately and maybe need more locks. This calls
// Machine::unregisterMetrics().
mParent->onMachineUninit(mPeer);
+#else
+ /*
+ * It is safe to call Machine::unregisterMetrics() here because
+ * PerformanceCollector::samplerCallback no longer accesses guest methods
+ * holding the lock.
+ */
+ unregisterMetrics(mParent->performanceCollector(), mPeer);
+#endif
if (aReason == Uninit::Abnormal)
{
@@ -10410,8 +10929,9 @@ void SessionMachine::uninit(Uninit::Reason aReason)
rollback(false /* aNotify */);
}
- Assert(mConsoleTaskData.mStateFilePath.isEmpty() || !mConsoleTaskData.mSnapshot);
- if (!mConsoleTaskData.mStateFilePath.isEmpty())
+ Assert( mConsoleTaskData.strStateFilePath.isEmpty()
+ || !mConsoleTaskData.mSnapshot);
+ if (!mConsoleTaskData.strStateFilePath.isEmpty())
{
LogWarningThisFunc(("canceling failed save state request!\n"));
endSavingState(E_FAIL, tr("Machine terminated with pending save state!"));
@@ -10423,11 +10943,13 @@ void SessionMachine::uninit(Uninit::Reason aReason)
/* delete all differencing hard disks created (this will also attach
* their parents back by rolling back mMediaData) */
rollbackMedia();
- /* delete the saved state file (it might have been already created) */
- if (mConsoleTaskData.mSnapshot->stateFilePath().length())
- RTFileDelete(mConsoleTaskData.mSnapshot->stateFilePath().c_str());
+ // delete the saved state file (it might have been already created)
+ // AFTER killing the snapshot so that releaseSavedStateFile() won't
+ // think it's still in use
+ Utf8Str strStateFile = mConsoleTaskData.mSnapshot->getStateFilePath();
mConsoleTaskData.mSnapshot->uninit();
+ releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ );
}
if (!mData->mSession.mType.isEmpty())
@@ -11009,7 +11531,7 @@ STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStat
AssertReturn( mData->mMachineState == MachineState_Paused
&& mConsoleTaskData.mLastState == MachineState_Null
- && mConsoleTaskData.mStateFilePath.isEmpty(),
+ && mConsoleTaskData.strStateFilePath.isEmpty(),
E_FAIL);
/* create a progress object to track operation completion */
@@ -11020,27 +11542,20 @@ STDMETHODIMP SessionMachine::BeginSavingState(IProgress **aProgress, BSTR *aStat
Bstr(tr("Saving the execution state of the virtual machine")).raw(),
FALSE /* aCancelable */);
- Bstr stateFilePath;
+ Utf8Str strStateFilePath;
/* stateFilePath is null when the machine is not running */
if (mData->mMachineState == MachineState_Paused)
- {
- Utf8Str strFullSnapshotFolder;
- calculateFullPath(mUserData->s.strSnapshotFolder, strFullSnapshotFolder);
- stateFilePath = Utf8StrFmt("%s%c{%RTuuid}.sav",
- strFullSnapshotFolder.c_str(),
- RTPATH_DELIMITER,
- mData->mUuid.raw());
- }
+ composeSavedStateFilename(strStateFilePath);
/* fill in the console task data */
mConsoleTaskData.mLastState = mData->mMachineState;
- mConsoleTaskData.mStateFilePath = stateFilePath;
+ mConsoleTaskData.strStateFilePath = strStateFilePath;
mConsoleTaskData.mProgress = pProgress;
/* set the state to Saving (this is expected by Console::SaveState()) */
setMachineState(MachineState_Saving);
- stateFilePath.cloneTo(aStateFilePath);
+ strStateFilePath.cloneTo(aStateFilePath);
pProgress.queryInterfaceTo(aProgress);
return S_OK;
@@ -11062,7 +11577,7 @@ STDMETHODIMP SessionMachine::EndSavingState(LONG iResult, IN_BSTR aErrMsg)
AssertReturn( ( (SUCCEEDED(iResult) && mData->mMachineState == MachineState_Saved)
|| (FAILED(iResult) && mData->mMachineState == MachineState_Saving))
&& mConsoleTaskData.mLastState != MachineState_Null
- && !mConsoleTaskData.mStateFilePath.isEmpty(),
+ && !mConsoleTaskData.strStateFilePath.isEmpty(),
E_FAIL);
/*
@@ -11104,7 +11619,7 @@ STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)
aSavedStateFile,
vrc);
- mSSData->mStateFilePath = stateFilePathFull;
+ mSSData->strStateFilePath = stateFilePathFull;
/* The below setMachineState() will detect the state transition and will
* update the settings file */
@@ -11177,8 +11692,8 @@ STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
using namespace guestProp;
CheckComArgStrNotEmptyOrNull(aName);
- if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))
- return E_POINTER; /* aValue can be NULL to indicate deletion */
+ CheckComArgMaybeNull(aValue);
+ CheckComArgMaybeNull(aFlags);
try
{
@@ -11280,6 +11795,85 @@ STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,
#endif
}
+STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment,
+ IMediumAttachment **aNewAttachment)
+{
+ CheckComArgNotNull(aAttachment);
+ CheckComArgOutPointerValid(aNewAttachment);
+
+ AutoCaller autoCaller(this);
+ if (FAILED(autoCaller.rc())) return autoCaller.rc();
+
+ // request the host lock first, since might be calling Host methods for getting host drives;
+ // next, protect the media tree all the while we're in here, as well as our member variables
+ AutoMultiWriteLock3 multiLock(mParent->host()->lockHandle(),
+ this->lockHandle(),
+ &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS);
+
+ ComObjPtr<MediumAttachment> pAttach = static_cast<MediumAttachment *>(aAttachment);
+
+ Bstr ctrlName;
+ LONG lPort;
+ LONG lDevice;
+ bool fTempEject;
+ {
+ AutoCaller autoAttachCaller(this);
+ if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
+
+ AutoReadLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
+
+ /* Need to query the details first, as the IMediumAttachment reference
+ * might be to the original settings, which we are going to change. */
+ ctrlName = pAttach->getControllerName();
+ lPort = pAttach->getPort();
+ lDevice = pAttach->getDevice();
+ fTempEject = pAttach->getTempEject();
+ }
+
+ if (!fTempEject)
+ {
+ /* Remember previously mounted medium. The medium before taking the
+ * backup is not necessarily the same thing. */
+ ComObjPtr<Medium> oldmedium;
+ oldmedium = pAttach->getMedium();
+
+ setModified(IsModified_Storage);
+ mMediaData.backup();
+
+ // The backup operation makes the pAttach reference point to the
+ // old settings. Re-get the correct reference.
+ pAttach = findAttachment(mMediaData->mAttachments,
+ ctrlName.raw(),
+ lPort,
+ lDevice);
+
+ {
+ AutoCaller autoAttachCaller(this);
+ if (FAILED(autoAttachCaller.rc())) return autoAttachCaller.rc();
+
+ AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
+ if (!oldmedium.isNull())
+ oldmedium->removeBackReference(mData->mUuid);
+
+ pAttach->updateMedium(NULL);
+ pAttach->updateEjected();
+ }
+
+ setModified(IsModified_Storage);
+ }
+ else
+ {
+ {
+ AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS);
+ pAttach->updateEjected();
+ }
+ }
+
+ pAttach.queryInterfaceTo(aNewAttachment);
+
+ return S_OK;
+}
+
// public methods only for internal purposes
/////////////////////////////////////////////////////////////////////////////
@@ -11648,6 +12242,29 @@ HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup)
}
/**
+ * @note Locks this object for reading.
+ */
+HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove)
+{
+ LogFlowThisFunc(("\n"));
+
+ AutoCaller autoCaller(this);
+ AssertComRCReturn(autoCaller.rc(), autoCaller.rc());
+
+ ComPtr<IInternalSessionControl> directControl;
+ {
+ AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);
+ directControl = mData->mSession.mDirectControl;
+ }
+
+ /* ignore notifications sent after #OnSessionEnd() is called */
+ if (!directControl)
+ return S_OK;
+
+ return directControl->OnStorageDeviceChange(aAttachment, aRemove);
+}
+
+/**
* Returns @c true if this machine's USB controller reports it has a matching
* filter for the given USB device and @c false otherwise.
*
@@ -11773,7 +12390,7 @@ HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
if (SUCCEEDED(aRc))
{
- mSSData->mStateFilePath = mConsoleTaskData.mStateFilePath;
+ mSSData->strStateFilePath = mConsoleTaskData.strStateFilePath;
/* save all VM settings */
rc = saveSettings(NULL);
@@ -11782,8 +12399,10 @@ HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
}
else
{
- /* delete the saved state file (it might have been already created) */
- RTFileDelete(mConsoleTaskData.mStateFilePath.c_str());
+ // delete the saved state file (it might have been already created);
+ // we need not check whether this is shared with a snapshot here because
+ // we certainly created this saved state file here anew
+ RTFileDelete(mConsoleTaskData.strStateFilePath.c_str());
}
/* notify the progress object about operation completion */
@@ -11803,7 +12422,7 @@ HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
/* clear out the temporary saved state data */
mConsoleTaskData.mLastState = MachineState_Null;
- mConsoleTaskData.mStateFilePath.setNull();
+ mConsoleTaskData.strStateFilePath.setNull();
mConsoleTaskData.mProgress.setNull();
LogFlowThisFuncLeave();
@@ -11811,6 +12430,33 @@ HRESULT SessionMachine::endSavingState(HRESULT aRc, const Utf8Str &aErrMsg)
}
/**
+ * Deletes the given file if it is no longer in use by either the current machine state
+ * (if the machine is "saved") or any of the machine's snapshots.
+ *
+ * Note: This checks mSSData->strStateFilePath, which is shared by the Machine and SessionMachine
+ * but is different for each SnapshotMachine. When calling this, the order of calling this
+ * function on the one hand and changing that variable OR the snapshots tree on the other hand
+ * is therefore critical. I know, it's all rather messy.
+ *
+ * @param strStateFile
+ * @param pSnapshotToIgnore Passed to Snapshot::sharesSavedStateFile(); this snapshot is ignored in the test for whether the saved state file is in use.
+ */
+void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile,
+ Snapshot *pSnapshotToIgnore)
+{
+ // it is safe to delete this saved state file if it is not currently in use by the machine ...
+ if ( (strStateFile.isNotEmpty())
+ && (strStateFile != mSSData->strStateFilePath) // session machine's saved state
+ )
+ // ... and it must also not be shared with other snapshots
+ if ( !mData->mFirstSnapshot
+ || !mData->mFirstSnapshot->sharesSavedStateFile(strStateFile, pSnapshotToIgnore)
+ // this checks the SnapshotMachine's state file paths
+ )
+ RTFileDelete(strStateFile.c_str());
+}
+
+/**
* Locks the attached media.
*
* All attached hard disks are locked for writing and DVD/floppy are locked for
@@ -12062,10 +12708,17 @@ HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
{
if (mRemoveSavedState)
{
- Assert(!mSSData->mStateFilePath.isEmpty());
- RTFileDelete(mSSData->mStateFilePath.c_str());
+ Assert(!mSSData->strStateFilePath.isEmpty());
+
+ // it is safe to delete the saved state file if ...
+ if ( !mData->mFirstSnapshot // ... we have no snapshots or
+ || !mData->mFirstSnapshot->sharesSavedStateFile(mSSData->strStateFilePath, NULL /* pSnapshotToIgnore */)
+ // ... none of the snapshots share the saved state file
+ )
+ RTFileDelete(mSSData->strStateFilePath.c_str());
}
- mSSData->mStateFilePath.setNull();
+
+ mSSData->strStateFilePath.setNull();
stsFlags |= SaveSTS_StateFilePath;
}
@@ -12089,7 +12742,7 @@ HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)
&& aMachineState == MachineState_Saved)
{
/* the saved state file was adopted */
- Assert(!mSSData->mStateFilePath.isEmpty());
+ Assert(!mSSData->strStateFilePath.isEmpty());
stsFlags |= SaveSTS_StateFilePath;
}