diff options
Diffstat (limited to 'src/VBox/Main/src-server')
81 files changed, 14122 insertions, 4590 deletions
diff --git a/src/VBox/Main/src-server/ApplianceImpl.cpp b/src/VBox/Main/src-server/ApplianceImpl.cpp index 05fc3b67..8d1e6014 100644 --- a/src/VBox/Main/src-server/ApplianceImpl.cpp +++ b/src/VBox/Main/src-server/ApplianceImpl.cpp @@ -1,11 +1,10 @@ /* $Id: ApplianceImpl.cpp $ */ /** @file - * * IAppliance and IVirtualSystem COM class implementations. */ /* - * Copyright (C) 2008-2012 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -18,8 +17,8 @@ #include <iprt/path.h> #include <iprt/cpp/utils.h> - #include <VBox/com/array.h> +#include <map> #include "ApplianceImpl.h" #include "VFSExplorerImpl.h" @@ -28,7 +27,8 @@ #include "Global.h" #include "ProgressImpl.h" #include "MachineImpl.h" - +#include "MediumFormatImpl.h" +#include "SystemPropertiesImpl.h" #include "AutoCaller.h" #include "Logging.h" @@ -42,6 +42,21 @@ using namespace std; // //////////////////////////////////////////////////////////////////////////////// +static const char* const strISOURI = "http://www.ecma-international.org/publications/standards/Ecma-119.htm"; +static const char* const strVMDKStreamURI = "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized"; +static const char* const strVMDKSparseURI = "http://www.vmware.com/specifications/vmdk.html#sparse"; +static const char* const strVMDKCompressedURI = "http://www.vmware.com/specifications/vmdk.html#compressed"; +static const char* const strVMDKCompressedURI2 = "http://www.vmware.com/interfaces/specifications/vmdk.html#compressed"; +static const char* const strVHDURI = "http://go.microsoft.com/fwlink/?LinkId=137171"; + +static std::map<Utf8Str, Utf8Str> supportedStandardsURI; + +static const char* const applianceIOTarName = "Appliance::IOTar"; +static const char* const applianceIOShaName = "Appliance::IOSha"; +static const char* const applianceIOFileName = "Appliance::IOFile"; + +static std::map<APPLIANCEIONAME, Utf8Str> applianceIONameMap; + static const struct { ovf::CIMOSType_T cim; @@ -83,6 +98,11 @@ g_osTypes[] = { ovf::CIMOSType_CIMOS_FreeBSD_64, VBOXOSTYPE_FreeBSD_x64 }, { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS }, { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS_x64 }, // there is no CIM 64-bit type for this + { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS106 }, + { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS106_x64 }, + { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS107_x64 }, + { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS108_x64 }, + { ovf::CIMOSType_CIMOS_MACOS, VBOXOSTYPE_MacOS109_x64 }, // Linuxes { ovf::CIMOSType_CIMOS_RedHatEnterpriseLinux, VBOXOSTYPE_RedHat }, @@ -238,24 +258,44 @@ void convertCIMOSType2VBoxOSType(Utf8Str &strType, ovf::CIMOSType_T c, const Utf } } - strType = Global::OSTypeId(VBOXOSTYPE_Unknown); + if (c == ovf::CIMOSType_CIMOS_Other_64) + strType = Global::OSTypeId(VBOXOSTYPE_Unknown_x64); + else + strType = Global::OSTypeId(VBOXOSTYPE_Unknown); } /** * Private helper func that suggests a VirtualBox guest OS type * for the given OVF operating system type. - * @param osTypeVBox - * @param c + * @returns CIM OS type. + * @param pcszVBox Our guest OS type identifier string. + * @param fLongMode Whether long mode is enabled and a 64-bit CIM type is + * preferred even if the VBox guest type isn't 64-bit. */ -ovf::CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVbox) +ovf::CIMOSType_T convertVBoxOSType2CIMOSType(const char *pcszVBox, BOOL fLongMode) { for (size_t i = 0; i < RT_ELEMENTS(g_osTypes); ++i) { - if (!RTStrICmp(pcszVbox, Global::OSTypeId(g_osTypes[i].osType))) + if (!RTStrICmp(pcszVBox, Global::OSTypeId(g_osTypes[i].osType))) + { + if (fLongMode && !(g_osTypes[i].osType & VBOXOSTYPE_x64)) + { + VBOXOSTYPE enmDesiredOsType = (VBOXOSTYPE)((int)g_osTypes[i].osType | (int)VBOXOSTYPE_x64); + size_t j = i; + while (++j < RT_ELEMENTS(g_osTypes)) + if (g_osTypes[j].osType == enmDesiredOsType) + return g_osTypes[j].cim; + j = i; + while (--j > 0) + if (g_osTypes[j].osType == enmDesiredOsType) + return g_osTypes[j].cim; + /* Not all OSes have 64-bit versions, so just return the 32-bit variant. */ + } return g_osTypes[i].cim; + } } - return ovf::CIMOSType_CIMOS_Other; + return fLongMode ? ovf::CIMOSType_CIMOS_Other_64 : ovf::CIMOSType_CIMOS_Other; } Utf8Str convertNetworkAttachmentTypeToString(NetworkAttachmentType_T type) @@ -268,6 +308,7 @@ Utf8Str convertNetworkAttachmentTypeToString(NetworkAttachmentType_T type) case NetworkAttachmentType_Internal: strType = "Internal"; break; case NetworkAttachmentType_HostOnly: strType = "HostOnly"; break; case NetworkAttachmentType_Generic: strType = "Generic"; break; + case NetworkAttachmentType_NATNetwork: strType = "NATNetwork"; break; case NetworkAttachmentType_Null: strType = "Null"; break; } return strType; @@ -324,6 +365,7 @@ Appliance::~Appliance() */ HRESULT Appliance::init(VirtualBox *aVirtualBox) { + HRESULT rc = S_OK; /* Enclose the state transition NotReady->InInit->Ready */ AutoInitSpan autoInitSpan(this); AssertReturn(autoInitSpan.isOk(), E_FAIL); @@ -334,10 +376,14 @@ HRESULT Appliance::init(VirtualBox *aVirtualBox) // initialize data m = new Data; + initApplianceIONameMap(); + + rc = initSetOfSupportedStandardsURI(); + /* Confirm a successful initialization */ autoInitSpan.setSucceeded(); - return S_OK; + return rc; } /** @@ -573,6 +619,113 @@ STDMETHODIMP Appliance::GetWarnings(ComSafeArrayOut(BSTR, aWarnings)) // //////////////////////////////////////////////////////////////////////////////// +HRESULT Appliance::initSetOfSupportedStandardsURI() +{ + HRESULT rc = S_OK; + if (!supportedStandardsURI.empty()) + return rc; + + /* Get the system properties. */ + SystemProperties *pSysProps = mVirtualBox->getSystemProperties(); + { + ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension("iso"); + if (trgFormat.isNull()) + return setError(E_FAIL, tr("Can't find appropriate medium format for ISO type of a virtual disk.")); + + Bstr bstrFormatName; + rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); + if (FAILED(rc)) return rc; + + Utf8Str strTrgFormat = Utf8Str(bstrFormatName); + + supportedStandardsURI.insert(std::make_pair(Utf8Str(strISOURI), strTrgFormat)); + } + + { + ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension("vmdk"); + if (trgFormat.isNull()) + return setError(E_FAIL, tr("Can't find appropriate medium format for VMDK type of a virtual disk.")); + + Bstr bstrFormatName; + rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); + if (FAILED(rc)) return rc; + + Utf8Str strTrgFormat = Utf8Str(bstrFormatName); + + supportedStandardsURI.insert(std::make_pair(Utf8Str(strVMDKStreamURI), strTrgFormat)); + supportedStandardsURI.insert(std::make_pair(Utf8Str(strVMDKSparseURI), strTrgFormat)); + supportedStandardsURI.insert(std::make_pair(Utf8Str(strVMDKCompressedURI), strTrgFormat)); + supportedStandardsURI.insert(std::make_pair(Utf8Str(strVMDKCompressedURI2), strTrgFormat)); + } + + { + ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension("vhd"); + if (trgFormat.isNull()) + return setError(E_FAIL, tr("Can't find appropriate medium format for VHD type of a virtual disk.")); + + Bstr bstrFormatName; + rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); + if (FAILED(rc)) return rc; + + Utf8Str strTrgFormat = Utf8Str(bstrFormatName); + + supportedStandardsURI.insert(std::make_pair(Utf8Str(strVHDURI), strTrgFormat)); + } + + return rc; +} + +Utf8Str Appliance::typeOfVirtualDiskFormatFromURI(Utf8Str uri) const +{ + Utf8Str type; + std::map<Utf8Str, Utf8Str>::const_iterator cit = supportedStandardsURI.find(uri); + if (cit != supportedStandardsURI.end()) + { + type = cit->second; + } + + return type; +} + +std::set<Utf8Str> Appliance::URIFromTypeOfVirtualDiskFormat(Utf8Str type) +{ + std::set<Utf8Str> uri; + std::map<Utf8Str, Utf8Str>::const_iterator cit = supportedStandardsURI.begin(); + while(cit != supportedStandardsURI.end()) + { + if (cit->second.compare(type,Utf8Str::CaseInsensitive) == 0) + uri.insert(cit->first); + ++cit; + } + + return uri; +} + +HRESULT Appliance::initApplianceIONameMap() +{ + HRESULT rc = S_OK; + if (!applianceIONameMap.empty()) + return rc; + + applianceIONameMap.insert(std::make_pair(applianceIOTar, applianceIOTarName)); + applianceIONameMap.insert(std::make_pair(applianceIOFile, applianceIOFileName)); + applianceIONameMap.insert(std::make_pair(applianceIOSha, applianceIOShaName)); + + return rc; +} + +Utf8Str Appliance::applianceIOName(APPLIANCEIONAME type) const +{ + Utf8Str name; + std::map<APPLIANCEIONAME, Utf8Str>::const_iterator cit = applianceIONameMap.find(type); + if (cit != applianceIONameMap.end()) + { + name = cit->second; + } + + return name; +} + /** * Returns true if the appliance is in "idle" state. This should always be the * case unless an import or export is currently in progress. Similar to machine @@ -626,7 +779,7 @@ HRESULT Appliance::searchUniqueDiskImageFilePath(Utf8Str& aName) const * already */ /** @todo: Maybe too cost-intensive; try to find a lighter way */ while ( RTPathExists(tmpName) - || mVirtualBox->OpenMedium(Bstr(tmpName).raw(), DeviceType_HardDisk, AccessMode_ReadWrite, FALSE /* fForceNewUuid */, &harddisk) != VBOX_E_OBJECT_NOT_FOUND + || mVirtualBox->OpenMedium(Bstr(tmpName).raw(), DeviceType_HardDisk, AccessMode_ReadWrite, FALSE /* fForceNewUuid */, &harddisk) != VBOX_E_OBJECT_NOT_FOUND ) { RTStrFree(tmpName); @@ -783,7 +936,7 @@ void Appliance::waitForAsyncProgress(ComObjPtr<Progress> &pProgressThis, that in the meantime more than one async operation was finished. So we have to loop as long as we reached the same operation count. */ ULONG curOp; - for(;;) + for (;;) { rc = pProgressAsync->COMGETTER(Operation(&curOp)); if (FAILED(rc)) throw rc; @@ -866,6 +1019,16 @@ void Appliance::disksWeight() m->ulTotalDisksMB += pHD->ulSizeMB; ++m->cDisks; } + + avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM); + for (itH = avsdeHDs.begin(); + itH != avsdeHDs.end(); + ++itH) + { + const VirtualSystemDescriptionEntry *pHD = *itH; + m->ulTotalDisksMB += pHD->ulSizeMB; + ++m->cDisks; + } } } @@ -1109,13 +1272,13 @@ STDMETHODIMP VirtualSystemDescription::COMGETTER(Count)(ULONG *aCount) STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes), ComSafeArrayOut(BSTR, aRefs), ComSafeArrayOut(BSTR, aOrigValues), - ComSafeArrayOut(BSTR, aVboxValues), + ComSafeArrayOut(BSTR, aVBoxValues), ComSafeArrayOut(BSTR, aExtraConfigValues)) { if (ComSafeArrayOutIsNull(aTypes) || ComSafeArrayOutIsNull(aRefs) || ComSafeArrayOutIsNull(aOrigValues) || - ComSafeArrayOutIsNull(aVboxValues) || + ComSafeArrayOutIsNull(aVBoxValues) || ComSafeArrayOutIsNull(aExtraConfigValues)) return E_POINTER; @@ -1128,7 +1291,7 @@ STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSys com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c); com::SafeArray<BSTR> sfaRefs(c); com::SafeArray<BSTR> sfaOrigValues(c); - com::SafeArray<BSTR> sfaVboxValues(c); + com::SafeArray<BSTR> sfaVBoxValues(c); com::SafeArray<BSTR> sfaExtraConfigValues(c); list<VirtualSystemDescriptionEntry>::const_iterator it; @@ -1147,8 +1310,8 @@ STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSys bstr = vsde.strOvf; bstr.cloneTo(&sfaOrigValues[i]); - bstr = vsde.strVboxCurrent; - bstr.cloneTo(&sfaVboxValues[i]); + bstr = vsde.strVBoxCurrent; + bstr.cloneTo(&sfaVBoxValues[i]); bstr = vsde.strExtraConfigCurrent; bstr.cloneTo(&sfaExtraConfigValues[i]); @@ -1157,7 +1320,7 @@ STDMETHODIMP VirtualSystemDescription::GetDescription(ComSafeArrayOut(VirtualSys sfaTypes.detachTo(ComSafeArrayOutArg(aTypes)); sfaRefs.detachTo(ComSafeArrayOutArg(aRefs)); sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues)); - sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues)); + sfaVBoxValues.detachTo(ComSafeArrayOutArg(aVBoxValues)); sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues)); return S_OK; @@ -1171,13 +1334,13 @@ STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescrip ComSafeArrayOut(VirtualSystemDescriptionType_T, aTypes), ComSafeArrayOut(BSTR, aRefs), ComSafeArrayOut(BSTR, aOrigValues), - ComSafeArrayOut(BSTR, aVboxValues), + ComSafeArrayOut(BSTR, aVBoxValues), ComSafeArrayOut(BSTR, aExtraConfigValues)) { if (ComSafeArrayOutIsNull(aTypes) || ComSafeArrayOutIsNull(aRefs) || ComSafeArrayOutIsNull(aOrigValues) || - ComSafeArrayOutIsNull(aVboxValues) || + ComSafeArrayOutIsNull(aVBoxValues) || ComSafeArrayOutIsNull(aExtraConfigValues)) return E_POINTER; @@ -1191,7 +1354,7 @@ STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescrip com::SafeArray<VirtualSystemDescriptionType_T> sfaTypes(c); com::SafeArray<BSTR> sfaRefs(c); com::SafeArray<BSTR> sfaOrigValues(c); - com::SafeArray<BSTR> sfaVboxValues(c); + com::SafeArray<BSTR> sfaVBoxValues(c); com::SafeArray<BSTR> sfaExtraConfigValues(c); list<VirtualSystemDescriptionEntry*>::const_iterator it; @@ -1210,8 +1373,8 @@ STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescrip bstr = vsde->strOvf; bstr.cloneTo(&sfaOrigValues[i]); - bstr = vsde->strVboxCurrent; - bstr.cloneTo(&sfaVboxValues[i]); + bstr = vsde->strVBoxCurrent; + bstr.cloneTo(&sfaVBoxValues[i]); bstr = vsde->strExtraConfigCurrent; bstr.cloneTo(&sfaExtraConfigValues[i]); @@ -1220,7 +1383,7 @@ STDMETHODIMP VirtualSystemDescription::GetDescriptionByType(VirtualSystemDescrip sfaTypes.detachTo(ComSafeArrayOutArg(aTypes)); sfaRefs.detachTo(ComSafeArrayOutArg(aRefs)); sfaOrigValues.detachTo(ComSafeArrayOutArg(aOrigValues)); - sfaVboxValues.detachTo(ComSafeArrayOutArg(aVboxValues)); + sfaVBoxValues.detachTo(ComSafeArrayOutArg(aVBoxValues)); sfaExtraConfigValues.detachTo(ComSafeArrayOutArg(aExtraConfigValues)); return S_OK; @@ -1258,7 +1421,7 @@ STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionT { case VirtualSystemDescriptionValueType_Reference: bstr = vsde->strRef; break; case VirtualSystemDescriptionValueType_Original: bstr = vsde->strOvf; break; - case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVboxCurrent; break; + case VirtualSystemDescriptionValueType_Auto: bstr = vsde->strVBoxCurrent; break; case VirtualSystemDescriptionValueType_ExtraConfig: bstr = vsde->strExtraConfigCurrent; break; } @@ -1275,7 +1438,7 @@ STDMETHODIMP VirtualSystemDescription::GetValuesByType(VirtualSystemDescriptionT * @return */ STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnabled), - ComSafeArrayIn(IN_BSTR, argVboxValues), + ComSafeArrayIn(IN_BSTR, argVBoxValues), ComSafeArrayIn(IN_BSTR, argExtraConfigValues)) { #ifndef RT_OS_WINDOWS @@ -1283,7 +1446,7 @@ STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnab #endif /* RT_OS_WINDOWS */ CheckComArgSafeArrayNotNull(aEnabled); - CheckComArgSafeArrayNotNull(argVboxValues); + CheckComArgSafeArrayNotNull(argVBoxValues); CheckComArgSafeArrayNotNull(argExtraConfigValues); AutoCaller autoCaller(this); @@ -1292,11 +1455,11 @@ STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnab AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); com::SafeArray<BOOL> sfaEnabled(ComSafeArrayInArg(aEnabled)); - com::SafeArray<IN_BSTR> sfaVboxValues(ComSafeArrayInArg(argVboxValues)); + com::SafeArray<IN_BSTR> sfaVBoxValues(ComSafeArrayInArg(argVBoxValues)); com::SafeArray<IN_BSTR> sfaExtraConfigValues(ComSafeArrayInArg(argExtraConfigValues)); if ( (sfaEnabled.size() != m->llDescriptions.size()) - || (sfaVboxValues.size() != m->llDescriptions.size()) + || (sfaVBoxValues.size() != m->llDescriptions.size()) || (sfaExtraConfigValues.size() != m->llDescriptions.size()) ) return E_INVALIDARG; @@ -1311,7 +1474,7 @@ STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnab if (sfaEnabled[i]) { - vsde.strVboxCurrent = sfaVboxValues[i]; + vsde.strVBoxCurrent = sfaVBoxValues[i]; vsde.strExtraConfigCurrent = sfaExtraConfigValues[i]; } else @@ -1326,7 +1489,7 @@ STDMETHODIMP VirtualSystemDescription::SetFinalValues(ComSafeArrayIn(BOOL, aEnab * @return */ STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionType_T aType, - IN_BSTR aVboxValue, + IN_BSTR aVBoxValue, IN_BSTR aExtraConfigValue) { AutoCaller autoCaller(this); @@ -1334,7 +1497,7 @@ STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionTy AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - addEntry(aType, "", aVboxValue, aVboxValue, 0, aExtraConfigValue); + addEntry(aType, "", aVBoxValue, aVBoxValue, 0, aExtraConfigValue); return S_OK; } @@ -1351,7 +1514,7 @@ STDMETHODIMP VirtualSystemDescription::AddDescription(VirtualSystemDescriptionTy void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType, const Utf8Str &strRef, const Utf8Str &aOvfValue, - const Utf8Str &aVboxValue, + const Utf8Str &aVBoxValue, uint32_t ulSizeMB, const Utf8Str &strExtraConfig /*= ""*/) { @@ -1360,15 +1523,17 @@ void VirtualSystemDescription::addEntry(VirtualSystemDescriptionType_T aType, vsde.type = aType; vsde.strRef = strRef; vsde.strOvf = aOvfValue; - vsde.strVboxSuggested // remember original value - = vsde.strVboxCurrent // and set current value which can be overridden by setFinalValues() - = aVboxValue; + vsde.strVBoxSuggested // remember original value + = vsde.strVBoxCurrent // and set current value which can be overridden by setFinalValues() + = aVBoxValue; vsde.strExtraConfigSuggested = vsde.strExtraConfigCurrent = strExtraConfig; vsde.ulSizeMB = ulSizeMB; + vsde.skipIt = false; m->llDescriptions.push_back(vsde); + } /** @@ -1394,6 +1559,26 @@ std::list<VirtualSystemDescriptionEntry*> VirtualSystemDescription::findByType(V } /** + * Private method; delete all records from the list + * m->llDescriptions that match the given type. + * @param aType + * @return + */ +void VirtualSystemDescription::removeByType(VirtualSystemDescriptionType_T aType) +{ + std::list<VirtualSystemDescriptionEntry*> vsd; + + list<VirtualSystemDescriptionEntry>::iterator it = m->llDescriptions.begin(); + while (it != m->llDescriptions.end()) + { + if (it->type == aType) + it = m->llDescriptions.erase(it); + else + ++it; + } +} + +/** * Private method; looks thru the member hardware items for the IDE, SATA, or SCSI controller with * the given reference ID. Useful when needing the controller for a particular * virtual disk. @@ -1437,7 +1622,7 @@ const VirtualSystemDescriptionEntry* VirtualSystemDescription::findControllerFro * @param elmMachine <vbox:Machine> element with attributes and subelements from some * DOM tree. */ -void VirtualSystemDescription::importVboxMachineXML(const xml::ElementNode &elmMachine) +void VirtualSystemDescription::importVBoxMachineXML(const xml::ElementNode &elmMachine) { settings::MachineConfigFile *pConfig = NULL; @@ -1459,7 +1644,7 @@ void VirtualSystemDescription::importVboxMachineXML(const xml::ElementNode &elmM } /** - * Returns the machine config created by importVboxMachineXML() or NULL if there's none. + * Returns the machine config created by importVBoxMachineXML() or NULL if there's none. * @return */ const settings::MachineConfigFile* VirtualSystemDescription::getMachineConfig() const diff --git a/src/VBox/Main/src-server/ApplianceImplExport.cpp b/src/VBox/Main/src-server/ApplianceImplExport.cpp index cc47b4d1..c8c5718f 100644 --- a/src/VBox/Main/src-server/ApplianceImplExport.cpp +++ b/src/VBox/Main/src-server/ApplianceImplExport.cpp @@ -1,11 +1,10 @@ /* $Id: ApplianceImplExport.cpp $ */ /** @file - * * IAppliance and IVirtualSystem COM class implementations. */ /* - * Copyright (C) 2008-2011 Oracle Corporation + * Copyright (C) 2008-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -57,7 +56,7 @@ using namespace std; * @param appliance * @return */ -STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualSystemDescription **aDescription) +STDMETHODIMP Machine::ExportTo(IAppliance *aAppliance, IN_BSTR location, IVirtualSystemDescription **aDescription) { HRESULT rc = S_OK; @@ -86,32 +85,32 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS // store the machine object so we can dump the XML in Appliance::Write() pNewDesc->m->pMachine = this; - // now fill it with description items - Bstr bstrName1; - Bstr bstrDescription; - Bstr bstrGuestOSType; - uint32_t cCPUs; - uint32_t ulMemSizeMB; - BOOL fUSBEnabled; - BOOL fAudioEnabled; - AudioControllerType_T audioController; + // first, call the COM methods, as they request locks + BOOL fUSBEnabled = FALSE; + com::SafeIfaceArray<IUSBController> usbControllers; + rc = COMGETTER(USBControllers)(ComSafeArrayAsOutParam(usbControllers)); + if (SUCCEEDED(rc)) + { + for (unsigned i = 0; i < usbControllers.size(); i++) + { + USBControllerType_T enmType; - ComPtr<IUSBController> pUsbController; - ComPtr<IAudioAdapter> pAudioAdapter; + rc = usbControllers[i]->COMGETTER(Type)(&enmType); + if (FAILED(rc)) throw rc; - // first, call the COM methods, as they request locks - rc = COMGETTER(USBController)(pUsbController.asOutParam()); - if (FAILED(rc)) - fUSBEnabled = false; - else - rc = pUsbController->COMGETTER(Enabled)(&fUSBEnabled); + if (enmType == USBControllerType_OHCI) + fUSBEnabled = TRUE; + } + } // request the machine lock while accessing internal members AutoReadLock alock1(this COMMA_LOCKVAL_SRC_POS); - pAudioAdapter = mAudioAdapter; + ComPtr<IAudioAdapter> pAudioAdapter = mAudioAdapter; + BOOL fAudioEnabled; rc = pAudioAdapter->COMGETTER(Enabled)(&fAudioEnabled); if (FAILED(rc)) throw rc; + AudioControllerType_T audioController; rc = pAudioAdapter->COMGETTER(AudioController)(&audioController); if (FAILED(rc)) throw rc; @@ -122,9 +121,9 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS // get guest OS Utf8Str strOsTypeVBox = mUserData->s.strOsType; // CPU count - cCPUs = mHWData->mCPUCount; + uint32_t cCPUs = mHWData->mCPUCount; // memory size in MB - ulMemSizeMB = mHWData->mMemorySize; + uint32_t ulMemSizeMB = mHWData->mMemorySize; // VRAM size? // BIOS settings? // 3D acceleration enabled? @@ -132,11 +131,16 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS // nested paging enabled? // HWVirtExVPIDEnabled? // PAEEnabled? + // Long mode enabled? + BOOL fLongMode; + rc = GetCPUProperty(CPUPropertyType_LongMode, &fLongMode); + if (FAILED(rc)) throw rc; + // snapshotFolder? // VRDPServer? /* Guest OS type */ - ovf::CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str()); + ovf::CIMOSType_T cim = convertVBoxOSType2CIMOSType(strOsTypeVBox.c_str(), fLongMode); pNewDesc->addEntry(VirtualSystemDescriptionType_OS, "", Utf8StrFmt("%RI32", cim), @@ -206,41 +210,41 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS // <const name="HardDiskControllerIDE" value="6" /> if (!pIDEController.isNull()) { - Utf8Str strVbox; + Utf8Str strVBox; StorageControllerType_T ctlr; rc = pIDEController->COMGETTER(ControllerType)(&ctlr); if (FAILED(rc)) throw rc; switch(ctlr) { - case StorageControllerType_PIIX3: strVbox = "PIIX3"; break; - case StorageControllerType_PIIX4: strVbox = "PIIX4"; break; - case StorageControllerType_ICH6: strVbox = "ICH6"; break; + case StorageControllerType_PIIX3: strVBox = "PIIX3"; break; + case StorageControllerType_PIIX4: strVBox = "PIIX4"; break; + case StorageControllerType_ICH6: strVBox = "ICH6"; break; } - if (strVbox.length()) + if (strVBox.length()) { lIDEControllerPrimaryIndex = (int32_t)pNewDesc->m->llDescriptions.size(); pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE, Utf8StrFmt("%d", lIDEControllerPrimaryIndex), // strRef - strVbox, // aOvfValue - strVbox); // aVboxValue + strVBox, // aOvfValue + strVBox); // aVBoxValue lIDEControllerSecondaryIndex = lIDEControllerPrimaryIndex + 1; pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE, Utf8StrFmt("%d", lIDEControllerSecondaryIndex), - strVbox, - strVbox); + strVBox, + strVBox); } } // <const name="HardDiskControllerSATA" value="7" /> if (!pSATAController.isNull()) { - Utf8Str strVbox = "AHCI"; + Utf8Str strVBox = "AHCI"; lSATAControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size(); pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSATA, Utf8StrFmt("%d", lSATAControllerIndex), - strVbox, - strVbox); + strVBox, + strVBox); } // <const name="HardDiskControllerSCSI" value="8" /> @@ -250,17 +254,17 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS rc = pSCSIController->COMGETTER(ControllerType)(&ctlr); if (SUCCEEDED(rc)) { - Utf8Str strVbox = "LsiLogic"; // the default in VBox + Utf8Str strVBox = "LsiLogic"; // the default in VBox switch(ctlr) { - case StorageControllerType_LsiLogic: strVbox = "LsiLogic"; break; - case StorageControllerType_BusLogic: strVbox = "BusLogic"; break; + case StorageControllerType_LsiLogic: strVBox = "LsiLogic"; break; + case StorageControllerType_BusLogic: strVBox = "BusLogic"; break; } lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size(); pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSCSI, Utf8StrFmt("%d", lSCSIControllerIndex), - strVbox, - strVbox); + strVBox, + strVBox); } else throw rc; @@ -270,12 +274,12 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS { // VirtualBox considers the SAS controller a class of its own but in OVF // it should be a SCSI controller - Utf8Str strVbox = "LsiLogicSas"; + Utf8Str strVBox = "LsiLogicSas"; lSCSIControllerIndex = (int32_t)pNewDesc->m->llDescriptions.size(); pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerSAS, Utf8StrFmt("%d", lSCSIControllerIndex), - strVbox, - strVbox); + strVBox, + strVBox); } // <const name="HardDiskImage" value="9" /> @@ -320,15 +324,15 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS rc = pHDA->COMGETTER(Device)(&lDevice); if (FAILED(rc)) throw rc; - Utf8Str strTargetVmdkName; + Utf8Str strTargetImageName; Utf8Str strLocation; LONG64 llSize = 0; - if ( deviceType == DeviceType_HardDisk - && pMedium - ) + if ( deviceType == DeviceType_HardDisk + && pMedium) { Bstr bstrLocation; + rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam()); if (FAILED(rc)) throw rc; strLocation = bstrLocation; @@ -343,12 +347,11 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS // returns pMedium if there are no diff images if (FAILED(rc)) throw rc; - Bstr bstrBaseName; - rc = pBaseMedium->COMGETTER(Name)(bstrBaseName.asOutParam()); - if (FAILED(rc)) throw rc; - - Utf8Str strTargetName = Utf8Str(locInfo.strPath).stripPath().stripExt(); - strTargetVmdkName = Utf8StrFmt("%s-disk%d.vmdk", strTargetName.c_str(), ++pAppliance->m->cDisks); + Utf8Str strName = Utf8Str(locInfo.strPath).stripPath().stripExt(); + strTargetImageName = Utf8StrFmt("%s-disk%d.vmdk", strName.c_str(), ++pAppliance->m->cDisks); + if (strTargetImageName.length() > RTTAR_NAME_MAX) + throw setError(VBOX_E_NOT_SUPPORTED, + tr("Cannot attach disk '%s' -- file name too long"), strTargetImageName.c_str()); // force reading state, or else size will be returned as 0 MediumState_T ms; @@ -358,7 +361,55 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS rc = pBaseMedium->COMGETTER(Size)(&llSize); if (FAILED(rc)) throw rc; } + else if ( deviceType == DeviceType_DVD + && pMedium) + { + /* + * check the minimal rules to grant access to export an image + * 1. no host drive CD/DVD image + * 2. the image must be accessible and readable + * 3. only ISO image is exported + */ + + //1. no host drive CD/DVD image + BOOL fHostDrive = false; + rc = pMedium->COMGETTER(HostDrive)(&fHostDrive); + if (FAILED(rc)) throw rc; + + if(fHostDrive) + continue; + + //2. the image must be accessible and readable + MediumState_T ms; + rc = pMedium->RefreshState(&ms); + if (FAILED(rc)) throw rc; + + if (ms != MediumState_Created) + continue; + //3. only ISO image is exported + Bstr bstrLocation; + rc = pMedium->COMGETTER(Location)(bstrLocation.asOutParam()); + if (FAILED(rc)) throw rc; + + strLocation = bstrLocation; + + Utf8Str ext = strLocation; + ext.assignEx(RTPathExt(ext.c_str()));//returns extension with dot (".iso") + + int eq = ext.compare(".iso", Utf8Str::CaseInsensitive); + if (eq != 0) + continue; + + Utf8Str strName = Utf8Str(locInfo.strPath).stripPath().stripExt(); + strTargetImageName = Utf8StrFmt("%s-disk%d.iso", strName.c_str(), ++pAppliance->m->cDisks); + if (strTargetImageName.length() > RTTAR_NAME_MAX) + throw setError(VBOX_E_NOT_SUPPORTED, + tr("Cannot attach image '%s' -- file name too long"), strTargetImageName.c_str()); + + rc = pMedium->COMGETTER(Size)(&llSize); + if (FAILED(rc)) throw rc; + } // and how this translates to the virtual system int32_t lControllerVsys = 0; LONG lChannelVsys; @@ -426,19 +477,20 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS case DeviceType_HardDisk: Log(("Adding VirtualSystemDescriptionType_HardDiskImage, disk size: %RI64\n", llSize)); pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage, - strTargetVmdkName, // disk ID: let's use the name - strTargetVmdkName, // OVF value: + strTargetImageName, // disk ID: let's use the name + strTargetImageName, // OVF value: strLocation, // vbox value: media path (uint32_t)(llSize / _1M), strExtra); break; case DeviceType_DVD: + Log(("Adding VirtualSystemDescriptionType_CDROM, disk size: %RI64\n", llSize)); pNewDesc->addEntry(VirtualSystemDescriptionType_CDROM, - strEmpty, // disk ID - strEmpty, // OVF value - strEmpty, // vbox value - 1, // ulSize + strTargetImageName, // disk ID + strTargetImageName, // OVF value + strLocation, // vbox value + (uint32_t)(llSize / _1M),// ulSize strExtra); break; @@ -525,11 +577,12 @@ STDMETHODIMP Machine::Export(IAppliance *aAppliance, IN_BSTR location, IVirtualS /** * Public method implementation. * @param format + * @param options * @param path * @param aProgress * @return */ -STDMETHODIMP Appliance::Write(IN_BSTR format, BOOL fManifest, IN_BSTR path, IProgress **aProgress) +STDMETHODIMP Appliance::Write(IN_BSTR format, ComSafeArrayIn(ImportOptions_T, options), IN_BSTR path, IProgress **aProgress) { if (!path) return E_POINTER; CheckComArgOutPointerValid(aProgress); @@ -539,6 +592,31 @@ STDMETHODIMP Appliance::Write(IN_BSTR format, BOOL fManifest, IN_BSTR path, IPro AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + if (options != NULL) + m->optListExport = com::SafeArray<ExportOptions_T>(ComSafeArrayInArg(options)).toList(); + +// AssertReturn(!(m->optListExport.contains(ExportOptions_CreateManifest) && m->optListExport.contains(ExportOptions_ExportDVDImages)), E_INVALIDARG); + + m->fExportISOImages = m->optListExport.contains(ExportOptions_ExportDVDImages); + + if (!m->fExportISOImages)/* remove all ISO images from VirtualSystemDescription */ + { + list< ComObjPtr<VirtualSystemDescription> >::const_iterator it; + for (it = m->virtualSystemDescriptions.begin(); + it != m->virtualSystemDescriptions.end(); + ++it) + { + ComObjPtr<VirtualSystemDescription> vsdescThis = (*it); + std::list<VirtualSystemDescriptionEntry*> skipped = vsdescThis->findByType(VirtualSystemDescriptionType_CDROM); + std::list<VirtualSystemDescriptionEntry*>:: iterator pItSkipped = skipped.begin(); + while (pItSkipped != skipped.end()) + { + (*pItSkipped)->skipIt = true; + ++pItSkipped; + } + } + } + // do not allow entering this method if the appliance is busy reading or writing if (!isApplianceIdle()) return E_ACCESSDENIED; @@ -550,21 +628,28 @@ STDMETHODIMP Appliance::Write(IN_BSTR format, BOOL fManifest, IN_BSTR path, IPro return setError(VBOX_E_FILE_ERROR, tr("Appliance file must have .ovf or .ova extension")); - m->fManifest = !!fManifest; + m->fManifest = m->optListExport.contains(ExportOptions_CreateManifest); Utf8Str strFormat(format); - OVFFormat ovfF; + + ovf::OVFVersion_T ovfF; if (strFormat == "ovf-0.9") - ovfF = OVF_0_9; + { + ovfF = ovf::OVFVersion_0_9; + } else if (strFormat == "ovf-1.0") - ovfF = OVF_1_0; + { + ovfF = ovf::OVFVersion_1_0; + } else if (strFormat == "ovf-2.0") - ovfF = OVF_2_0; + { + ovfF = ovf::OVFVersion_2_0; + } else return setError(VBOX_E_FILE_ERROR, tr("Invalid format \"%s\" specified"), strFormat.c_str()); /* as of OVF 2.0 we have to use SHA256 */ - m->fSha256 = ovfF >= OVF_2_0; + m->fSha256 = ovfF >= ovf::OVFVersion_2_0; ComObjPtr<Progress> progress; HRESULT rc = S_OK; @@ -614,7 +699,7 @@ STDMETHODIMP Appliance::Write(IN_BSTR format, BOOL fManifest, IN_BSTR path, IPro * @param aProgress * @return */ -HRESULT Appliance::writeImpl(OVFFormat aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) +HRESULT Appliance::writeImpl(ovf::OVFVersion_T aFormat, const LocationInfo &aLocInfo, ComObjPtr<Progress> &aProgress) { HRESULT rc = S_OK; try @@ -658,18 +743,30 @@ void Appliance::buildXML(AutoWriteLockBase& writeLock, xml::Document &doc, XMLStack &stack, const Utf8Str &strPath, - OVFFormat enFormat) + ovf::OVFVersion_T enFormat) { xml::ElementNode *pelmRoot = doc.createRootElement("Envelope"); - pelmRoot->setAttribute("ovf:version", enFormat == OVF_2_0 ? "2.0" - : enFormat == OVF_1_0 ? "1.0" + pelmRoot->setAttribute("ovf:version", enFormat == ovf::OVFVersion_2_0 ? "2.0" + : enFormat == ovf::OVFVersion_1_0 ? "1.0" : "0.9"); pelmRoot->setAttribute("xml:lang", "en-US"); - Utf8Str strNamespace = (enFormat == OVF_0_9) - ? "http://www.vmware.com/schema/ovf/1/envelope" // 0.9 - : "http://schemas.dmtf.org/ovf/envelope/1"; // 1.0 + Utf8Str strNamespace; + + if (enFormat == ovf::OVFVersion_0_9) + { + strNamespace = ovf::OVF09_URI_string; + } + else if (enFormat == ovf::OVFVersion_1_0) + { + strNamespace = ovf::OVF10_URI_string; + } + else + { + strNamespace = ovf::OVF20_URI_string; + } + pelmRoot->setAttribute("xmlns", strNamespace); pelmRoot->setAttribute("xmlns:ovf", strNamespace); @@ -680,6 +777,14 @@ void Appliance::buildXML(AutoWriteLockBase& writeLock, pelmRoot->setAttribute("xmlns:vbox", "http://www.virtualbox.org/ovf/machine"); // pelmRoot->setAttribute("xsi:schemaLocation", "http://schemas.dmtf.org/ovf/envelope/1 ../ovf-envelope.xsd"); + if (enFormat == ovf::OVFVersion_2_0) + { + pelmRoot->setAttribute("xmlns:epasd", + "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_EthernetPortAllocationSettingData.xsd"); + pelmRoot->setAttribute("xmlns:sasd", + "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_StorageAllocationSettingData.xsd"); + } + // <Envelope>/<References> xml::ElementNode *pelmReferences = pelmRoot->createChild("References"); // 0.9 and 1.0 @@ -689,7 +794,7 @@ void Appliance::buildXML(AutoWriteLockBase& writeLock, <Disk ovf:capacity="4294967296" ovf:diskId="lamp" ovf:format="..." ovf:populatedSize="1924967692"/> </DiskSection> */ xml::ElementNode *pelmDiskSection; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) { // <Section xsi:type="ovf:DiskSection_Type"> pelmDiskSection = pelmRoot->createChild("Section"); @@ -709,7 +814,7 @@ void Appliance::buildXML(AutoWriteLockBase& writeLock, </Network> </NetworkSection> */ xml::ElementNode *pelmNetworkSection; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) { // <Section xsi:type="ovf:NetworkSection_Type"> pelmNetworkSection = pelmRoot->createChild("Section"); @@ -729,7 +834,7 @@ void Appliance::buildXML(AutoWriteLockBase& writeLock, xml::ElementNode *pelmToAddVirtualSystemsTo; if (m->virtualSystemDescriptions.size() > 1) { - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) throw setError(VBOX_E_FILE_ERROR, tr("Cannot export more than one virtual system with OVF 0.9, use OVF 1.0")); @@ -783,19 +888,46 @@ void Appliance::buildXML(AutoWriteLockBase& writeLock, const VirtualSystemDescriptionEntry *pDiskEntry = itS->second; // source path: where the VBox image is - const Utf8Str &strSrcFilePath = pDiskEntry->strVboxCurrent; + const Utf8Str &strSrcFilePath = pDiskEntry->strVBoxCurrent; Bstr bstrSrcFilePath(strSrcFilePath); + //skip empty Medium. There are no information to add into section <References> or <DiskSection> + if (strSrcFilePath.isEmpty() || + pDiskEntry->skipIt == true) + continue; + // Do NOT check here whether the file exists. FindMedium will figure // that out, and filesystem-based tests are simply wrong in the // general case (think of iSCSI). // We need some info from the source disks ComPtr<IMedium> pSourceDisk; + //DeviceType_T deviceType = DeviceType_HardDisk;// by default Log(("Finding source disk \"%ls\"\n", bstrSrcFilePath.raw())); - HRESULT rc = mVirtualBox->OpenMedium(bstrSrcFilePath.raw(), DeviceType_HardDisk, AccessMode_ReadWrite, FALSE /* fForceNewUuid */, pSourceDisk.asOutParam()); - if (FAILED(rc)) throw rc; + + HRESULT rc; + + if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage) + { + rc = mVirtualBox->OpenMedium(bstrSrcFilePath.raw(), + DeviceType_HardDisk, + AccessMode_ReadWrite, + FALSE /* fForceNewUuid */, + pSourceDisk.asOutParam()); + if (FAILED(rc)) + throw rc; + } + else if (pDiskEntry->type == VirtualSystemDescriptionType_CDROM)//may be, this is CD/DVD + { + rc = mVirtualBox->OpenMedium(bstrSrcFilePath.raw(), + DeviceType_DVD, + AccessMode_ReadOnly, + FALSE, + pSourceDisk.asOutParam()); + if (FAILED(rc)) + throw rc; + } Bstr uuidSource; rc = pSourceDisk->COMGETTER(Id)(uuidSource.asOutParam()); @@ -811,7 +943,7 @@ void Appliance::buildXML(AutoWriteLockBase& writeLock, strTargetFilePath.append(strTargetFileNameOnly); // We are always exporting to VMDK stream optimized for now - Bstr bstrSrcFormat = L"VMDK"; + //Bstr bstrSrcFormat = L"VMDK";//not used diskList.push_back(strTargetFilePath); @@ -843,12 +975,22 @@ void Appliance::buildXML(AutoWriteLockBase& writeLock, pelmDisk->setAttribute("ovf:capacity", Utf8StrFmt("%RI64", cbCapacity).c_str()); pelmDisk->setAttribute("ovf:diskId", strDiskID); pelmDisk->setAttribute("ovf:fileRef", strFileRef); - pelmDisk->setAttribute("ovf:format", - (enFormat == OVF_0_9) - ? "http://www.vmware.com/specifications/vmdk.html#sparse" // must be sparse or ovftool chokes - : "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" - // correct string as communicated to us by VMware (public bug #6612) - ); + + if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage)//deviceType == DeviceType_HardDisk + { + pelmDisk->setAttribute("ovf:format", + (enFormat == ovf::OVFVersion_0_9) + ? "http://www.vmware.com/specifications/vmdk.html#sparse" // must be sparse or ovftool ch + : "http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized" + // correct string as communicated to us by VMware (public bug #6612) + ); + } + else //pDiskEntry->type == VirtualSystemDescriptionType_CDROM, deviceType == DeviceType_DVD + { + pelmDisk->setAttribute("ovf:format", + "http://www.ecma-international.org/publications/standards/Ecma-119.htm" + ); + } // add the UUID of the newly target image to the OVF disk element, but in the // vbox: namespace since it's not part of the standard @@ -891,13 +1033,13 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, xml::ElementNode &elmToAddVirtualSystemsTo, std::list<xml::ElementNode*> *pllElementsWithUuidAttributes, ComObjPtr<VirtualSystemDescription> &vsdescThis, - OVFFormat enFormat, + ovf::OVFVersion_T enFormat, XMLStack &stack) { LogFlowFunc(("ENTER appliance %p\n", this)); xml::ElementNode *pelmVirtualSystem; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) { // <Section xsi:type="ovf:NetworkSection_Type"> pelmVirtualSystem = elmToAddVirtualSystemsTo.createChild("Content"); @@ -909,10 +1051,9 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, /*xml::ElementNode *pelmVirtualSystemInfo =*/ pelmVirtualSystem->createChild("Info")->addContent("A virtual machine"); std::list<VirtualSystemDescriptionEntry*> llName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); - if (llName.size() != 1) - throw setError(VBOX_E_NOT_SUPPORTED, - tr("Missing VM name")); - Utf8Str &strVMName = llName.front()->strVboxCurrent; + if (!llName.size()) + throw setError(VBOX_E_NOT_SUPPORTED, tr("Missing VM name")); + Utf8Str &strVMName = llName.back()->strVBoxCurrent; pelmVirtualSystem->setAttribute("ovf:id", strVMName); // product info @@ -921,11 +1062,11 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, std::list<VirtualSystemDescriptionEntry*> llVendor = vsdescThis->findByType(VirtualSystemDescriptionType_Vendor); std::list<VirtualSystemDescriptionEntry*> llVendorUrl = vsdescThis->findByType(VirtualSystemDescriptionType_VendorUrl); std::list<VirtualSystemDescriptionEntry*> llVersion = vsdescThis->findByType(VirtualSystemDescriptionType_Version); - bool fProduct = llProduct.size() && !llProduct.front()->strVboxCurrent.isEmpty(); - bool fProductUrl = llProductUrl.size() && !llProductUrl.front()->strVboxCurrent.isEmpty(); - bool fVendor = llVendor.size() && !llVendor.front()->strVboxCurrent.isEmpty(); - bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.front()->strVboxCurrent.isEmpty(); - bool fVersion = llVersion.size() && !llVersion.front()->strVboxCurrent.isEmpty(); + bool fProduct = llProduct.size() && !llProduct.back()->strVBoxCurrent.isEmpty(); + bool fProductUrl = llProductUrl.size() && !llProductUrl.back()->strVBoxCurrent.isEmpty(); + bool fVendor = llVendor.size() && !llVendor.back()->strVBoxCurrent.isEmpty(); + bool fVendorUrl = llVendorUrl.size() && !llVendorUrl.back()->strVBoxCurrent.isEmpty(); + bool fVersion = llVersion.size() && !llVersion.back()->strVBoxCurrent.isEmpty(); if (fProduct || fProductUrl || fVersion || @@ -941,7 +1082,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, <VendorUrl>http://www.sun.com</VendorUrl> </Section> */ xml::ElementNode *pelmAnnotationSection; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) { // <Section ovf:required="false" xsi:type="ovf:ProductSection_Type"> pelmAnnotationSection = pelmVirtualSystem->createChild("Section"); @@ -952,28 +1093,28 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, pelmAnnotationSection->createChild("Info")->addContent("Meta-information about the installed software"); if (fProduct) - pelmAnnotationSection->createChild("Product")->addContent(llProduct.front()->strVboxCurrent); + pelmAnnotationSection->createChild("Product")->addContent(llProduct.back()->strVBoxCurrent); if (fVendor) - pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.front()->strVboxCurrent); + pelmAnnotationSection->createChild("Vendor")->addContent(llVendor.back()->strVBoxCurrent); if (fVersion) - pelmAnnotationSection->createChild("Version")->addContent(llVersion.front()->strVboxCurrent); + pelmAnnotationSection->createChild("Version")->addContent(llVersion.back()->strVBoxCurrent); if (fProductUrl) - pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.front()->strVboxCurrent); + pelmAnnotationSection->createChild("ProductUrl")->addContent(llProductUrl.back()->strVBoxCurrent); if (fVendorUrl) - pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.front()->strVboxCurrent); + pelmAnnotationSection->createChild("VendorUrl")->addContent(llVendorUrl.back()->strVBoxCurrent); } // description std::list<VirtualSystemDescriptionEntry*> llDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description); if (llDescription.size() && - !llDescription.front()->strVboxCurrent.isEmpty()) + !llDescription.back()->strVBoxCurrent.isEmpty()) { /* <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type"> <Info>A human-readable annotation</Info> <Annotation>Plan 9</Annotation> </Section> */ xml::ElementNode *pelmAnnotationSection; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) { // <Section ovf:required="false" xsi:type="ovf:AnnotationSection_Type"> pelmAnnotationSection = pelmVirtualSystem->createChild("Section"); @@ -983,20 +1124,20 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, pelmAnnotationSection = pelmVirtualSystem->createChild("AnnotationSection"); pelmAnnotationSection->createChild("Info")->addContent("A human-readable annotation"); - pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.front()->strVboxCurrent); + pelmAnnotationSection->createChild("Annotation")->addContent(llDescription.back()->strVBoxCurrent); } // license std::list<VirtualSystemDescriptionEntry*> llLicense = vsdescThis->findByType(VirtualSystemDescriptionType_License); if (llLicense.size() && - !llLicense.front()->strVboxCurrent.isEmpty()) + !llLicense.back()->strVBoxCurrent.isEmpty()) { /* <EulaSection> <Info ovf:msgid="6">License agreement for the Virtual System.</Info> <License ovf:msgid="1">License terms can go in here.</License> </EulaSection> */ xml::ElementNode *pelmEulaSection; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) { pelmEulaSection = pelmVirtualSystem->createChild("Section"); pelmEulaSection->setAttribute("xsi:type", "ovf:EulaSection_Type"); @@ -1005,21 +1146,20 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, pelmEulaSection = pelmVirtualSystem->createChild("EulaSection"); pelmEulaSection->createChild("Info")->addContent("License agreement for the virtual system"); - pelmEulaSection->createChild("License")->addContent(llLicense.front()->strVboxCurrent); + pelmEulaSection->createChild("License")->addContent(llLicense.back()->strVBoxCurrent); } // operating system std::list<VirtualSystemDescriptionEntry*> llOS = vsdescThis->findByType(VirtualSystemDescriptionType_OS); - if (llOS.size() != 1) - throw setError(VBOX_E_NOT_SUPPORTED, - tr("Missing OS type")); + if (!llOS.size()) + throw setError(VBOX_E_NOT_SUPPORTED, tr("Missing OS type")); /* <OperatingSystemSection ovf:id="82"> <Info>Guest Operating System</Info> <Description>Linux 2.6.x</Description> </OperatingSystemSection> */ - VirtualSystemDescriptionEntry *pvsdeOS = llOS.front(); + VirtualSystemDescriptionEntry *pvsdeOS = llOS.back(); xml::ElementNode *pelmOperatingSystemSection; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) { pelmOperatingSystemSection = pelmVirtualSystem->createChild("Section"); pelmOperatingSystemSection->setAttribute("xsi:type", "ovf:OperatingSystemSection_Type"); @@ -1035,11 +1175,11 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, // add the VirtualBox ostype in a custom tag in a different namespace xml::ElementNode *pelmVBoxOSType = pelmOperatingSystemSection->createChild("vbox:OSType"); pelmVBoxOSType->setAttribute("ovf:required", "false"); - pelmVBoxOSType->addContent(pvsdeOS->strVboxCurrent); + pelmVBoxOSType->addContent(pvsdeOS->strVBoxCurrent); // <VirtualHardwareSection ovf:id="hw1" ovf:transport="iso"> xml::ElementNode *pelmVirtualHardwareSection; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) { // <Section xsi:type="ovf:VirtualHardwareSection_Type"> pelmVirtualHardwareSection = pelmVirtualSystem->createChild("Section"); @@ -1062,7 +1202,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, pelmSystem->createChild("vssd:ElementName")->addContent("Virtual Hardware Family"); // required OVF 1.0 // <vssd:InstanceId>0</vssd:InstanceId> - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) pelmSystem->createChild("vssd:InstanceId")->addContent("0"); else // capitalization changed... pelmSystem->createChild("vssd:InstanceID")->addContent("0"); @@ -1071,7 +1211,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, pelmSystem->createChild("vssd:VirtualSystemIdentifier")->addContent(strVMName); // <vssd:VirtualSystemType>vmx-4</vssd:VirtualSystemType> const char *pcszHardware = "virtualbox-2.2"; - if (enFormat == OVF_0_9) + if (enFormat == ovf::OVFVersion_0_9) // pretend to be vmware compatible then pcszHardware = "vmx-6"; pelmSystem->createChild("vssd:VirtualSystemType")->addContent(pcszHardware); @@ -1105,7 +1245,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, { const VirtualSystemDescriptionEntry &desc = *itD; - LogFlowFunc(("Loop %u: handling description entry ulIndex=%u, type=%s, strRef=%s, strOvf=%s, strVbox=%s, strExtraConfig=%s\n", + LogFlowFunc(("Loop %u: handling description entry ulIndex=%u, type=%s, strRef=%s, strOvf=%s, strVBox=%s, strExtraConfig=%s\n", uLoop, desc.ulIndex, ( desc.type == VirtualSystemDescriptionType_HardDiskControllerIDE ? "HardDiskControllerIDE" @@ -1116,7 +1256,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, : Utf8StrFmt("%d", desc.type).c_str()), desc.strRef.c_str(), desc.strOvf.c_str(), - desc.strVboxCurrent.c_str(), + desc.strVBoxCurrent.c_str(), desc.strExtraConfigCurrent.c_str())); ovf::ResourceType_T type = (ovf::ResourceType_T)0; // if this becomes != 0 then we do stuff @@ -1140,6 +1280,10 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, uint64_t uTemp; + ovf::VirtualHardwareItem vhi; + ovf::StorageItem si; + ovf::EthernetPortItem epi; + switch (desc.type) { case VirtualSystemDescriptionType_CPU: @@ -1155,7 +1299,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, { strDescription = "Number of virtual CPUs"; type = ovf::ResourceType_Processor; // 3 - desc.strVboxCurrent.toInt(uTemp); + desc.strVBoxCurrent.toInt(uTemp); lVirtualQuantity = (int32_t)uTemp; strCaption = Utf8StrFmt("%d virtual CPU", lVirtualQuantity); // without this ovftool won't eat the item } @@ -1175,7 +1319,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, { strDescription = "Memory Size"; type = ovf::ResourceType_Memory; // 4 - desc.strVboxCurrent.toInt(uTemp); + desc.strVBoxCurrent.toInt(uTemp); lVirtualQuantity = (int32_t)(uTemp / _1M); strAllocationUnits = "MegaBytes"; strCaption = Utf8StrFmt("%d MB of memory", lVirtualQuantity); // without this ovftool won't eat the item @@ -1195,7 +1339,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, { strDescription = "IDE Controller"; type = ovf::ResourceType_IDEController; // 5 - strResourceSubType = desc.strVboxCurrent; + strResourceSubType = desc.strVBoxCurrent; if (!lIDEPrimaryControllerIndex) { @@ -1241,13 +1385,13 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, lAddress = 0; lBusNumber = 0; - if ( desc.strVboxCurrent.isEmpty() // AHCI is the default in VirtualBox - || (!desc.strVboxCurrent.compare("ahci", Utf8Str::CaseInsensitive)) + if ( desc.strVBoxCurrent.isEmpty() // AHCI is the default in VirtualBox + || (!desc.strVBoxCurrent.compare("ahci", Utf8Str::CaseInsensitive)) ) strResourceSubType = "AHCI"; else throw setError(VBOX_E_NOT_SUPPORTED, - tr("Invalid config string \"%s\" in SATA controller"), desc.strVboxCurrent.c_str()); + tr("Invalid config string \"%s\" in SATA controller"), desc.strVBoxCurrent.c_str()); // remember this ID idSATAController = ulInstanceID; @@ -1277,17 +1421,17 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, lAddress = 0; lBusNumber = 0; - if ( desc.strVboxCurrent.isEmpty() // LsiLogic is the default in VirtualBox - || (!desc.strVboxCurrent.compare("lsilogic", Utf8Str::CaseInsensitive)) + if ( desc.strVBoxCurrent.isEmpty() // LsiLogic is the default in VirtualBox + || (!desc.strVBoxCurrent.compare("lsilogic", Utf8Str::CaseInsensitive)) ) strResourceSubType = "lsilogic"; - else if (!desc.strVboxCurrent.compare("buslogic", Utf8Str::CaseInsensitive)) + else if (!desc.strVBoxCurrent.compare("buslogic", Utf8Str::CaseInsensitive)) strResourceSubType = "buslogic"; - else if (!desc.strVboxCurrent.compare("lsilogicsas", Utf8Str::CaseInsensitive)) + else if (!desc.strVBoxCurrent.compare("lsilogicsas", Utf8Str::CaseInsensitive)) strResourceSubType = "lsilogicsas"; else throw setError(VBOX_E_NOT_SUPPORTED, - tr("Invalid config string \"%s\" in SCSI/SAS controller"), desc.strVboxCurrent.c_str()); + tr("Invalid config string \"%s\" in SCSI/SAS controller"), desc.strVBoxCurrent.c_str()); // remember this ID idSCSIController = ulInstanceID; @@ -1360,13 +1504,32 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, break; case VirtualSystemDescriptionType_CDROM: + /* <Item> + <rasd:Caption>cdrom1</rasd:Caption> + <rasd:InstanceId>8</rasd:InstanceId> + <rasd:ResourceType>15</rasd:ResourceType> + <rasd:HostResource>/disk/cdrom1</rasd:HostResource> + <rasd:Parent>4</rasd:Parent> + <rasd:AddressOnParent>0</rasd:AddressOnParent> + </Item> */ if (uLoop == 2) { + //uint32_t cDisks = stack.mapDisks.size(); + Utf8Str strDiskID = Utf8StrFmt("iso%RI32", ++cDVDs); + strDescription = "CD-ROM Drive"; - strCaption = Utf8StrFmt("cdrom%RI32", ++cDVDs); // OVFTool starts with 1 + strCaption = Utf8StrFmt("cdrom%RI32", cDVDs); // OVFTool starts with 1 type = ovf::ResourceType_CDDrive; // 15 lAutomaticAllocation = 1; + //skip empty Medium. There are no information to add into section <References> or <DiskSection> + if (desc.strVBoxCurrent.isNotEmpty() && + desc.skipIt == false) + { + // the following references the "<Disks>" XML block + strHostResource = Utf8StrFmt("/disk/%s", strDiskID.c_str()); + } + // controller=<index>;channel=<c> size_t pos1 = desc.strExtraConfigCurrent.find("controller="); size_t pos2 = desc.strExtraConfigCurrent.find("channel="); @@ -1395,6 +1558,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, throw setError(VBOX_E_NOT_SUPPORTED, tr("Missing or bad extra config string in DVD drive medium: \"%s\""), desc.strExtraConfigCurrent.c_str()); + stack.mapDisks[strDiskID] = &desc; // there is no DVD drive map to update because it is // handled completely with this entry. } @@ -1409,7 +1573,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, <rasd:InstanceID>3</rasd:InstanceID> <rasd:ResourceType>10</rasd:ResourceType> </Item> */ - if (uLoop == 1) + if (uLoop == 2) { lAutomaticAllocation = 1; strCaption = Utf8StrFmt("Ethernet adapter on '%s'", desc.strOvf.c_str()); @@ -1418,7 +1582,7 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, * To be compatible with vmware & others we set * PCNet32 for our PCNet types & E1000 for the * E1000 cards. */ - switch (desc.strVboxCurrent.toInt32()) + switch (desc.strVBoxCurrent.toInt32()) { case NetworkAdapterType_Am79C970A: case NetworkAdapterType_Am79C973: strResourceSubType = "PCNet32"; break; @@ -1478,65 +1642,176 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, if (type) { xml::ElementNode *pItem; + xml::ElementNode *pItemHelper; + RTCString itemElement; + RTCString itemElementHelper; - pItem = pelmVirtualHardwareSection->createChild("Item"); + if (enFormat == ovf::OVFVersion_2_0) + { + if(uLoop == 2) + { + if (desc.type == VirtualSystemDescriptionType_NetworkAdapter) + { + itemElement = "epasd:"; + pItem = pelmVirtualHardwareSection->createChild("EthernetPortItem"); + } + else if (desc.type == VirtualSystemDescriptionType_CDROM || + desc.type == VirtualSystemDescriptionType_HardDiskImage) + { + itemElement = "sasd:"; + pItem = pelmVirtualHardwareSection->createChild("StorageItem"); + } + else + pItem = NULL; + } + else + { + itemElement = "rasd:"; + pItem = pelmVirtualHardwareSection->createChild("Item"); + } + } + else + { + itemElement = "rasd:"; + pItem = pelmVirtualHardwareSection->createChild("Item"); + } // NOTE: DO NOT CHANGE THE ORDER of these items! The OVF standards prescribes that // the elements from the rasd: namespace must be sorted by letter, and VMware // actually requires this as well (see public bug #6612) if (lAddress != -1) - pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress)); + { + //pItem->createChild("rasd:Address")->addContent(Utf8StrFmt("%d", lAddress)); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("Address").c_str()); + pItemHelper->addContent(Utf8StrFmt("%d", lAddress)); + } if (lAddressOnParent != -1) - pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent)); + { + //pItem->createChild("rasd:AddressOnParent")->addContent(Utf8StrFmt("%d", lAddressOnParent)); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("AddressOnParent").c_str()); + pItemHelper->addContent(Utf8StrFmt("%d", lAddressOnParent)); + } if (!strAllocationUnits.isEmpty()) - pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits); + { + //pItem->createChild("rasd:AllocationUnits")->addContent(strAllocationUnits); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("AllocationUnits").c_str()); + pItemHelper->addContent(strAllocationUnits); + } if (lAutomaticAllocation != -1) - pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" ); + { + //pItem->createChild("rasd:AutomaticAllocation")->addContent( (lAutomaticAllocation) ? "true" : "false" ); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("AutomaticAllocation").c_str()); + pItemHelper->addContent((lAutomaticAllocation) ? "true" : "false" ); + } if (lBusNumber != -1) - if (enFormat == OVF_0_9) // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool compatibility - pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber)); + { + if (enFormat == ovf::OVFVersion_0_9) + { + // BusNumber is invalid OVF 1.0 so only write it in 0.9 mode for OVFTool + //pItem->createChild("rasd:BusNumber")->addContent(Utf8StrFmt("%d", lBusNumber)); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("BusNumber").c_str()); + pItemHelper->addContent(Utf8StrFmt("%d", lBusNumber)); + } + } if (!strCaption.isEmpty()) - pItem->createChild("rasd:Caption")->addContent(strCaption); + { + //pItem->createChild("rasd:Caption")->addContent(strCaption); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("Caption").c_str()); + pItemHelper->addContent(strCaption); + } if (!strConnection.isEmpty()) - pItem->createChild("rasd:Connection")->addContent(strConnection); + { + //pItem->createChild("rasd:Connection")->addContent(strConnection); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("Connection").c_str()); + pItemHelper->addContent(strConnection); + } if (!strDescription.isEmpty()) - pItem->createChild("rasd:Description")->addContent(strDescription); + { + //pItem->createChild("rasd:Description")->addContent(strDescription); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("Description").c_str()); + pItemHelper->addContent(strDescription); + } if (!strCaption.isEmpty()) - if (enFormat == OVF_1_0) - pItem->createChild("rasd:ElementName")->addContent(strCaption); + { + if (enFormat == ovf::OVFVersion_1_0) + { + //pItem->createChild("rasd:ElementName")->addContent(strCaption); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("ElementName").c_str()); + pItemHelper->addContent(strCaption); + } + } if (!strHostResource.isEmpty()) - pItem->createChild("rasd:HostResource")->addContent(strHostResource); + { + //pItem->createChild("rasd:HostResource")->addContent(strHostResource); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("HostResource").c_str()); + pItemHelper->addContent(strHostResource); + } - // <rasd:InstanceID>1</rasd:InstanceID> - xml::ElementNode *pelmInstanceID; - if (enFormat == OVF_0_9) - pelmInstanceID = pItem->createChild("rasd:InstanceId"); - else - pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed... - pelmInstanceID->addContent(Utf8StrFmt("%d", ulInstanceID++)); + { + // <rasd:InstanceID>1</rasd:InstanceID> + itemElementHelper = itemElement; + if (enFormat == ovf::OVFVersion_0_9) + //pelmInstanceID = pItem->createChild("rasd:InstanceId"); + pItemHelper = pItem->createChild(itemElementHelper.append("InstanceId").c_str()); + else + //pelmInstanceID = pItem->createChild("rasd:InstanceID"); // capitalization changed... + pItemHelper = pItem->createChild(itemElementHelper.append("InstanceID").c_str()); + + pItemHelper->addContent(Utf8StrFmt("%d", ulInstanceID++)); + } if (ulParent) - pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent)); + { + //pItem->createChild("rasd:Parent")->addContent(Utf8StrFmt("%d", ulParent)); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("Parent").c_str()); + pItemHelper->addContent(Utf8StrFmt("%d", ulParent)); + } if (!strResourceSubType.isEmpty()) - pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType); + { + //pItem->createChild("rasd:ResourceSubType")->addContent(strResourceSubType); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("ResourceSubType").c_str()); + pItemHelper->addContent(strResourceSubType); + } - // <rasd:ResourceType>3</rasd:ResourceType> - pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type)); + { + // <rasd:ResourceType>3</rasd:ResourceType> + //pItem->createChild("rasd:ResourceType")->addContent(Utf8StrFmt("%d", type)); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("ResourceType").c_str()); + pItemHelper->addContent(Utf8StrFmt("%d", type)); + } // <rasd:VirtualQuantity>1</rasd:VirtualQuantity> if (lVirtualQuantity != -1) - pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity)); + { + //pItem->createChild("rasd:VirtualQuantity")->addContent(Utf8StrFmt("%d", lVirtualQuantity)); + itemElementHelper = itemElement; + pItemHelper = pItem->createChild(itemElementHelper.append("VirtualQuantity").c_str()); + pItemHelper->addContent(Utf8StrFmt("%d", lVirtualQuantity)); + } } } } // for (size_t uLoop = 1; uLoop <= 2; ++uLoop) @@ -1558,10 +1833,26 @@ void Appliance::buildXMLForOneVirtualSystem(AutoWriteLockBase& writeLock, AutoWriteLock machineLock(vsdescThis->m->pMachine COMMA_LOCKVAL_SRC_POS); // fill the machine config vsdescThis->m->pMachine->copyMachineDataToSettings(*pConfig); + + // Apply export tweaks to machine settings + bool fStripAllMACs = m->optListExport.contains(ExportOptions_StripAllMACs); + bool fStripAllNonNATMACs = m->optListExport.contains(ExportOptions_StripAllNonNATMACs); + if (fStripAllMACs || fStripAllNonNATMACs) + { + for (settings::NetworkAdaptersList::iterator it = pConfig->hardwareMachine.llNetworkAdapters.begin(); + it != pConfig->hardwareMachine.llNetworkAdapters.end(); + ++it) + { + settings::NetworkAdapter &nic = *it; + if (fStripAllMACs || (fStripAllNonNATMACs && nic.mode != NetworkAttachmentType_NAT)) + nic.strMACAddress.setNull(); + } + } + // write the machine config to the vbox:Machine element pConfig->buildMachineXML(*pelmVBoxMachine, - settings::MachineConfigFile::BuildMachineXML_WriteVboxVersionAttribute - | settings::MachineConfigFile::BuildMachineXML_SkipRemovableMedia + settings::MachineConfigFile::BuildMachineXML_WriteVBoxVersionAttribute + /*| settings::MachineConfigFile::BuildMachineXML_SkipRemovableMedia*/ | settings::MachineConfigFile::BuildMachineXML_SuppressSavedState, // but not BuildMachineXML_IncludeSnapshots nor BuildMachineXML_MediaRegistry pllElementsWithUuidAttributes); @@ -1648,7 +1939,11 @@ HRESULT Appliance::writeFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) RT_ZERO(storage); storage.fCreateDigest = m->fManifest; storage.fSha256 = m->fSha256; - int vrc = VDInterfaceAdd(&pFileIo->Core, "Appliance::IOFile", + + + Utf8Str name = applianceIOName(applianceIOFile); + + int vrc = VDInterfaceAdd(&pFileIo->Core, name.c_str(), VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), &storage.pVDImageIfaces); if (RT_FAILURE(vrc)) @@ -1657,7 +1952,7 @@ HRESULT Appliance::writeFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) break; } rc = writeFSImpl(pTask, writeLock, pShaIo, &storage); - }while(0); + } while (0); /* Cleanup */ if (pShaIo) @@ -1702,16 +1997,20 @@ HRESULT Appliance::writeFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) RT_ZERO(storage); storage.fCreateDigest = m->fManifest; storage.fSha256 = m->fSha256; - vrc = VDInterfaceAdd(&pTarIo->Core, "Appliance::IOTar", + + Utf8Str name = applianceIOName(applianceIOTar); + + vrc = VDInterfaceAdd(&pTarIo->Core, name.c_str(), VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO), &storage.pVDImageIfaces); + if (RT_FAILURE(vrc)) { rc = E_FAIL; break; } rc = writeFSImpl(pTask, writeLock, pShaIo, &storage); - }while(0); + } while (0); RTTarClose(tar); @@ -1722,7 +2021,7 @@ HRESULT Appliance::writeFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) RTMemFree(pTarIo); /* Delete ova file on error */ - if(FAILED(rc)) + if (FAILED(rc)) RTFileDelete(pTask->locInfo.strPath.c_str()); LogFlowFuncLeave(); @@ -1749,8 +2048,10 @@ HRESULT Appliance::writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVD xml::Document doc; // Now fully build a valid ovf document in memory buildXML(writeLock, doc, stack, pTask->locInfo.strPath, pTask->enFormat); + /* Extract the OVA file name */ + Utf8Str strOvaFile = pTask->locInfo.strPath; /* Extract the path */ - Utf8Str strOvfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".ovf"); + Utf8Str strOvfFile = strOvaFile.stripExt().append(".ovf"); // Create a memory buffer containing the XML. */ void *pvBuf = 0; size_t cbSize; @@ -1770,12 +2071,16 @@ HRESULT Appliance::writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVD } // We need a proper format description + ComObjPtr<MediumFormat> formatTemp; + ComObjPtr<MediumFormat> format; // Scope for the AutoReadLock { SystemProperties *pSysProps = mVirtualBox->getSystemProperties(); AutoReadLock propsLock(pSysProps COMMA_LOCKVAL_SRC_POS); // We are always exporting to VMDK stream optimized for now + formatTemp = pSysProps->mediumFormatFromExtension("iso"); + format = pSysProps->mediumFormat("VMDK"); if (format.isNull()) throw setError(VBOX_E_NOT_SUPPORTED, @@ -1791,7 +2096,12 @@ HRESULT Appliance::writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVD const VirtualSystemDescriptionEntry *pDiskEntry = itS->second; // source path: where the VBox image is - const Utf8Str &strSrcFilePath = pDiskEntry->strVboxCurrent; + const Utf8Str &strSrcFilePath = pDiskEntry->strVBoxCurrent; + + //skip empty Medium. In common, It's may be empty CD/DVD + if (strSrcFilePath.isEmpty() || + pDiskEntry->skipIt == true) + continue; // Do NOT check here whether the file exists. findHardDisk will // figure that out, and filesystem-based tests are simply wrong @@ -1801,8 +2111,21 @@ HRESULT Appliance::writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVD ComObjPtr<Medium> pSourceDisk; Log(("Finding source disk \"%s\"\n", strSrcFilePath.c_str())); - rc = mVirtualBox->findHardDiskByLocation(strSrcFilePath, true, &pSourceDisk); - if (FAILED(rc)) throw rc; + + if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage) + { + rc = mVirtualBox->findHardDiskByLocation(strSrcFilePath, true, &pSourceDisk); + if (FAILED(rc)) throw rc; + } + else//may be CD or DVD + { + rc = mVirtualBox->findDVDOrFloppyImage(DeviceType_DVD, + NULL, + strSrcFilePath, + true, + &pSourceDisk); + if (FAILED(rc)) throw rc; + } Bstr uuidSource; rc = pSourceDisk->COMGETTER(Id)(uuidSource.asOutParam()); @@ -1821,22 +2144,118 @@ HRESULT Appliance::writeFSImpl(TaskOVF *pTask, AutoWriteLockBase& writeLock, PVD writeLock.release(); try { - ComObjPtr<Progress> pProgress2; - pProgress2.createObject(); - rc = pProgress2->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium '%s'"), strTargetFilePath.c_str()).raw(), TRUE); - if (FAILED(rc)) throw rc; - // advance to the next operation pTask->pProgress->SetNextOperation(BstrFmt(tr("Exporting to disk image '%s'"), RTPathFilename(strTargetFilePath.c_str())).raw(), pDiskEntry->ulSizeMB); // operation's weight, as set up with the IProgress originally // create a flat copy of the source disk image - rc = pSourceDisk->exportFile(strTargetFilePath.c_str(), format, MediumVariant_VmdkStreamOptimized, pIfIo, pStorage, pProgress2); - if (FAILED(rc)) throw rc; + if (pDiskEntry->type == VirtualSystemDescriptionType_HardDiskImage) + { + ComObjPtr<Progress> pProgress2; + pProgress2.createObject(); + rc = pProgress2->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium '%s'"), strTargetFilePath.c_str()).raw(), TRUE); + if (FAILED(rc)) throw rc; + + rc = pSourceDisk->exportFile(strTargetFilePath.c_str(), + format, + MediumVariant_VmdkStreamOptimized, + pIfIo, + pStorage, + pProgress2); + if (FAILED(rc)) throw rc; + + ComPtr<IProgress> pProgress3(pProgress2); + // now wait for the background disk operation to complete; this throws HRESULTs on error + waitForAsyncProgress(pTask->pProgress, pProgress3); + } + else//pDiskEntry->type == VirtualSystemDescriptionType_CDROM + { + //copy/clone CD/DVD image + /* Read the ISO file and add one to OVA/OVF package */ + { + void *pvStorage; + RTFILE pFile = NULL; + void *pvUser = pStorage; + + vrc = pIfIo->pfnOpen(pvUser, strTargetFilePath.c_str(), + RTFILE_O_OPEN_CREATE | RTFILE_O_WRITE | RTFILE_O_DENY_NONE, + 0, + &pvStorage); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_FILE_ERROR, + tr("Could not create or open file '%s' (%Rrc)"), + strTargetFilePath.c_str(), vrc); + + vrc = RTFileOpen(&pFile, + strSrcFilePath.c_str(), + RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); + + if (RT_FAILURE(vrc) || pFile == NULL) + { + pIfIo->pfnClose(pvUser, pvStorage); + throw setError(VBOX_E_FILE_ERROR, + tr("Could not create or open file '%s' (%Rrc)"), + strSrcFilePath.c_str(), vrc); + } + + void *pvTmpBuf = 0; + size_t cbTmpSize = _1M; + uint64_t cbAllWritten = 0; + uint64_t cbFile = 0; + size_t cbSize = 0; + + vrc = RTFileGetSize(pFile, &cbFile); + + do + { + pvTmpBuf = RTMemAlloc(cbTmpSize); + if (!pvTmpBuf) + { + vrc = VERR_NO_MEMORY; + break; + } - ComPtr<IProgress> pProgress3(pProgress2); - // now wait for the background disk operation to complete; this throws HRESULTs on error - waitForAsyncProgress(pTask->pProgress, pProgress3); + for (;;) + { + // copy raw data into the buffer pvTmpBuf + vrc = RTFileRead(pFile, pvTmpBuf, cbTmpSize, &cbSize); + + if (RT_FAILURE(vrc) || cbSize == 0) + break; + + size_t cbToWrite = cbSize; + size_t cbWritten = 0; + + vrc = pIfIo->pfnWriteSync(pvUser, + pvStorage, + cbAllWritten, + pvTmpBuf, + cbToWrite,&cbWritten); + + if (RT_FAILURE(vrc)) + break; + + cbAllWritten += cbWritten; + } + } while (0); + + pIfIo->pfnClose(pvUser, pvStorage); + RTFileClose(pFile); + + if (pvTmpBuf) + RTMemFree(pvTmpBuf); + + if (RT_FAILURE(vrc)) + { + if (vrc == VERR_EOF) + vrc = VINF_SUCCESS; + else + throw setError(VBOX_E_FILE_ERROR, + tr("Error during copy CD/DVD image '%s' (%Rrc)"), + strSrcFilePath.c_str(), vrc); + } + } + } } catch (HRESULT rc3) { diff --git a/src/VBox/Main/src-server/ApplianceImplIO.cpp b/src/VBox/Main/src-server/ApplianceImplIO.cpp index a31f93a8..1a9ac12a 100644 --- a/src/VBox/Main/src-server/ApplianceImplIO.cpp +++ b/src/VBox/Main/src-server/ApplianceImplIO.cpp @@ -1,11 +1,10 @@ /* $Id: ApplianceImplIO.cpp $ */ /** @file - * * IO helper for IAppliance COM class implementations. */ /* - * Copyright (C) 2010-2012 Oracle Corporation + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,19 +22,23 @@ #include "ProgressImpl.h" #include "ApplianceImpl.h" #include "ApplianceImplPrivate.h" +#include "VirtualBoxImpl.h" +#include <iprt/zip.h> #include <iprt/tar.h> #include <iprt/sha.h> #include <iprt/path.h> #include <iprt/asm.h> #include <iprt/stream.h> #include <iprt/circbuf.h> +#include <iprt/vfs.h> +#include <iprt/manifest.h> +#include <VBox/vd-ifs.h> #include <VBox/vd.h> /****************************************************************************** * Structures and Typedefs * ******************************************************************************/ - typedef struct FILESTORAGEINTERNAL { /** File handle. */ @@ -107,13 +110,14 @@ typedef struct SHASTORAGEINTERNAL #if 0 # define DEBUG_PRINT_FLOW() RTPrintf("%s\n", __FUNCTION__) #else -# define DEBUG_PRINT_FLOW() do {} while(0) +# define DEBUG_PRINT_FLOW() do {} while (0) #endif /****************************************************************************** * Internal Functions * ******************************************************************************/ + /****************************************************************************** * Internal: RTFile interface ******************************************************************************/ @@ -281,7 +285,7 @@ static int tarOpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen int rc = VINF_SUCCESS; - if ( fOpen & RTFILE_O_READ + if (fOpen & RTFILE_O_READ && !(fOpen & RTFILE_O_WRITE)) { /* Read only is a little bit more complicated than writing, cause we @@ -297,13 +301,20 @@ static int tarOpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen * */ bool fFound = false; + for (;;) { char *pszFilename = 0; rc = RTTarCurrentFile(tar, &pszFilename); if (RT_SUCCESS(rc)) { - fFound = !strcmp(pszFilename, RTPathFilename(pszLocation)); + if (rc == VINF_TAR_DIR_PATH) + { + break; + } + + fFound = !RTStrICmp(pszFilename, pszLocation); + RTStrFree(pszFilename); if (fFound) break; @@ -311,7 +322,9 @@ static int tarOpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen { rc = RTTarSeekNextFile(tar); if (RT_FAILURE(rc)) + { break; + } } } else @@ -477,7 +490,7 @@ DECLCALLBACK(int) shaCalcWorkerThread(RTTHREAD /* aThread */, void *pvUser) int rc = VINF_SUCCESS; bool fLoop = true; - while(fLoop) + while (fLoop) { /* What should we do next? */ uint32_t u32Status = ASMAtomicReadU32(&pInt->u32Status); @@ -755,7 +768,7 @@ static int shaOpenCallback(void *pvUser, const char *pszLocation, uint32_t fOpen rc = shaSignalManifestThread(pInt, STATUS_READ); } } - while(0); + while (0); if (RT_FAILURE(rc)) { @@ -1014,7 +1027,7 @@ static int shaWriteSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset, if ((cbWrite - cbAllWritten) > cbAvail) { rc = shaSignalManifestThread(pInt, STATUS_WRITE); - if(RT_FAILURE(rc)) + if (RT_FAILURE(rc)) break; /* If there is _no_ free space available, we have to wait until it is. */ if (cbAvail == 0) @@ -1084,24 +1097,29 @@ static int shaReadSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset, AssertMsgFailed(("Jumping backwards is not possible, sequential access is supported only\n")); size_t cbAllRead = 0; + size_t cbAvail = 0; for (;;) { /* Finished? */ if (cbAllRead == cbRead) break; - size_t cbAvail = RTCircBufUsed(pInt->pCircBuf); + + cbAvail = RTCircBufUsed(pInt->pCircBuf); + if ( cbAvail == 0 && pInt->fEOF && !RTCircBufIsWriting(pInt->pCircBuf)) { + rc = VINF_EOF; break; } + /* If there isn't enough data make sure the worker thread is fetching * more. */ if ((cbRead - cbAllRead) > cbAvail) { rc = shaSignalManifestThread(pInt, STATUS_READ); - if(RT_FAILURE(rc)) + if (RT_FAILURE(rc)) break; /* If there is _no_ data available, we have to wait until it is. */ if (cbAvail == 0) @@ -1136,6 +1154,7 @@ static int shaReadSyncCallback(void *pvUser, void *pvStorage, uint64_t uOffset, /* Signal the thread to read more data in the mean time. */ if ( RT_SUCCESS(rc) + && rc != VINF_EOF && RTCircBufFree(pInt->pCircBuf) >= (RTCircBufSize(pInt->pCircBuf) / 2)) rc = shaSignalManifestThread(pInt, STATUS_READ); @@ -1164,10 +1183,6 @@ static int shaFlushSyncCallback(void *pvUser, void *pvStorage) return vdIfIoFileFlushSync(pIfIo, pInt->pvStorage); } -/****************************************************************************** - * Public Functions * - ******************************************************************************/ - PVDINTERFACEIO ShaCreateInterface() { PVDINTERFACEIO pCallbacks = (PVDINTERFACEIO)RTMemAllocZ(sizeof(VDINTERFACEIO)); @@ -1274,7 +1289,7 @@ int ShaReadBuf(const char *pcszFilename, void **ppvBuf, size_t *pcbSize, PVDINTE memcpy(&((char*)pvBuf)[cbAllRead], pvTmpBuf, cbRead); cbAllRead += cbRead; } - }while(0); + } while (0); pIfIo->pfnClose(pvUser, pvStorage); @@ -1325,8 +1340,152 @@ int ShaWriteBuf(const char *pcszFilename, void *pvBuf, size_t cbSize, PVDINTERFA cbAllWritten += cbWritten; } + rc = pIfIo->pfnClose(pvUser, pvStorage); + + return rc; +} + +int decompressImageAndSave(const char *pcszFullFilenameIn, const char *pcszFullFilenameOut, PVDINTERFACEIO pIfIo, void *pvUser) +{ + /* Validate input. */ + AssertPtrReturn(pIfIo, VERR_INVALID_POINTER); + + PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; + /* + * Open the source file. + */ + void *pvStorage; + int rc = pIfIo->pfnOpen(pvUser, pcszFullFilenameIn, + RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, 0, + &pvStorage); + if (RT_FAILURE(rc)) + return rc; + + /* Turn the source file handle/whatever into a VFS stream. */ + RTVFSIOSTREAM hVfsIosCompressedSrc; + + rc = VDIfCreateVfsStream(pIfIo, pvStorage, RTFILE_O_READ, &hVfsIosCompressedSrc); + if (RT_SUCCESS(rc)) + { + /* Pass the source thru gunzip. */ + RTVFSIOSTREAM hVfsIosSrc; + rc = RTZipGzipDecompressIoStream(hVfsIosCompressedSrc, 0, &hVfsIosSrc); + if (RT_SUCCESS(rc)) + { + /* + * Create the output file, including necessary paths. + * Any existing file will be overwritten. + */ + rc = VirtualBox::ensureFilePathExists(Utf8Str(pcszFullFilenameOut), true /*fCreate*/); + if (RT_SUCCESS(rc)) + { + RTVFSIOSTREAM hVfsIosDst; + rc = RTVfsIoStrmOpenNormal(pcszFullFilenameOut, + RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL, + &hVfsIosDst); + if (RT_SUCCESS(rc)) + { + /* + * Pump the bytes thru. If we fail, delete the output file. + */ + RTMANIFEST hFileManifest = NIL_RTMANIFEST; + rc = RTManifestCreate(0 /*fFlags*/, &hFileManifest); + if (RT_SUCCESS(rc)) + { + RTVFSIOSTREAM hVfsIosMfst; + + uint32_t digestType = (pShaStorage->fSha256 == true) ? RTMANIFEST_ATTR_SHA256: RTMANIFEST_ATTR_SHA1; + + rc = RTManifestEntryAddPassthruIoStream(hFileManifest, + hVfsIosSrc, + "ovf import", + digestType, + true /*read*/, &hVfsIosMfst); + if (RT_SUCCESS(rc)) + { + rc = RTVfsUtilPumpIoStreams(hVfsIosMfst, hVfsIosDst, 0); + } + + RTVfsIoStrmRelease(hVfsIosMfst); + } + + RTVfsIoStrmRelease(hVfsIosDst); + } + } + + RTVfsIoStrmRelease(hVfsIosSrc); + } + } pIfIo->pfnClose(pvUser, pvStorage); return rc; } +int copyFileAndCalcShaDigest(const char *pcszSourceFilename, const char *pcszTargetFilename, PVDINTERFACEIO pIfIo, void *pvUser) +{ + /* Validate input. */ + AssertPtrReturn(pIfIo, VERR_INVALID_POINTER); + + PSHASTORAGE pShaStorage = (PSHASTORAGE)pvUser; + void *pvStorage; + + int rc = pIfIo->pfnOpen(pvUser, pcszSourceFilename, + RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, 0, + &pvStorage); + if (RT_FAILURE(rc)) + return rc; + + /* Turn the source file handle/whatever into a VFS stream. */ + RTVFSIOSTREAM hVfsIosSrc; + + rc = VDIfCreateVfsStream(pIfIo, pvStorage, RTFILE_O_READ, &hVfsIosSrc); + if (RT_SUCCESS(rc)) + { + /* + * Create the output file, including necessary paths. + * Any existing file will be overwritten. + */ + rc = VirtualBox::ensureFilePathExists(Utf8Str(pcszTargetFilename), true /*fCreate*/); + if (RT_SUCCESS(rc)) + { + RTVFSIOSTREAM hVfsIosDst; + rc = RTVfsIoStrmOpenNormal(pcszTargetFilename, + RTFILE_O_CREATE_REPLACE | RTFILE_O_WRITE | RTFILE_O_DENY_ALL, + &hVfsIosDst); + if (RT_SUCCESS(rc)) + { + /* + * Pump the bytes thru. If we fail, delete the output file. + */ + RTMANIFEST hFileManifest = NIL_RTMANIFEST; + rc = RTManifestCreate(0 /*fFlags*/, &hFileManifest); + if (RT_SUCCESS(rc)) + { + RTVFSIOSTREAM hVfsIosMfst; + + uint32_t digestType = (pShaStorage->fSha256 == true) ? RTMANIFEST_ATTR_SHA256: RTMANIFEST_ATTR_SHA1; + + rc = RTManifestEntryAddPassthruIoStream(hFileManifest, + hVfsIosSrc, + "ovf import", + digestType, + true /*read*/, &hVfsIosMfst); + if (RT_SUCCESS(rc)) + { + rc = RTVfsUtilPumpIoStreams(hVfsIosMfst, hVfsIosDst, 0); + } + + RTVfsIoStrmRelease(hVfsIosMfst); + } + + RTVfsIoStrmRelease(hVfsIosDst); + } + } + + RTVfsIoStrmRelease(hVfsIosSrc); + + } + + pIfIo->pfnClose(pvUser, pvStorage); + return rc; +} diff --git a/src/VBox/Main/src-server/ApplianceImplImport.cpp b/src/VBox/Main/src-server/ApplianceImplImport.cpp index 447d717f..1eada736 100644 --- a/src/VBox/Main/src-server/ApplianceImplImport.cpp +++ b/src/VBox/Main/src-server/ApplianceImplImport.cpp @@ -1,11 +1,10 @@ /* $Id: ApplianceImplImport.cpp $ */ /** @file - * * IAppliance and IVirtualSystem COM class implementations. */ /* - * Copyright (C) 2008-2012 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -47,6 +46,8 @@ #include <VBox/version.h> #include <VBox/settings.h> +#include <set> + using namespace std; //////////////////////////////////////////////////////////////////////////////// @@ -116,7 +117,8 @@ STDMETHODIMP Appliance::Read(IN_BSTR path, IProgress **aProgress) STDMETHODIMP Appliance::Interpret() { // @todo: - // - don't use COM methods but the methods directly (faster, but needs appropriate locking of that objects itself (s. HardDisk)) + // - don't use COM methods but the methods directly (faster, but needs appropriate + // locking of that objects itself (s. HardDisk)) // - Appropriate handle errors like not supported file formats AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -160,21 +162,21 @@ STDMETHODIMP Appliance::Interpret() // if the virtual system in OVF had a <vbox:Machine> element, have the // VirtualBox settings code parse that XML now - if (vsysThis.pelmVboxMachine) - pNewDesc->importVboxMachineXML(*vsysThis.pelmVboxMachine); + if (vsysThis.pelmVBoxMachine) + pNewDesc->importVBoxMachineXML(*vsysThis.pelmVBoxMachine); // Guest OS type // This is taken from one of three places, in this order: Utf8Str strOsTypeVBox; Utf8StrFmt strCIMOSType("%RU32", (uint32_t)vsysThis.cimos); // 1) If there is a <vbox:Machine>, then use the type from there. - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->machineUserData.strOsType.isNotEmpty() ) strOsTypeVBox = pNewDesc->m->pConfig->machineUserData.strOsType; // 2) Otherwise, if there is OperatingSystemSection/vbox:OSType, use that one. - else if (vsysThis.strTypeVbox.isNotEmpty()) // OVFReader has found vbox:OSType - strOsTypeVBox = vsysThis.strTypeVbox; + else if (vsysThis.strTypeVBox.isNotEmpty()) // OVFReader has found vbox:OSType + strOsTypeVBox = vsysThis.strTypeVBox; // 3) Otherwise, make a best guess what the vbox type is from the OVF (CIM) OS type. else convertCIMOSType2VBoxOSType(strOsTypeVBox, vsysThis.cimos, vsysThis.strCimosDesc); @@ -186,7 +188,7 @@ STDMETHODIMP Appliance::Interpret() /* VM name */ Utf8Str nameVBox; /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->machineUserData.strName.isNotEmpty()) nameVBox = pNewDesc->m->pConfig->machineUserData.strName; else @@ -269,7 +271,7 @@ STDMETHODIMP Appliance::Interpret() /* CPU count */ ULONG cpuCountVBox; /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->hardwareMachine.cCPUs) cpuCountVBox = pNewDesc->m->pConfig->hardwareMachine.cCPUs; else @@ -277,7 +279,8 @@ STDMETHODIMP Appliance::Interpret() /* Check for the constraints */ if (cpuCountVBox > SchemaDefs::MaxCPUCount) { - addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for max %u CPU's only."), + addWarning(tr("The virtual system \"%s\" claims support for %u CPU's, but VirtualBox has support for " + "max %u CPU's only."), vsysThis.strName.c_str(), cpuCountVBox, SchemaDefs::MaxCPUCount); cpuCountVBox = SchemaDefs::MaxCPUCount; } @@ -291,7 +294,7 @@ STDMETHODIMP Appliance::Interpret() /* RAM */ uint64_t ullMemSizeVBox; /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB) ullMemSizeVBox = pNewDesc->m->pConfig->hardwareMachine.ulMemorySizeMB; else @@ -303,7 +306,8 @@ STDMETHODIMP Appliance::Interpret() ) ) { - addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has support for min %u & max %u MB RAM size only."), + addWarning(tr("The virtual system \"%s\" claims support for %llu MB RAM size, but VirtualBox has " + "support for min %u & max %u MB RAM size only."), vsysThis.strName.c_str(), ullMemSizeVBox, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB); ullMemSizeVBox = RT_MIN(RT_MAX(ullMemSizeVBox, MM_RAM_MIN_IN_MB), MM_RAM_MAX_IN_MB); } @@ -325,9 +329,12 @@ STDMETHODIMP Appliance::Interpret() Utf8Str strSoundCard; Utf8Str strSoundCardOrig; /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( vsysThis.pelmVboxMachine + if ( vsysThis.pelmVBoxMachine && pNewDesc->m->pConfig->hardwareMachine.audioAdapter.fEnabled) - strSoundCard = Utf8StrFmt("%RU32", (uint32_t)pNewDesc->m->pConfig->hardwareMachine.audioAdapter.controllerType); + { + strSoundCard = Utf8StrFmt("%RU32", + (uint32_t)pNewDesc->m->pConfig->hardwareMachine.audioAdapter.controllerType); + } else if (vsysThis.strSoundCardType.isNotEmpty()) { /* Set the AC97 always for the simple OVF case. @@ -344,22 +351,23 @@ STDMETHODIMP Appliance::Interpret() #ifdef VBOX_WITH_USB /* USB Controller */ /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if ( ( vsysThis.pelmVboxMachine - && pNewDesc->m->pConfig->hardwareMachine.usbController.fEnabled) + if ( ( vsysThis.pelmVBoxMachine + && pNewDesc->m->pConfig->hardwareMachine.usbSettings.llUSBControllers.size() > 0) || vsysThis.fHasUsbController) pNewDesc->addEntry(VirtualSystemDescriptionType_USBController, "", "", ""); #endif /* VBOX_WITH_USB */ /* Network Controller */ /* If there is a <vbox:Machine>, we always prefer the setting from there. */ - if (vsysThis.pelmVboxMachine) + if (vsysThis.pelmVBoxMachine) { uint32_t maxNetworkAdapters = Global::getMaxNetworkAdapters(pNewDesc->m->pConfig->hardwareMachine.chipsetType); const settings::NetworkAdaptersList &llNetworkAdapters = pNewDesc->m->pConfig->hardwareMachine.llNetworkAdapters; /* Check for the constrains */ if (llNetworkAdapters.size() > maxNetworkAdapters) - addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."), + addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox " + "has support for max %u network adapter only."), vsysThis.strName.c_str(), llNetworkAdapters.size(), maxNetworkAdapters); /* Iterate through all network adapters. */ settings::NetworkAdaptersList::const_iterator it1; @@ -387,7 +395,8 @@ STDMETHODIMP Appliance::Interpret() /* Check for the constrains */ if (cEthernetAdapters > maxNetworkAdapters) - addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox has support for max %u network adapter only."), + addWarning(tr("The virtual system \"%s\" claims support for %zu network adapters, but VirtualBox " + "has support for max %u network adapter only."), vsysThis.strName.c_str(), cEthernetAdapters, maxNetworkAdapters); /* Get the default network adapter type for the selected guest OS */ @@ -462,7 +471,7 @@ STDMETHODIMP Appliance::Interpret() /* If there is a <vbox:Machine>, we always prefer the setting from there. */ bool fFloppy = false; bool fDVD = false; - if (vsysThis.pelmVboxMachine) + if (vsysThis.pelmVBoxMachine) { settings::StorageControllersList &llControllers = pNewDesc->m->pConfig->storageMachine.llStorageControllers; settings::StorageControllersList::iterator it3; @@ -526,12 +535,13 @@ STDMETHODIMP Appliance::Interpret() pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskControllerIDE, strControllerID, // strRef hdc.strControllerType, // aOvfValue - strType); // aVboxValue + strType); // aVBoxValue } else /* Warn only once */ if (cIDEused == 2) - addWarning(tr("The virtual \"%s\" system requests support for more than two IDE controller channels, but VirtualBox supports only two."), + addWarning(tr("The virtual \"%s\" system requests support for more than two " + "IDE controller channels, but VirtualBox supports only two."), vsysThis.strName.c_str()); ++cIDEused; @@ -552,7 +562,8 @@ STDMETHODIMP Appliance::Interpret() { /* Warn only once */ if (cSATAused == 1) - addWarning(tr("The virtual system \"%s\" requests support for more than one SATA controller, but VirtualBox has support for only one"), + addWarning(tr("The virtual system \"%s\" requests support for more than one " + "SATA controller, but VirtualBox has support for only one"), vsysThis.strName.c_str()); } @@ -579,7 +590,9 @@ STDMETHODIMP Appliance::Interpret() hdcController); } else - addWarning(tr("The virtual system \"%s\" requests support for an additional SCSI controller of type \"%s\" with ID %s, but VirtualBox presently supports only one SCSI controller."), + addWarning(tr("The virtual system \"%s\" requests support for an additional " + "SCSI controller of type \"%s\" with ID %s, but VirtualBox presently " + "supports only one SCSI controller."), vsysThis.strName.c_str(), hdc.strControllerType.c_str(), strControllerID.c_str()); @@ -599,22 +612,96 @@ STDMETHODIMP Appliance::Interpret() { const ovf::VirtualDisk &hd = itVD->second; /* Get the associated disk image */ - const ovf::DiskImage &di = m->pReader->m_mapDisks[hd.strDiskId]; + ovf::DiskImage di; + std::map<RTCString, ovf::DiskImage>::iterator foundDisk; + + foundDisk = m->pReader->m_mapDisks.find(hd.strDiskId); + if (foundDisk == m->pReader->m_mapDisks.end()) + continue; + else + { + di = foundDisk->second; + } + + /* + * Figure out from URI which format the image of disk has. + * URI must have inside section <Disk> . + * But there aren't strong requirements about correspondence one URI for one disk virtual format. + * So possibly, we aren't able to recognize some URIs. + */ + Utf8Str vdf = typeOfVirtualDiskFormatFromURI(di.strFormat); + + /* + * fallback, if we can't determine virtual disk format using URI from the attribute ovf:format + * in the corresponding section <Disk> in the OVF file. + */ + if (vdf.isEmpty()) + { + /* Figure out from extension which format the image of disk has. */ + { + char *pszExt = RTPathExt(di.strHref.c_str()); + /* Get the system properties. */ + SystemProperties *pSysProps = mVirtualBox->getSystemProperties(); + ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension(&pszExt[1]); + if (trgFormat.isNull()) + { + throw setError(E_FAIL, + tr("Internal inconsistency looking up medium format for the disk image '%s'"), + di.strHref.c_str()); + } + + Bstr bstrFormatName; + rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); + if (FAILED(rc)) + throw rc; + + vdf = Utf8Str(bstrFormatName); + } + } // @todo: // - figure out all possible vmdk formats we also support // - figure out if there is a url specifier for vhd already // - we need a url specifier for the vdi format - if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) - ) + + if (vdf.compare("VMDK", Utf8Str::CaseInsensitive) == 0) { /* If the href is empty use the VM name as filename */ Utf8Str strFilename = di.strHref; if (!strFilename.length()) - strFilename = Utf8StrFmt("%s.vmdk", nameVBox.c_str()); + strFilename = Utf8StrFmt("%s.vmdk", hd.strDiskId.c_str()); + + Utf8Str strTargetPath = Utf8Str(strMachineFolder); + strTargetPath.append(RTPATH_DELIMITER).append(di.strHref); + searchUniqueDiskImageFilePath(strTargetPath); + + /* find the description for the hard disk controller + * that has the same ID as hd.idController */ + const VirtualSystemDescriptionEntry *pController; + if (!(pController = pNewDesc->findControllerFromID(hd.idController))) + throw setError(E_FAIL, + tr("Cannot find hard disk controller with OVF instance ID %RI32 " + "to which disk \"%s\" should be attached"), + hd.idController, + di.strHref.c_str()); + + /* controller to attach to, and the bus within that controller */ + Utf8StrFmt strExtraConfig("controller=%RI16;channel=%RI16", + pController->ulIndex, + hd.ulAddressOnParent); + pNewDesc->addEntry(VirtualSystemDescriptionType_HardDiskImage, + hd.strDiskId, + di.strHref, + strTargetPath, + di.ulSuggestedSizeMB, + strExtraConfig); + } + else if (vdf.compare("RAW", Utf8Str::CaseInsensitive) == 0) + { + /* If the href is empty use the VM name as filename */ + Utf8Str strFilename = di.strHref; + if (!strFilename.length()) + strFilename = Utf8StrFmt("%s.iso", hd.strDiskId.c_str()); Utf8Str strTargetPath = Utf8Str(strMachineFolder) .append(RTPATH_DELIMITER) @@ -626,7 +713,8 @@ STDMETHODIMP Appliance::Interpret() const VirtualSystemDescriptionEntry *pController; if (!(pController = pNewDesc->findControllerFromID(hd.idController))) throw setError(E_FAIL, - tr("Cannot find hard disk controller with OVF instance ID %RI32 to which disk \"%s\" should be attached"), + tr("Cannot find disk controller with OVF instance ID %RI32 " + "to which disk \"%s\" should be attached"), hd.idController, di.strHref.c_str()); @@ -643,7 +731,9 @@ STDMETHODIMP Appliance::Interpret() } else throw setError(VBOX_E_FILE_ERROR, - tr("Unsupported format for virtual disk image in OVF: \"%s\"", di.strFormat.c_str())); + tr("Unsupported format for virtual disk image %s in OVF: \"%s\""), + di.strHref.c_str(), + di.strFormat.c_str()); } } @@ -678,12 +768,12 @@ STDMETHODIMP Appliance::ImportMachines(ComSafeArrayIn(ImportOptions_T, options), AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - if (options != NULL) - m->optList = com::SafeArray<ImportOptions_T>(ComSafeArrayInArg(options)).toList(); + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - AssertReturn(!(m->optList.contains(ImportOptions_KeepAllMACs) && m->optList.contains(ImportOptions_KeepNATMACs)), E_INVALIDARG); + if (options != NULL) + m->optListImport = com::SafeArray<ImportOptions_T>(ComSafeArrayInArg(options)).toList(); - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + AssertReturn(!(m->optListImport.contains(ImportOptions_KeepAllMACs) && m->optListImport.contains(ImportOptions_KeepNATMACs)), E_INVALIDARG); // do not allow entering this method if the appliance is busy reading or writing if (!isApplianceIdle()) @@ -717,6 +807,35 @@ STDMETHODIMP Appliance::ImportMachines(ComSafeArrayIn(ImportOptions_T, options), // //////////////////////////////////////////////////////////////////////////////// +HRESULT Appliance::preCheckImageAvailability(PSHASTORAGE pSHAStorage, + RTCString &availableImage) +{ + HRESULT rc = S_OK; + RTTAR tar = (RTTAR)pSHAStorage->pVDImageIfaces->pvUser; + char *pszFilename = 0; + + int vrc = RTTarCurrentFile(tar, &pszFilename); + + if (RT_FAILURE(vrc)) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Could not open the current file in the OVA package (%Rrc)"), vrc); + } + else + { + if (vrc == VINF_TAR_DIR_PATH) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Empty directory folder (%s) isn't allowed in the OVA package (%Rrc)"), + pszFilename, + vrc); + } + } + + availableImage = pszFilename; + + return rc; +} /******************************************************************************* * Read stuff @@ -818,36 +937,125 @@ HRESULT Appliance::readFSOVF(TaskOVF *pTask) LogFlowFuncEnter(); HRESULT rc = S_OK; + int vrc = VINF_SUCCESS; PVDINTERFACEIO pShaIo = 0; PVDINTERFACEIO pFileIo = 0; do { - pShaIo = ShaCreateInterface(); - if (!pShaIo) - { - rc = E_OUTOFMEMORY; - break; - } - pFileIo = FileCreateInterface(); - if (!pFileIo) + try { - rc = E_OUTOFMEMORY; - break; + /* Create the necessary file access interfaces. */ + pFileIo = FileCreateInterface(); + if (!pFileIo) + { + rc = E_OUTOFMEMORY; + break; + } + + Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf"); + + SHASTORAGE storage; + RT_ZERO(storage); + + if (RTFileExists(strMfFile.c_str())) + { + pShaIo = ShaCreateInterface(); + if (!pShaIo) + { + rc = E_OUTOFMEMORY; + break; + } + + //read the manifest file and find a type of used digest + RTFILE pFile = NULL; + vrc = RTFileOpen(&pFile, strMfFile.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); + if (RT_SUCCESS(vrc) && pFile != NULL) + { + uint64_t cbFile = 0; + uint64_t maxFileSize = _1M; + size_t cbRead = 0; + void *pBuf; /** @todo r=bird: You leak this buffer! throwing stuff is evil. */ + + vrc = RTFileGetSize(pFile, &cbFile); + if (cbFile > maxFileSize) + throw setError(VBOX_E_FILE_ERROR, + tr("Size of the manifest file '%s' is bigger than 1Mb. Check it, please."), + RTPathFilename(strMfFile.c_str())); + + if (RT_SUCCESS(vrc)) + pBuf = RTMemAllocZ(cbFile); + else + throw setError(VBOX_E_FILE_ERROR, + tr("Could not get size of the manifest file '%s' "), + RTPathFilename(strMfFile.c_str())); + + vrc = RTFileRead(pFile, pBuf, cbFile, &cbRead); + + if (RT_FAILURE(vrc)) + { + if (pBuf) + RTMemFree(pBuf); + throw setError(VBOX_E_FILE_ERROR, + tr("Could not read the manifest file '%s' (%Rrc)"), + RTPathFilename(strMfFile.c_str()), vrc); + } + + RTFileClose(pFile); + + RTDIGESTTYPE digestType; + vrc = RTManifestVerifyDigestType(pBuf, cbRead, &digestType); + + if (pBuf) + RTMemFree(pBuf); + + if (RT_FAILURE(vrc)) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Could not verify supported digest types in the manifest file '%s' (%Rrc)"), + RTPathFilename(strMfFile.c_str()), vrc); + } + + storage.fCreateDigest = true; + + if (digestType == RTDIGESTTYPE_SHA256) + { + storage.fSha256 = true; + } + + Utf8Str name = applianceIOName(applianceIOFile); + + vrc = VDInterfaceAdd(&pFileIo->Core, name.c_str(), + VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), + &storage.pVDImageIfaces); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); + + rc = readFSImpl(pTask, pTask->locInfo.strPath, pShaIo, &storage); + if (FAILED(rc)) + break; + } + else + { + throw setError(VBOX_E_FILE_ERROR, + tr("Could not open the manifest file '%s' (%Rrc)"), + RTPathFilename(strMfFile.c_str()), vrc); + } + } + else + { + storage.fCreateDigest = false; + rc = readFSImpl(pTask, pTask->locInfo.strPath, pFileIo, &storage); + if (FAILED(rc)) + break; + } } - SHASTORAGE storage; - RT_ZERO(storage); - int vrc = VDInterfaceAdd(&pFileIo->Core, "Appliance::IOFile", - VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), - &storage.pVDImageIfaces); - if (RT_FAILURE(vrc)) + catch (HRESULT rc2) { - rc = setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); - break; + rc = rc2; } - rc = readFSImpl(pTask, pTask->locInfo.strPath, pShaIo, &storage); - }while(0); + }while (0); /* Cleanup */ if (pShaIo) @@ -866,51 +1074,79 @@ HRESULT Appliance::readFSOVA(TaskOVF *pTask) LogFlowFuncEnter(); RTTAR tar; - int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); - if (RT_FAILURE(vrc)) - return setError(VBOX_E_FILE_ERROR, - tr("Could not open OVA file '%s' (%Rrc)"), - pTask->locInfo.strPath.c_str(), vrc); - HRESULT rc = S_OK; - + int vrc = 0; PVDINTERFACEIO pShaIo = 0; PVDINTERFACEIO pTarIo = 0; char *pszFilename = 0; - do + SHASTORAGE storage; + + RT_ZERO(storage); + + vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); + if (RT_FAILURE(vrc)) + rc = setError(VBOX_E_FILE_ERROR, + tr("Could not open the OVA file '%s' (%Rrc)"), + pTask->locInfo.strPath.c_str(), vrc); + else { - vrc = RTTarCurrentFile(tar, &pszFilename); - if (RT_FAILURE(vrc)) - { - rc = VBOX_E_FILE_ERROR; - break; - } - pShaIo = ShaCreateInterface(); - if (!pShaIo) - { - rc = E_OUTOFMEMORY; - break; - } - pTarIo = TarCreateInterface(); - if (!pTarIo) - { - rc = E_OUTOFMEMORY; - break; - } - SHASTORAGE storage; - RT_ZERO(storage); - vrc = VDInterfaceAdd(&pTarIo->Core, "Appliance::IOTar", - VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO), - &storage.pVDImageIfaces); - if (RT_FAILURE(vrc)) + do { - rc = setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); - break; - } - rc = readFSImpl(pTask, pszFilename, pShaIo, &storage); - }while(0); + vrc = RTTarCurrentFile(tar, &pszFilename); + if (RT_FAILURE(vrc)) + { + rc = VBOX_E_FILE_ERROR; + break; + } + + Utf8Str extension(RTPathExt(pszFilename)); + + if (!extension.endsWith(".ovf",Utf8Str::CaseInsensitive)) + { + vrc = VERR_FILE_NOT_FOUND; + rc = setError(VBOX_E_FILE_ERROR, + tr("First file in the OVA package must have the extension 'ovf'. " + "But the file '%s' has the different extension (%Rrc)"), + pszFilename, + vrc); + break; + } + + pTarIo = TarCreateInterface(); + if (!pTarIo) + { + rc = E_OUTOFMEMORY; + break; + } + + pShaIo = ShaCreateInterface(); + if (!pShaIo) + { + rc = E_OUTOFMEMORY; + break ; + } + + Utf8Str name = applianceIOName(applianceIOTar); + + vrc = VDInterfaceAdd(&pTarIo->Core, name.c_str(), + VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO), + &storage.pVDImageIfaces); + if (RT_FAILURE(vrc)) + { + rc = setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); + break; + } + + rc = readFSImpl(pTask, pszFilename, pShaIo, &storage); + if (FAILED(rc)) + break; + + } while (0); + + RTTarClose(tar); + } + - RTTarClose(tar); /* Cleanup */ if (pszFilename) @@ -940,15 +1176,49 @@ HRESULT Appliance::readFSImpl(TaskOVF *pTask, const RTCString &strFilename, PVDI /* Read the OVF into a memory buffer */ size_t cbSize = 0; int vrc = ShaReadBuf(strFilename.c_str(), &pvTmpBuf, &cbSize, pIfIo, pStorage); - if ( RT_FAILURE(vrc) + if (RT_FAILURE(vrc) || !pvTmpBuf) throw setError(VBOX_E_FILE_ERROR, tr("Could not read OVF file '%s' (%Rrc)"), RTPathFilename(strFilename.c_str()), vrc); - /* Copy the SHA1/SHA256 sum of the OVF file for later validation */ - m->strOVFSHADigest = pStorage->strDigest; + /* Read & parse the XML structure of the OVF file */ m->pReader = new ovf::OVFReader(pvTmpBuf, cbSize, pTask->locInfo.strPath); + + if (m->pReader->m_envelopeData.getOVFVersion() == ovf::OVFVersion_2_0) + { + m->fSha256 = true; + + uint8_t digest[RTSHA256_HASH_SIZE]; + size_t cbDigest = RTSHA256_DIGEST_LEN; + char *pszDigest; + + RTSha256(pvTmpBuf, cbSize, &digest[0]); + + vrc = RTStrAllocEx(&pszDigest, cbDigest + 1); + if (RT_SUCCESS(vrc)) + vrc = RTSha256ToString(digest, pszDigest, cbDigest + 1); + else + throw setError(VBOX_E_FILE_ERROR, + tr("Could not allocate string for SHA256 digest (%Rrc)"), vrc); + + if (RT_SUCCESS(vrc)) + /* Copy the SHA256 sum of the OVF file for later validation */ + m->strOVFSHADigest = pszDigest; + else + throw setError(VBOX_E_FILE_ERROR, + tr("Converting SHA256 digest to a string was failed (%Rrc)"), vrc); + + RTStrFree(pszDigest); + + } + else + { + m->fSha256 = false; + /* Copy the SHA1 sum of the OVF file for later validation */ + m->strOVFSHADigest = pStorage->strDigest; + } + } catch (RTCError &x) // includes all XML exceptions { @@ -1017,7 +1287,11 @@ HRESULT Appliance::readS3(TaskOVF *pTask) strTmpOvf = Utf8StrFmt("%s/%s", pszTmpDir, RTPathFilename(tmpPath.c_str())); /* Next we have to download the OVF */ - vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); + vrc = RTS3Create(&hS3, + pTask->locInfo.strUsername.c_str(), + pTask->locInfo.strPassword.c_str(), + pTask->locInfo.strHostname.c_str(), + "virtualbox-agent/"VBOX_VERSION_STRING); if (RT_FAILURE(vrc)) throw setError(VBOX_E_IPRT_ERROR, tr("Cannot create S3 service handler")); @@ -1032,7 +1306,8 @@ HRESULT Appliance::readS3(TaskOVF *pTask) throw S_OK; /* todo: !!!!!!!!!!!!! */ else if (vrc == VERR_S3_ACCESS_DENIED) throw setError(E_ACCESSDENIED, - tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that your credentials are right." + tr("Cannot download file '%s' from S3 storage server (Access denied). Make sure that " + "your credentials are right. " "Also check that your host clock is properly synced"), pszFilename); else if (vrc == VERR_S3_NOT_FOUND) @@ -1148,9 +1423,12 @@ HRESULT Appliance::importImpl(const LocationInfo &locInfo, } /** - * Actual worker code for importing OVF data into VirtualBox. This is called from Appliance::taskThreadImportOrExport() - * and therefore runs on the OVF import worker thread. This creates one or more new machines according to the - * VirtualSystemScription instances created by Appliance::Interpret(). + * Actual worker code for importing OVF data into VirtualBox. + * + * This is called from Appliance::taskThreadImportOrExport() and therefore runs + * on the OVF import worker thread. This creates one or more new machines + * according to the VirtualSystemScription instances created by + * Appliance::Interpret(). * * This runs in three contexts: * @@ -1162,8 +1440,8 @@ HRESULT Appliance::importImpl(const LocationInfo &locInfo, * 3) in a second worker thread; in that case, Appliance::ImportMachines() called Appliance::importImpl(), which * called Appliance::importS3(), which called Appliance::importImpl(), which then called this again. * - * @param pTask - * @return + * @param pTask The OVF task data. + * @return COM status code. */ HRESULT Appliance::importFS(TaskOVF *pTask) { @@ -1199,6 +1477,7 @@ HRESULT Appliance::importFS(TaskOVF *pTask) /* With _whatever_ error we've had, do a complete roll-back of * machines and disks we've created */ writeLock.release(); + ErrorInfoKeeper eik; for (list<Guid>::iterator itID = m->llGuidsMachinesCreated.begin(); itID != m->llGuidsMachinesCreated.end(); ++itID) @@ -1212,7 +1491,7 @@ HRESULT Appliance::importFS(TaskOVF *pTask) SafeIfaceArray<IMedium> aMedia; rc2 = failedMachine->Unregister(CleanupMode_DetachAllReturnHardDisksOnly, ComSafeArrayAsOutParam(aMedia)); ComPtr<IProgress> pProgress2; - rc2 = failedMachine->Delete(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam()); + rc2 = failedMachine->DeleteConfig(ComSafeArrayAsInParam(aMedia), pProgress2.asOutParam()); pProgress2->WaitForCompletion(-1); } } @@ -1237,6 +1516,7 @@ HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) PVDINTERFACEIO pShaIo = NULL; PVDINTERFACEIO pFileIo = NULL; void *pvMfBuf = NULL; + void *pvCertBuf = NULL; writeLock.release(); try { @@ -1246,39 +1526,69 @@ HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) throw setError(E_OUTOFMEMORY); Utf8Str strMfFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".mf"); + + SHASTORAGE storage; + RT_ZERO(storage); + + Utf8Str name = applianceIOName(applianceIOFile); + + int vrc = VDInterfaceAdd(&pFileIo->Core, name.c_str(), + VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), + &storage.pVDImageIfaces); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); + /* Create the import stack for the rollback on errors. */ ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress); if (RTFileExists(strMfFile.c_str())) { - SHASTORAGE storage; - RT_ZERO(storage); - pShaIo = ShaCreateInterface(); if (!pShaIo) throw setError(E_OUTOFMEMORY); + Utf8Str nameSha = applianceIOName(applianceIOSha); + /* Fill out interface descriptor. */ + pShaIo->Core.u32Magic = VDINTERFACE_MAGIC; + pShaIo->Core.cbSize = sizeof(VDINTERFACEIO); + pShaIo->Core.pszInterfaceName = nameSha.c_str(); + pShaIo->Core.enmInterface = VDINTERFACETYPE_IO; + pShaIo->Core.pvUser = &storage; + pShaIo->Core.pNext = NULL; + storage.fCreateDigest = true; - int vrc = VDInterfaceAdd(&pFileIo->Core, "Appliance::IOFile", - VDINTERFACETYPE_IO, 0, sizeof(VDINTERFACEIO), - &storage.pVDImageIfaces); - if (RT_FAILURE(vrc)) - throw setError(VBOX_E_IPRT_ERROR, "Creation of the VD interface failed (%Rrc)", vrc); size_t cbMfSize = 0; - storage.fCreateDigest = true; + /* Now import the appliance. */ importMachines(stack, pShaIo, &storage); /* Read & verify the manifest file. */ /* Add the ovf file to the digest list. */ stack.llSrcDisksDigest.push_front(STRPAIR(pTask->locInfo.strPath, m->strOVFSHADigest)); - rc = readManifestFile(strMfFile, &pvMfBuf, &cbMfSize, pShaIo, &storage); + rc = readFileToBuf(strMfFile, &pvMfBuf, &cbMfSize, true, pShaIo, &storage); if (FAILED(rc)) throw rc; rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize); if (FAILED(rc)) throw rc; + + size_t cbCertSize = 0; + + /* Save the SHA digest of the manifest file for the next validation */ + Utf8Str manifestShaDigest = storage.strDigest; + + Utf8Str strCertFile = Utf8Str(pTask->locInfo.strPath).stripExt().append(".cert"); + if (RTFileExists(strCertFile.c_str())) + { + rc = readFileToBuf(strCertFile, &pvCertBuf, &cbCertSize, false, pShaIo, &storage); + if (FAILED(rc)) throw rc; + + /* verify Certificate */ + } } else - importMachines(stack, pFileIo, NULL); + { + storage.fCreateDigest = false; + importMachines(stack, pFileIo, &storage); + } } catch (HRESULT rc2) { @@ -1289,6 +1599,8 @@ HRESULT Appliance::importFSOVF(TaskOVF *pTask, AutoWriteLockBase& writeLock) /* Cleanup */ if (pvMfBuf) RTMemFree(pvMfBuf); + if (pvCertBuf) + RTMemFree(pvCertBuf); if (pShaIo) RTMemFree(pShaIo); if (pFileIo) @@ -1305,7 +1617,9 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) LogFlowFuncEnter(); RTTAR tar; - int vrc = RTTarOpen(&tar, pTask->locInfo.strPath.c_str(), RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); + int vrc = RTTarOpen(&tar, + pTask->locInfo.strPath.c_str(), + RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE, true); if (RT_FAILURE(vrc)) return setError(VBOX_E_FILE_ERROR, tr("Could not open OVA file '%s' (%Rrc)"), @@ -1317,6 +1631,9 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) PVDINTERFACEIO pTarIo = 0; char *pszFilename = 0; void *pvMfBuf = 0; + void *pvCertBuf = 0; + Utf8Str OVFfilename; + writeLock.release(); try { @@ -1330,25 +1647,66 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) SHASTORAGE storage; RT_ZERO(storage); - vrc = VDInterfaceAdd(&pTarIo->Core, "Appliance::IOTar", + + Utf8Str nameTar = applianceIOName(applianceIOTar); + + vrc = VDInterfaceAdd(&pTarIo->Core, nameTar.c_str(), VDINTERFACETYPE_IO, tar, sizeof(VDINTERFACEIO), &storage.pVDImageIfaces); if (RT_FAILURE(vrc)) throw setError(VBOX_E_IPRT_ERROR, tr("Creation of the VD interface failed (%Rrc)"), vrc); + Utf8Str nameSha = applianceIOName(applianceIOSha); + /* Fill out interface descriptor. */ + pShaIo->Core.u32Magic = VDINTERFACE_MAGIC; + pShaIo->Core.cbSize = sizeof(VDINTERFACEIO); + pShaIo->Core.pszInterfaceName = nameSha.c_str(); + pShaIo->Core.enmInterface = VDINTERFACETYPE_IO; + pShaIo->Core.pvUser = &storage; + pShaIo->Core.pNext = NULL; + /* Read the file name of the first file (need to be the ovf file). This * is how all internal files are named. */ vrc = RTTarCurrentFile(tar, &pszFilename); if (RT_FAILURE(vrc)) throw setError(VBOX_E_IPRT_ERROR, - tr("Getting the current file within the archive failed (%Rrc)"), vrc); + tr("Getting the OVF file within the archive failed (%Rrc)"), vrc); + else + { + if (vrc == VINF_TAR_DIR_PATH) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Empty directory folder (%s) isn't allowed in the OVA package (%Rrc)"), + pszFilename, + vrc); + } + } + + /* save original OVF filename */ + OVFfilename = pszFilename; + size_t cbMfSize = 0; + size_t cbCertSize = 0; + Utf8Str strMfFile = (Utf8Str(pszFilename)).stripExt().append(".mf"); + Utf8Str strCertFile = (Utf8Str(pszFilename)).stripExt().append(".cert"); + /* Skip the OVF file, cause this was read in IAppliance::Read already. */ vrc = RTTarSeekNextFile(tar); if ( RT_FAILURE(vrc) && vrc != VERR_TAR_END_OF_FILE) throw setError(VBOX_E_IPRT_ERROR, tr("Seeking within the archive failed (%Rrc)"), vrc); + else + { + RTTarCurrentFile(tar, &pszFilename); + if (vrc == VINF_TAR_DIR_PATH) + { + throw setError(VBOX_E_FILE_ERROR, + tr("Empty directory folder (%s) isn't allowed in the OVA package (%Rrc)"), + pszFilename, + vrc); + } + } PVDINTERFACEIO pCallbacks = pShaIo; PSHASTORAGE pStorage = &storage; @@ -1357,8 +1715,6 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) * is a manifest file in the stream. */ pStorage->fCreateDigest = true; - size_t cbMfSize = 0; - Utf8Str strMfFile = Utf8Str(pszFilename).stripExt().append(".mf"); /* Create the import stack for the rollback on errors. */ ImportStack stack(pTask->locInfo, m->pReader->m_mapDisks, pTask->pProgress); /* @@ -1373,23 +1729,68 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) * searching for it. We have to try to open it on all possible places. * If it fails here, we will try it again after all disks where read. */ - rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage); + rc = readTarFileToBuf(tar, strMfFile, &pvMfBuf, &cbMfSize, true, pCallbacks, pStorage); if (FAILED(rc)) throw rc; + + /* + * Try to read the certificate file. First try. + * Logic is the same as with manifest file + * Only if the manifest file had been read successfully before + */ + vrc = RTTarCurrentFile(tar, &pszFilename); + if (RT_SUCCESS(vrc)) + { + if (pvMfBuf) + { + if (strCertFile.compare(pszFilename) == 0) + { + rc = readTarFileToBuf(tar, strCertFile, &pvCertBuf, &cbCertSize, false, pCallbacks, pStorage); + if (FAILED(rc)) throw rc; + + if (pvCertBuf) + { + /* verify the certificate */ + } + } + } + } + /* Now import the appliance. */ importMachines(stack, pCallbacks, pStorage); /* Try to read the manifest file. Second try. */ if (!pvMfBuf) { - rc = readTarManifestFile(tar, strMfFile, &pvMfBuf, &cbMfSize, pCallbacks, pStorage); - if (FAILED(rc)) throw rc; - } - /* If we were able to read a manifest file we can check it now. */ - if (pvMfBuf) - { - /* Add the ovf file to the digest list. */ - stack.llSrcDisksDigest.push_front(STRPAIR(Utf8Str(pszFilename).stripExt().append(".ovf"), m->strOVFSHADigest)); - rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize); + rc = readTarFileToBuf(tar, strMfFile, &pvMfBuf, &cbMfSize, true, pCallbacks, pStorage); if (FAILED(rc)) throw rc; + + /* If we were able to read a manifest file we can check it now. */ + if (pvMfBuf) + { + /* Add the ovf file to the digest list. */ + stack.llSrcDisksDigest.push_front(STRPAIR(OVFfilename, m->strOVFSHADigest)); + rc = verifyManifestFile(strMfFile, stack, pvMfBuf, cbMfSize); + if (FAILED(rc)) throw rc; + + /* + * Try to read the certificate file. Second try. + * Only if the manifest file had been read successfully before + */ + + vrc = RTTarCurrentFile(tar, &pszFilename); + if (RT_SUCCESS(vrc)) + { + if (strCertFile.compare(pszFilename) == 0) + { + rc = readTarFileToBuf(tar, strCertFile, &pvCertBuf, &cbCertSize, false, pCallbacks, pStorage); + if (FAILED(rc)) throw rc; + + if (pvCertBuf) + { + /* verify the certificate */ + } + } + } + } } } catch (HRESULT rc2) @@ -1409,6 +1810,8 @@ HRESULT Appliance::importFSOVA(TaskOVF *pTask, AutoWriteLockBase& writeLock) RTMemFree(pShaIo); if (pTarIo) RTMemFree(pTarIo); + if (pvCertBuf) + RTMemFree(pvCertBuf); LogFlowFunc(("rc=%Rhrc\n", rc)); LogFlowFuncLeave(); @@ -1481,7 +1884,11 @@ HRESULT Appliance::importS3(TaskOVF *pTask) } /* Next we have to download the disk images */ - vrc = RTS3Create(&hS3, pTask->locInfo.strUsername.c_str(), pTask->locInfo.strPassword.c_str(), pTask->locInfo.strHostname.c_str(), "virtualbox-agent/"VBOX_VERSION_STRING); + vrc = RTS3Create(&hS3, + pTask->locInfo.strUsername.c_str(), + pTask->locInfo.strPassword.c_str(), + pTask->locInfo.strHostname.c_str(), + "virtualbox-agent/"VBOX_VERSION_STRING); if (RT_FAILURE(vrc)) throw setError(VBOX_E_IPRT_ERROR, tr("Cannot create S3 service handler")); @@ -1506,7 +1913,8 @@ HRESULT Appliance::importS3(TaskOVF *pTask) else if (vrc == VERR_S3_ACCESS_DENIED) throw setError(E_ACCESSDENIED, tr("Cannot download file '%s' from S3 storage server (Access denied). " - "Make sure that your credentials are right. Also check that your host clock is properly synced"), + "Make sure that your credentials are right. Also check that your host clock is " + "properly synced"), pszFilename); else if (vrc == VERR_S3_NOT_FOUND) throw setError(VBOX_E_FILE_ERROR, @@ -1542,7 +1950,8 @@ HRESULT Appliance::importS3(TaskOVF *pTask) else if (vrc == VERR_S3_ACCESS_DENIED) throw setError(E_ACCESSDENIED, tr("Cannot download file '%s' from S3 storage server (Access denied)." - "Make sure that your credentials are right. Also check that your host clock is properly synced"), + "Make sure that your credentials are right. " + "Also check that your host clock is properly synced"), pszFilename); else throw setError(VBOX_E_IPRT_ERROR, @@ -1609,24 +2018,35 @@ HRESULT Appliance::importS3(TaskOVF *pTask) } #endif /* VBOX_WITH_S3 */ -HRESULT Appliance::readManifestFile(const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) +HRESULT Appliance::readFileToBuf(const Utf8Str &strFile, + void **ppvBuf, + size_t *pcbSize, + bool fCreateDigest, + PVDINTERFACEIO pCallbacks, + PSHASTORAGE pStorage) { HRESULT rc = S_OK; - bool fOldDigest = pStorage->fCreateDigest; - pStorage->fCreateDigest = false; /* No digest for the manifest file */ + bool fOldDigest = pStorage->fCreateDigest;/* Save the old digest property */ + pStorage->fCreateDigest = fCreateDigest; int vrc = ShaReadBuf(strFile.c_str(), ppvBuf, pcbSize, pCallbacks, pStorage); if ( RT_FAILURE(vrc) && vrc != VERR_FILE_NOT_FOUND) rc = setError(VBOX_E_FILE_ERROR, - tr("Could not read manifest file '%s' (%Rrc)"), + tr("Could not read file '%s' (%Rrc)"), RTPathFilename(strFile.c_str()), vrc); pStorage->fCreateDigest = fOldDigest; /* Restore the old digest creation behavior again. */ return rc; } -HRESULT Appliance::readTarManifestFile(RTTAR tar, const Utf8Str &strFile, void **ppvBuf, size_t *pcbSize, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) +HRESULT Appliance::readTarFileToBuf(RTTAR tar, + const Utf8Str &strFile, + void **ppvBuf, + size_t *pcbSize, + bool fCreateDigest, + PVDINTERFACEIO pCallbacks, + PSHASTORAGE pStorage) { HRESULT rc = S_OK; @@ -1634,9 +2054,19 @@ HRESULT Appliance::readTarManifestFile(RTTAR tar, const Utf8Str &strFile, void * int vrc = RTTarCurrentFile(tar, &pszCurFile); if (RT_SUCCESS(vrc)) { - if (!strcmp(pszCurFile, RTPathFilename(strFile.c_str()))) - rc = readManifestFile(strFile, ppvBuf, pcbSize, pCallbacks, pStorage); - RTStrFree(pszCurFile); + if (vrc == VINF_TAR_DIR_PATH) + { + rc = setError(VBOX_E_FILE_ERROR, + tr("Empty directory folder (%s) isn't allowed in the OVA package (%Rrc)"), + pszCurFile, + vrc); + } + else + { + if (!strcmp(pszCurFile, RTPathFilename(strFile.c_str()))) + rc = readFileToBuf(strFile, ppvBuf, pcbSize, fCreateDigest, pCallbacks, pStorage); + RTStrFree(pszCurFile); + } } else if (vrc != VERR_TAR_END_OF_FILE) rc = setError(VBOX_E_IPRT_ERROR, "Seeking within the archive failed (%Rrc)", vrc); @@ -1665,7 +2095,7 @@ HRESULT Appliance::verifyManifestFile(const Utf8Str &strFile, ImportStack &stack int vrc = RTManifestVerifyFilesBuf(pvBuf, cbSize, paTests, stack.llSrcDisksDigest.size(), &iFailed); if (RT_UNLIKELY(vrc == VERR_MANIFEST_DIGEST_MISMATCH)) rc = setError(VBOX_E_FILE_ERROR, - tr("The SHA1 digest of '%s' does not match the one in '%s' (%Rrc)"), + tr("The SHA digest of '%s' does not match the one in '%s' (%Rrc)"), RTPathFilename(paTests[iFailed].pszTestFile), RTPathFilename(strFile.c_str()), vrc); else if (RT_FAILURE(vrc)) rc = setError(VBOX_E_FILE_ERROR, @@ -1677,7 +2107,6 @@ HRESULT Appliance::verifyManifestFile(const Utf8Str &strFile, ImportStack &stack return rc; } - /** * Helper that converts VirtualSystem attachment values into VirtualBox attachment values. * Throws HRESULT values on errors! @@ -1694,7 +2123,10 @@ void Appliance::convertDiskAttachmentValues(const ovf::HardDiskController &hdc, int32_t &lControllerPort, int32_t &lDevice) { - Log(("Appliance::convertDiskAttachmentValues: hdc.system=%d, hdc.fPrimary=%d, ulAddressOnParent=%d\n", hdc.system, hdc.fPrimary, ulAddressOnParent)); + Log(("Appliance::convertDiskAttachmentValues: hdc.system=%d, hdc.fPrimary=%d, ulAddressOnParent=%d\n", + hdc.system, + hdc.fPrimary, + ulAddressOnParent)); switch (hdc.system) { @@ -1790,31 +2222,57 @@ void Appliance::convertDiskAttachmentValues(const ovf::HardDiskController &hdc, * This advances stack.pProgress by one operation with the disk's weight. * * @param di ovfreader.cpp structure describing the disk image from the OVF that is to be imported - * @param ulSizeMB Size of the disk image (for progress reporting) * @param strTargetPath Where to create the target image. * @param pTargetHD out: The newly created target disk. This also gets pushed on stack.llHardDisksCreated for cleanup. * @param stack */ void Appliance::importOneDiskImage(const ovf::DiskImage &di, - const Utf8Str &strTargetPath, + Utf8Str *strTargetPath, ComObjPtr<Medium> &pTargetHD, ImportStack &stack, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) { + SHASTORAGE finalStorage; + PSHASTORAGE pRealUsedStorage = pStorage;/* may be changed later to finalStorage */ + PVDINTERFACEIO pFileIo = NULL;/* used in GZIP case*/ ComObjPtr<Progress> pProgress; pProgress.createObject(); - HRESULT rc = pProgress->init(mVirtualBox, static_cast<IAppliance*>(this), BstrFmt(tr("Creating medium '%s'"), strTargetPath.c_str()).raw(), TRUE); + HRESULT rc = pProgress->init(mVirtualBox, + static_cast<IAppliance*>(this), + BstrFmt(tr("Creating medium '%s'"), + strTargetPath->c_str()).raw(), + TRUE); if (FAILED(rc)) throw rc; /* Get the system properties. */ SystemProperties *pSysProps = mVirtualBox->getSystemProperties(); + /* + * we put strSourceOVF into the stack.llSrcDisksDigest in the end of this + * function like a key for a later validation of the SHA digests + */ + const Utf8Str &strSourceOVF = di.strHref; + + Utf8Str strSrcFilePath(stack.strSourceDir); + Utf8Str strTargetDir(*strTargetPath); + + /* Construct source file path */ + Utf8Str name = applianceIOName(applianceIOTar); + + if (RTStrNICmp(pStorage->pVDImageIfaces->pszInterfaceName, name.c_str(), name.length()) == 0) + strSrcFilePath = strSourceOVF; + else + { + strSrcFilePath.append(RTPATH_SLASH_STR); + strSrcFilePath.append(strSourceOVF); + } + /* First of all check if the path is an UUID. If so, the user like to * import the disk into an existing path. This is useful for iSCSI for * example. */ RTUUID uuid; - int vrc = RTUuidFromStr(&uuid, strTargetPath.c_str()); + int vrc = RTUuidFromStr(&uuid, strTargetPath->c_str()); if (vrc == VINF_SUCCESS) { rc = mVirtualBox->findHardDiskById(Guid(uuid), true, &pTargetHD); @@ -1822,102 +2280,248 @@ void Appliance::importOneDiskImage(const ovf::DiskImage &di, } else { - Utf8Str strTrgFormat = "VMDK"; - if (RTPathHaveExt(strTargetPath.c_str())) + bool fGzipUsed = !(di.strCompression.compare("gzip",Utf8Str::CaseInsensitive)); + /* check read file to GZIP compression */ + try { - char *pszExt = RTPathExt(strTargetPath.c_str()); - /* Figure out which format the user like to have. Default is VMDK. */ - ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension(&pszExt[1]); - if (trgFormat.isNull()) - throw setError(VBOX_E_NOT_SUPPORTED, - tr("Could not find a valid medium format for the target disk '%s'"), - strTargetPath.c_str()); - /* Check the capabilities. We need create capabilities. */ + if (fGzipUsed == true) + { + /* + * Create the necessary file access interfaces. + * For the next step: + * We need to replace the previously created chain of SHA-TAR or SHA-FILE interfaces + * with simple FILE interface because we don't need SHA or TAR interfaces here anymore. + * But we mustn't delete the chain of SHA-TAR or SHA-FILE interfaces. + */ + + /* Decompress the GZIP file and save a new file in the target path */ + strTargetDir = strTargetDir.stripFilename(); + strTargetDir.append("/temp_"); + + Utf8Str strTempTargetFilename(*strTargetPath); + strTempTargetFilename = strTempTargetFilename.stripPath(); + strTempTargetFilename = strTempTargetFilename.stripExt(); + Utf8Str vdf = typeOfVirtualDiskFormatFromURI(di.strFormat); + + strTargetDir.append(strTempTargetFilename); + + vrc = decompressImageAndSave(strSrcFilePath.c_str(), strTargetDir.c_str(), pCallbacks, pStorage); + + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_FILE_ERROR, + tr("Could not read the file '%s' (%Rrc)"), + RTPathFilename(strSrcFilePath.c_str()), vrc); + + /* Create the necessary file access interfaces. */ + pFileIo = FileCreateInterface(); + if (!pFileIo) + throw setError(E_OUTOFMEMORY); + + name = applianceIOName(applianceIOFile); + + vrc = VDInterfaceAdd(&pFileIo->Core, name.c_str(), + VDINTERFACETYPE_IO, NULL, sizeof(VDINTERFACEIO), + &finalStorage.pVDImageIfaces); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_IPRT_ERROR, + tr("Creation of the VD interface failed (%Rrc)"), vrc); + + /* Correct the source and the target with the actual values */ + strSrcFilePath = strTargetDir; + strTargetDir = strTargetDir.stripFilename(); + strTargetDir.append(RTPATH_SLASH_STR); + strTargetDir.append(strTempTargetFilename.c_str()); + *strTargetPath = strTargetDir.c_str(); + + pRealUsedStorage = &finalStorage; + } + + Utf8Str strTrgFormat = "VMDK"; ULONG lCabs = 0; - rc = trgFormat->COMGETTER(Capabilities)(&lCabs); - if (FAILED(rc)) throw rc; - if (!( ((lCabs & MediumFormatCapabilities_CreateFixed) == MediumFormatCapabilities_CreateFixed) - || ((lCabs & MediumFormatCapabilities_CreateDynamic) == MediumFormatCapabilities_CreateDynamic))) - throw setError(VBOX_E_NOT_SUPPORTED, - tr("Could not find a valid medium format for the target disk '%s'"), - strTargetPath.c_str()); - Bstr bstrFormatName; - rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); - if (FAILED(rc)) throw rc; - strTrgFormat = Utf8Str(bstrFormatName); - } + char *pszExt = NULL; - /* Create an IMedium object. */ - pTargetHD.createObject(); - rc = pTargetHD->init(mVirtualBox, - strTrgFormat, - strTargetPath, - Guid::Empty /* media registry: none yet */); - if (FAILED(rc)) throw rc; + if (RTPathHaveExt(strTargetPath->c_str())) + { + pszExt = RTPathExt(strTargetPath->c_str()); + /* Figure out which format the user like to have. Default is VMDK. */ + ComObjPtr<MediumFormat> trgFormat = pSysProps->mediumFormatFromExtension(&pszExt[1]); + if (trgFormat.isNull()) + throw setError(VBOX_E_NOT_SUPPORTED, + tr("Could not find a valid medium format for the target disk '%s'"), + strTargetPath->c_str()); + /* Check the capabilities. We need create capabilities. */ + lCabs = 0; + com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap; + rc = trgFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap)); + + if (FAILED(rc)) + throw rc; + else + { + for (ULONG j = 0; j < mediumFormatCap.size(); j++) + lCabs |= mediumFormatCap[j]; + } - /* Now create an empty hard disk. */ - rc = mVirtualBox->CreateHardDisk(NULL, - Bstr(strTargetPath).raw(), - ComPtr<IMedium>(pTargetHD).asOutParam()); - if (FAILED(rc)) throw rc; - } + if (!( ((lCabs & MediumFormatCapabilities_CreateFixed) == MediumFormatCapabilities_CreateFixed) + || ((lCabs & MediumFormatCapabilities_CreateDynamic) == MediumFormatCapabilities_CreateDynamic))) + throw setError(VBOX_E_NOT_SUPPORTED, + tr("Could not find a valid medium format for the target disk '%s'"), + strTargetPath->c_str()); + Bstr bstrFormatName; + rc = trgFormat->COMGETTER(Name)(bstrFormatName.asOutParam()); + if (FAILED(rc)) throw rc; + strTrgFormat = Utf8Str(bstrFormatName); + } + else + { + throw setError(VBOX_E_FILE_ERROR, + tr("The target disk '%s' has no extension "), + strTargetPath->c_str(), VERR_INVALID_NAME); + } - const Utf8Str &strSourceOVF = di.strHref; - /* Construct source file path */ - Utf8StrFmt strSrcFilePath("%s%c%s", stack.strSourceDir.c_str(), RTPATH_DELIMITER, strSourceOVF.c_str()); + /* Create an IMedium object. */ + pTargetHD.createObject(); - /* If strHref is empty we have to create a new file. */ - if (strSourceOVF.isEmpty()) - { - /* Create a dynamic growing disk image with the given capacity. */ - rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, MediumVariant_Standard, ComPtr<IProgress>(pProgress).asOutParam()); - if (FAILED(rc)) throw rc; + /*CD/DVD case*/ + if (strTrgFormat.compare("RAW", Utf8Str::CaseInsensitive) == 0) + { + try + { + if (fGzipUsed == true) + { + /* + * The source and target pathes are the same. + * It means that we have the needed file already. + * For example, in GZIP case, we decompress the file and save it in the target path, + * but with some prefix like "temp_". See part "check read file to GZIP compression" earlier + * in this function. + * Just rename the file by deleting "temp_" from it's name + */ + vrc = RTFileRename(strSrcFilePath.c_str(), strTargetPath->c_str(), RTPATHRENAME_FLAGS_NO_REPLACE); + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_FILE_ERROR, + tr("Could not rename the file '%s' (%Rrc)"), + RTPathFilename(strSourceOVF.c_str()), vrc); + } + else + { + /* Calculating SHA digest for ISO file while copying one */ + vrc = copyFileAndCalcShaDigest(strSrcFilePath.c_str(), + strTargetPath->c_str(), + pCallbacks, + pRealUsedStorage); + + if (RT_FAILURE(vrc)) + throw setError(VBOX_E_FILE_ERROR, + tr("Could not copy ISO file '%s' listed in the OVF file (%Rrc)"), + RTPathFilename(strSourceOVF.c_str()), vrc); + } + } + catch (HRESULT /*arc*/) + { + throw; + } - /* Advance to the next operation. */ - stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"), strTargetPath.c_str()).raw(), - di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally - } - else - { - /* We need a proper source format description */ - ComObjPtr<MediumFormat> srcFormat; - /* Which format to use? */ - Utf8Str strSrcFormat = "VDI"; - if ( di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#sparse", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#streamOptimized", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) - || di.strFormat.compare("http://www.vmware.com/interfaces/specifications/vmdk.html#compressed", Utf8Str::CaseInsensitive) - ) - strSrcFormat = "VMDK"; - srcFormat = pSysProps->mediumFormat(strSrcFormat); - if (srcFormat.isNull()) - throw setError(VBOX_E_NOT_SUPPORTED, - tr("Could not find a valid medium format for the source disk '%s'"), - RTPathFilename(strSrcFilePath.c_str())); - - /* Clone the source disk image */ - ComObjPtr<Medium> nullParent; - rc = pTargetHD->importFile(strSrcFilePath.c_str(), - srcFormat, - MediumVariant_Standard, - pCallbacks, pStorage, - nullParent, - pProgress); - if (FAILED(rc)) throw rc; + /* Advance to the next operation. */ + /* operation's weight, as set up with the IProgress originally */ + stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), + RTPathFilename(strSourceOVF.c_str())).raw(), + di.ulSuggestedSizeMB); + } + else/* HDD case*/ + { + rc = pTargetHD->init(mVirtualBox, + strTrgFormat, + *strTargetPath, + Guid::Empty /* media registry: none yet */); + if (FAILED(rc)) throw rc; - /* Advance to the next operation. */ - stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), RTPathFilename(strSrcFilePath.c_str())).raw(), - di.ulSuggestedSizeMB); // operation's weight, as set up with the IProgress originally); + /* Now create an empty hard disk. */ + rc = mVirtualBox->CreateHardDisk(Bstr(strTrgFormat).raw(), + Bstr(*strTargetPath).raw(), + ComPtr<IMedium>(pTargetHD).asOutParam()); + if (FAILED(rc)) throw rc; + + /* If strHref is empty we have to create a new file. */ + if (strSourceOVF.isEmpty()) + { + com::SafeArray<MediumVariant_T> mediumVariant; + mediumVariant.push_back(MediumVariant_Standard); + /* Create a dynamic growing disk image with the given capacity. */ + rc = pTargetHD->CreateBaseStorage(di.iCapacity / _1M, + ComSafeArrayAsInParam(mediumVariant), + ComPtr<IProgress>(pProgress).asOutParam()); + if (FAILED(rc)) throw rc; + + /* Advance to the next operation. */ + /* operation's weight, as set up with the IProgress originally */ + stack.pProgress->SetNextOperation(BstrFmt(tr("Creating disk image '%s'"), + strTargetPath->c_str()).raw(), + di.ulSuggestedSizeMB); + } + else + { + /* We need a proper source format description */ + /* Which format to use? */ + Utf8Str strSrcFormat = typeOfVirtualDiskFormatFromURI(di.strFormat); + + ComObjPtr<MediumFormat> srcFormat = pSysProps->mediumFormat(strSrcFormat); + if (srcFormat.isNull()) + throw setError(VBOX_E_NOT_SUPPORTED, + tr("Could not find a valid medium format for the source disk '%s' " + "Check correctness of the image format URL in the OVF description file."), + RTPathFilename(strSourceOVF.c_str())); + + /* Clone the source disk image */ + ComObjPtr<Medium> nullParent; + rc = pTargetHD->importFile(strSrcFilePath.c_str(), + srcFormat, + MediumVariant_Standard, + pCallbacks, pRealUsedStorage, + nullParent, + pProgress); + if (FAILED(rc)) throw rc; + + /* Advance to the next operation. */ + /* operation's weight, as set up with the IProgress originally */ + stack.pProgress->SetNextOperation(BstrFmt(tr("Importing virtual disk image '%s'"), + RTPathFilename(strSourceOVF.c_str())).raw(), + di.ulSuggestedSizeMB); + } + + /* Now wait for the background disk operation to complete; this throws + * HRESULTs on error. */ + ComPtr<IProgress> pp(pProgress); + waitForAsyncProgress(stack.pProgress, pp); + + if (fGzipUsed == true) + { + /* + * Just delete the temporary file + */ + vrc = RTFileDelete(strSrcFilePath.c_str()); + if (RT_FAILURE(vrc)) + setWarning(VBOX_E_FILE_ERROR, + tr("Could not delete the file '%s' (%Rrc)"), + RTPathFilename(strSrcFilePath.c_str()), vrc); + } + } + } + catch (...) + { + if (pFileIo) + RTMemFree(pFileIo); + + throw; + } } - /* Now wait for the background disk operation to complete; this throws - * HRESULTs on error. */ - ComPtr<IProgress> pp(pProgress); - waitForAsyncProgress(stack.pProgress, pp); + if (pFileIo) + RTMemFree(pFileIo); /* Add the newly create disk path + a corresponding digest the our list for * later manifest verification. */ - stack.llSrcDisksDigest.push_back(STRPAIR(strSrcFilePath, pStorage ? pStorage->strDigest : "")); + stack.llSrcDisksDigest.push_back(STRPAIR(strSourceOVF, pStorage ? pStorage->strDigest : "")); } /** @@ -1940,6 +2544,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) { + LogFlowFuncEnter(); HRESULT rc; // Get the instance of IGuestOSType which matches our string guest OS type so we @@ -2027,11 +2632,12 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, #ifdef VBOX_WITH_USB /* USB Controller */ - ComPtr<IUSBController> usbController; - rc = pNewMachine->COMGETTER(USBController)(usbController.asOutParam()); - if (FAILED(rc)) throw rc; - rc = usbController->COMSETTER(Enabled)(stack.fUSBEnabled); - if (FAILED(rc)) throw rc; + if (stack.fUSBEnabled) + { + ComPtr<IUSBController> usbController; + rc = pNewMachine->AddUSBController(Bstr("OHCI").raw(), USBControllerType_OHCI, usbController.asOutParam()); + if (FAILED(rc)) throw rc; + } #endif /* VBOX_WITH_USB */ /* Change the network adapters */ @@ -2049,7 +2655,8 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, } else if (vsdeNW.size() > maxNetworkAdapters) throw setError(VBOX_E_FILE_ERROR, - tr("Too many network adapters: OVF requests %d network adapters, but VirtualBox only supports %d"), + tr("Too many network adapters: OVF requests %d network adapters, " + "but VirtualBox only supports %d"), vsdeNW.size(), maxNetworkAdapters); else { @@ -2061,7 +2668,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, { const VirtualSystemDescriptionEntry* pvsys = *nwIt; - const Utf8Str &nwTypeVBox = pvsys->strVboxCurrent; + const Utf8Str &nwTypeVBox = pvsys->strVBoxCurrent; uint32_t tt1 = RTStrToUInt32(nwTypeVBox.c_str()); ComPtr<INetworkAdapter> pNetworkAdapter; rc = pNewMachine->GetNetworkAdapter((ULONG)a, pNetworkAdapter.asOutParam()); @@ -2099,7 +2706,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam()); if (FAILED(rc)) throw rc; /* Set the interface name to attach to */ - pNetworkAdapter->COMSETTER(BridgedInterface)(name.raw()); + rc = pNetworkAdapter->COMSETTER(BridgedInterface)(name.raw()); if (FAILED(rc)) throw rc; break; } @@ -2132,7 +2739,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, rc = nwInterfaces[j]->COMGETTER(Name)(name.asOutParam()); if (FAILED(rc)) throw rc; /* Set the interface name to attach to */ - pNetworkAdapter->COMSETTER(HostOnlyInterface)(name.raw()); + rc = pNetworkAdapter->COMSETTER(HostOnlyInterface)(name.raw()); if (FAILED(rc)) throw rc; break; } @@ -2152,13 +2759,37 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_Generic); if (FAILED(rc)) throw rc; } + /* Next test for NAT network interfaces */ + else if (pvsys->strExtraConfigCurrent.endsWith("type=NATNetwork", Utf8Str::CaseInsensitive)) + { + /* Attach to the right interface */ + rc = pNetworkAdapter->COMSETTER(AttachmentType)(NetworkAttachmentType_NATNetwork); + if (FAILED(rc)) throw rc; + com::SafeIfaceArray<INATNetwork> nwNATNetworks; + rc = mVirtualBox->COMGETTER(NATNetworks)(ComSafeArrayAsOutParam(nwNATNetworks)); + if (FAILED(rc)) throw rc; + // Pick the first NAT network (if there is any) + if (nwNATNetworks.size()) + { + Bstr name; + rc = nwNATNetworks[0]->COMGETTER(NetworkName)(name.asOutParam()); + if (FAILED(rc)) throw rc; + /* Set the NAT network name to attach to */ + rc = pNetworkAdapter->COMSETTER(NATNetwork)(name.raw()); + if (FAILED(rc)) throw rc; + break; + } + } } } // IDE Hard disk controller std::list<VirtualSystemDescriptionEntry*> vsdeHDCIDE = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskControllerIDE); - // In OVF (at least VMware's version of it), an IDE controller has two ports, so VirtualBox's single IDE controller - // with two channels and two ports each counts as two OVF IDE controllers -- so we accept one or two such IDE controllers + /* + * In OVF (at least VMware's version of it), an IDE controller has two ports, + * so VirtualBox's single IDE controller with two channels and two ports each counts as + * two OVF IDE controllers -- so we accept one or two such IDE controllers + */ size_t cIDEControllers = vsdeHDCIDE.size(); if (cIDEControllers > 2) throw setError(VBOX_E_FILE_ERROR, @@ -2170,7 +2801,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, rc = pNewMachine->AddStorageController(Bstr("IDE Controller").raw(), StorageBus_IDE, pController.asOutParam()); if (FAILED(rc)) throw rc; - const char *pcszIDEType = vsdeHDCIDE.front()->strVboxCurrent.c_str(); + const char *pcszIDEType = vsdeHDCIDE.front()->strVBoxCurrent.c_str(); if (!strcmp(pcszIDEType, "PIIX3")) rc = pController->COMSETTER(ControllerType)(StorageControllerType_PIIX3); else if (!strcmp(pcszIDEType, "PIIX4")) @@ -2192,10 +2823,12 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, if (vsdeHDCSATA.size() > 0) { ComPtr<IStorageController> pController; - const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVboxCurrent; + const Utf8Str &hdcVBox = vsdeHDCSATA.front()->strVBoxCurrent; if (hdcVBox == "AHCI") { - rc = pNewMachine->AddStorageController(Bstr("SATA Controller").raw(), StorageBus_SATA, pController.asOutParam()); + rc = pNewMachine->AddStorageController(Bstr("SATA Controller").raw(), + StorageBus_SATA, + pController.asOutParam()); if (FAILED(rc)) throw rc; } else @@ -2215,7 +2848,7 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, Bstr bstrName(L"SCSI Controller"); StorageBus_T busType = StorageBus_SCSI; StorageControllerType_T controllerType; - const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVboxCurrent; + const Utf8Str &hdcVBox = vsdeHDCSCSI.front()->strVBoxCurrent; if (hdcVBox == "LsiLogic") controllerType = StorageControllerType_LsiLogic; else if (hdcVBox == "LsiLogicSas") @@ -2246,7 +2879,9 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, if (vsdeHDCSAS.size() > 0) { ComPtr<IStorageController> pController; - rc = pNewMachine->AddStorageController(Bstr(L"SAS Controller").raw(), StorageBus_SAS, pController.asOutParam()); + rc = pNewMachine->AddStorageController(Bstr(L"SAS Controller").raw(), + StorageBus_SAS, + pController.asOutParam()); if (FAILED(rc)) throw rc; rc = pController->COMSETTER(ControllerType)(StorageControllerType_LsiLogicSas); if (FAILED(rc)) throw rc; @@ -2291,7 +2926,9 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, if (vsdeFloppy.size() == 1) { ComPtr<IStorageController> pController; - rc = sMachine->AddStorageController(Bstr("Floppy Controller").raw(), StorageBus_Floppy, pController.asOutParam()); + rc = sMachine->AddStorageController(Bstr("Floppy Controller").raw(), + StorageBus_Floppy, + pController.asOutParam()); if (FAILED(rc)) throw rc; Bstr bstrName; @@ -2317,54 +2954,6 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, stack.llHardDiskAttachments.push_back(mhda); } - // CD-ROMs next - for (std::list<VirtualSystemDescriptionEntry*>::const_iterator jt = vsdeCDROM.begin(); - jt != vsdeCDROM.end(); - ++jt) - { - // for now always attach to secondary master on IDE controller; - // there seems to be no useful information in OVF where else to - // attach it (@todo test with latest versions of OVF software) - - // find the IDE controller - const ovf::HardDiskController *pController = NULL; - for (ovf::ControllersMap::const_iterator kt = vsysThis.mapControllers.begin(); - kt != vsysThis.mapControllers.end(); - ++kt) - { - if (kt->second.system == ovf::HardDiskController::IDE) - { - pController = &kt->second; - break; - } - } - - if (!pController) - throw setError(VBOX_E_FILE_ERROR, - tr("OVF wants a CD-ROM drive but cannot find IDE controller, which is required in this version of VirtualBox")); - - // this is for rollback later - MyHardDiskAttachment mhda; - mhda.pMachine = pNewMachine; - - convertDiskAttachmentValues(*pController, - 2, // interpreted as secondary master - mhda.controllerType, // Bstr - mhda.lControllerPort, - mhda.lDevice); - - Log(("Attaching CD-ROM to port %d on device %d\n", mhda.lControllerPort, mhda.lDevice)); - - rc = sMachine->AttachDevice(mhda.controllerType.raw(), - mhda.lControllerPort, - mhda.lDevice, - DeviceType_DVD, - NULL); - if (FAILED(rc)) throw rc; - - stack.llHardDiskAttachments.push_back(mhda); - } // end for (itHD = avsdeHDs.begin(); - rc = sMachine->SaveSettings(); if (FAILED(rc)) throw rc; @@ -2373,12 +2962,17 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, if (FAILED(rc)) throw rc; stack.fSessionOpen = false; } - catch(HRESULT /* aRC */) + catch(HRESULT aRC) { + com::ErrorInfo info; + if (stack.fSessionOpen) stack.pSession->UnlockMachine(); - throw; + if (info.isFullAvailable()) + throw setError(aRC, Utf8Str(info.getText()).c_str()); + else + throw setError(aRC, "Unknown error during OVF import"); } } @@ -2390,37 +2984,190 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, // we need another try/catch block. try { +#ifdef LOG_ENABLED + if (LogIsEnabled()) + { + size_t i = 0; + for (list<VirtualSystemDescriptionEntry*>::const_iterator itHD = avsdeHDs.begin(); itHD != avsdeHDs.end(); ++itHD, i++) + Log(("avsdeHDs[%zu]: strRef=%s\n", i, (*itHD)->strRef.c_str())); + } +#endif + // to attach things we need to open a session for the new machine rc = pNewMachine->LockMachine(stack.pSession, LockType_Write); if (FAILED(rc)) throw rc; stack.fSessionOpen = true; - /* Iterate over all given disk images */ - list<VirtualSystemDescriptionEntry*>::const_iterator itHD; - for (itHD = avsdeHDs.begin(); - itHD != avsdeHDs.end(); - ++itHD) + /* get VM name from virtual system description. Only one record is possible (size of list is equal 1). */ + std::list<VirtualSystemDescriptionEntry*> vmName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); + std::list<VirtualSystemDescriptionEntry*>::iterator vmNameIt = vmName.begin(); + VirtualSystemDescriptionEntry* vmNameEntry = *vmNameIt; + + ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin(); + std::set<RTCString> disksResolvedNames; + + uint32_t cImportedDisks = 0; + + while(oit != stack.mapDisks.end() && cImportedDisks != avsdeHDs.size()) { - VirtualSystemDescriptionEntry *vsdeHD = *itHD; + ovf::DiskImage diCurrent = oit->second; + ovf::VirtualDisksMap::const_iterator itVDisk = vsysThis.mapVirtualDisks.begin(); + + VirtualSystemDescriptionEntry *vsdeTargetHD = 0; + Log(("diCurrent.strDiskId=%s\n", diCurrent.strDiskId.c_str())); + + /* + * + * Iterate over all given disk images of the virtual system + * disks description. We need to find the target disk path, + * which could be changed by the user. + * + */ + { + list<VirtualSystemDescriptionEntry*>::const_iterator itHD; + for (itHD = avsdeHDs.begin(); + itHD != avsdeHDs.end(); + ++itHD) + { + VirtualSystemDescriptionEntry *vsdeHD = *itHD; + if (vsdeHD->strRef == diCurrent.strDiskId) + { + vsdeTargetHD = vsdeHD; + break; + } + } + if (!vsdeTargetHD) + { + /* possible case if a disk image belongs to other virtual system (OVF package with multiple VMs inside) */ + LogWarning(("OVA/OVF import: Disk image %s was missed during import of VM %s\n", + oit->first.c_str(), vmNameEntry->strOvf.c_str())); + ++oit; + continue; + } + + //diCurrent.strDiskId contains the disk identifier (e.g. "vmdisk1"), which should exist + //in the virtual system's disks map under that ID and also in the global images map + itVDisk = vsysThis.mapVirtualDisks.find(diCurrent.strDiskId); + if (itVDisk == vsysThis.mapVirtualDisks.end()) + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'"), + diCurrent.strHref.c_str()); + } + + /* + * preliminary check availability of the image + * This step is useful if image is placed in the OVA (TAR) package + */ + + Utf8Str name = applianceIOName(applianceIOTar); + + if (strncmp(pStorage->pVDImageIfaces->pszInterfaceName, name.c_str(), name.length()) == 0) + { + /* It means that we possibly have imported the storage earlier on the previous loop steps*/ + std::set<RTCString>::const_iterator h = disksResolvedNames.find(diCurrent.strHref); + if (h != disksResolvedNames.end()) + { + /* Yes, disk name was found, we can skip it*/ + ++oit; + continue; + } + + RTCString availableImage(diCurrent.strHref); + + rc = preCheckImageAvailability(pStorage, + availableImage + ); + + if (SUCCEEDED(rc)) + { + /* current opened file isn't the same as passed one */ + if(availableImage.compare(diCurrent.strHref, Utf8Str::CaseInsensitive) != 0) + { + /* + * availableImage contains the disk file reference (e.g. "disk1.vmdk"), which should exist + * in the global images map. + * And find the disk from the OVF's disk list + * + */ + { + ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.begin(); + while (++itDiskImage != stack.mapDisks.end()) + { + if (itDiskImage->second.strHref.compare(availableImage, Utf8Str::CaseInsensitive) == 0) + break; + } + if (itDiskImage == stack.mapDisks.end()) + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'. " + "Check compliance OVA package structure and file names " + "references in the section <References> in the OVF file"), + availableImage.c_str()); + + /* replace with a new found disk image */ + diCurrent = *(&itDiskImage->second); + } + + /* + * Again iterate over all given disk images of the virtual system + * disks description using the found disk image + */ + { + list<VirtualSystemDescriptionEntry*>::const_iterator itHD; + for (itHD = avsdeHDs.begin(); + itHD != avsdeHDs.end(); + ++itHD) + { + VirtualSystemDescriptionEntry *vsdeHD = *itHD; + if (vsdeHD->strRef == diCurrent.strDiskId) + { + vsdeTargetHD = vsdeHD; + break; + } + } + if (!vsdeTargetHD) + /* + * in this case it's an error because something wrong with OVF description file. + * May be VBox imports OVA package with wrong file sequence inside the archive. + */ + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'"), + diCurrent.strHref.c_str()); + + itVDisk = vsysThis.mapVirtualDisks.find(diCurrent.strDiskId); + if (itVDisk == vsysThis.mapVirtualDisks.end()) + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'"), + diCurrent.strHref.c_str()); + } + } + else + { + ++oit; + } + } + else + { + ++oit; + continue; + } + } + else + { + /* just continue with normal files*/ + ++oit; + } - // vsdeHD->strRef contains the disk identifier (e.g. "vmdisk1"), which should exist - // in the virtual system's disks map under that ID and also in the global images map - ovf::VirtualDisksMap::const_iterator itVirtualDisk = vsysThis.mapVirtualDisks.find(vsdeHD->strRef); - // and find the disk from the OVF's disk list - ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef); - if ( (itVirtualDisk == vsysThis.mapVirtualDisks.end()) - || (itDiskImage == stack.mapDisks.end()) - ) - throw setError(E_FAIL, - tr("Internal inconsistency looking up disk image '%s'"), - vsdeHD->strRef.c_str()); - - const ovf::DiskImage &ovfDiskImage = itDiskImage->second; - const ovf::VirtualDisk &ovfVdisk = itVirtualDisk->second; + const ovf::VirtualDisk &ovfVdisk = itVDisk->second; + + /* very important to store disk name for the next checks */ + disksResolvedNames.insert(diCurrent.strHref); ComObjPtr<Medium> pTargetHD; - importOneDiskImage(ovfDiskImage, - vsdeHD->strVboxCurrent, + + Utf8Str savedVBoxCurrent = vsdeTargetHD->strVBoxCurrent; + + importOneDiskImage(diCurrent, + &vsdeTargetHD->strVBoxCurrent, pTargetHD, stack, pCallbacks, @@ -2429,7 +3176,8 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, // now use the new uuid to attach the disk image to our new machine ComPtr<IMachine> sMachine; rc = stack.pSession->COMGETTER(Machine)(sMachine.asOutParam()); - if (FAILED(rc)) throw rc; + if (FAILED(rc)) + throw rc; // find the hard disk controller to which we should attach ovf::HardDiskController hdc = (*vsysThis.mapControllers.find(ovfVdisk.idController)).second; @@ -2444,34 +3192,88 @@ void Appliance::importMachineGeneric(const ovf::VirtualSystem &vsysThis, mhda.lControllerPort, mhda.lDevice); - Log(("Attaching disk %s to port %d on device %d\n", vsdeHD->strVboxCurrent.c_str(), mhda.lControllerPort, mhda.lDevice)); + Log(("Attaching disk %s to port %d on device %d\n", + vsdeTargetHD->strVBoxCurrent.c_str(), mhda.lControllerPort, mhda.lDevice)); - rc = sMachine->AttachDevice(mhda.controllerType.raw(), // wstring name - mhda.lControllerPort, // long controllerPort - mhda.lDevice, // long device - DeviceType_HardDisk, // DeviceType_T type - pTargetHD); - if (FAILED(rc)) throw rc; + Utf8Str vdf = typeOfVirtualDiskFormatFromURI(diCurrent.strFormat); + + if (vdf.compare("RAW", Utf8Str::CaseInsensitive) == 0) + { + ComPtr<IMedium> dvdImage(pTargetHD); + + rc = mVirtualBox->OpenMedium(Bstr(vsdeTargetHD->strVBoxCurrent).raw(), + DeviceType_DVD, + AccessMode_ReadWrite, + false, + dvdImage.asOutParam()); + + if (FAILED(rc)) + throw rc; + + rc = sMachine->AttachDevice(mhda.controllerType.raw(),// wstring name + mhda.lControllerPort, // long controllerPort + mhda.lDevice, // long device + DeviceType_DVD, // DeviceType_T type + dvdImage); + if (FAILED(rc)) + throw rc; + } + else + { + rc = sMachine->AttachDevice(mhda.controllerType.raw(),// wstring name + mhda.lControllerPort, // long controllerPort + mhda.lDevice, // long device + DeviceType_HardDisk, // DeviceType_T type + pTargetHD); + + if (FAILED(rc)) + throw rc; + } stack.llHardDiskAttachments.push_back(mhda); rc = sMachine->SaveSettings(); - if (FAILED(rc)) throw rc; - } // end for (itHD = avsdeHDs.begin(); + if (FAILED(rc)) + throw rc; + + /* restore */ + vsdeTargetHD->strVBoxCurrent = savedVBoxCurrent; + + ++cImportedDisks; + + } // end while(oit != stack.mapDisks.end()) + + /* + * quantity of the imported disks isn't equal to the size of the avsdeHDs list. + */ + if(cImportedDisks < avsdeHDs.size()) + { + LogWarning(("Not all disk images were imported for VM %s. Check OVF description file.", + vmNameEntry->strOvf.c_str())); + } // only now that we're done with all disks, close the session rc = stack.pSession->UnlockMachine(); - if (FAILED(rc)) throw rc; + + if (FAILED(rc)) + throw rc; + stack.fSessionOpen = false; } - catch(HRESULT /* aRC */) + catch(HRESULT aRC) { + com::ErrorInfo info; + if (stack.fSessionOpen) stack.pSession->UnlockMachine(); - throw; + if (info.isFullAvailable()) + throw setError(aRC, Utf8Str(info.getText()).c_str()); + else + throw setError(aRC, "Unknown error during OVF import"); } } + LogFlowFuncLeave(); } /** @@ -2509,6 +3311,7 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi PVDINTERFACEIO pCallbacks, PSHASTORAGE pStorage) { + LogFlowFuncEnter(); Assert(vsdescThis->m->pConfig); HRESULT rc = S_OK; @@ -2516,10 +3319,8 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi settings::MachineConfigFile &config = *vsdescThis->m->pConfig; /* - * * step 1): modify machine config according to OVF config, in case the user * has modified them using setFinalValues() - * */ /* OS Type */ @@ -2544,7 +3345,34 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi #ifdef VBOX_WITH_USB /* USB controller */ - config.hardwareMachine.usbController.fEnabled = stack.fUSBEnabled; + if (stack.fUSBEnabled) + { + /** @todo r=klaus add support for arbitrary USB controller types, this can't handle multiple controllers due to its design anyway */ + + /* usually the OHCI controller is enabled already, need to check */ + bool fOHCIEnabled = false; + settings::USBControllerList &llUSBControllers = config.hardwareMachine.usbSettings.llUSBControllers; + settings::USBControllerList::iterator it; + for (it = llUSBControllers.begin(); it != llUSBControllers.end(); ++it) + { + if (it->enmType == USBControllerType_OHCI) + { + fOHCIEnabled = true; + break; + } + } + + if (!fOHCIEnabled) + { + settings::USBController ctrl; + ctrl.strName = "OHCI"; + ctrl.enmType = USBControllerType_OHCI; + + llUSBControllers.push_back(ctrl); + } + } + else + config.hardwareMachine.usbSettings.llUSBControllers.clear(); #endif /* Audio adapter */ if (stack.strAudioAdapter.isNotEmpty()) @@ -2558,13 +3386,14 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi settings::NetworkAdaptersList &llNetworkAdapters = config.hardwareMachine.llNetworkAdapters; /* First disable all network cards, they will be enabled below again. */ settings::NetworkAdaptersList::iterator it1; - bool fKeepAllMACs = m->optList.contains(ImportOptions_KeepAllMACs); - bool fKeepNATMACs = m->optList.contains(ImportOptions_KeepNATMACs); + bool fKeepAllMACs = m->optListImport.contains(ImportOptions_KeepAllMACs); + bool fKeepNATMACs = m->optListImport.contains(ImportOptions_KeepNATMACs); for (it1 = llNetworkAdapters.begin(); it1 != llNetworkAdapters.end(); ++it1) { it1->fEnabled = false; if (!( fKeepAllMACs - || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NAT))) + || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NAT) + || (fKeepNATMACs && it1->mode == NetworkAttachmentType_NATNetwork))) Host::generateMACAddress(it1->strMACAddress); } /* Now iterate over all network entries. */ @@ -2593,7 +3422,7 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi if (it1->ulSlot == iSlot) { it1->fEnabled = true; - it1->type = (NetworkAdapterType_T)vsdeNW->strVboxCurrent.toUInt32(); + it1->type = (NetworkAdapterType_T)vsdeNW->strVBoxCurrent.toUInt32(); break; } } @@ -2612,7 +3441,7 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi * failures. A long fixed bug, however the OVF files are long lived. */ settings::StorageControllersList &llControllers = config.storageMachine.llStorageControllers; Guid hdUuid; - uint32_t cHardDisks = 0; + uint32_t cDisks = 0; bool fInconsistent = false; bool fRepairDuplicate = false; settings::StorageControllersList::iterator it3; @@ -2636,17 +3465,17 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi else if (it4->deviceType == DeviceType_HardDisk) { const Guid &thisUuid = it4->uuid; - cHardDisks++; - if (cHardDisks == 1) + cDisks++; + if (cDisks == 1) { - if (hdUuid.isEmpty()) + if (hdUuid.isZero()) hdUuid = thisUuid; else fInconsistent = true; } else { - if (thisUuid.isEmpty()) + if (thisUuid.isZero()) fInconsistent = true; else if (thisUuid == hdUuid) fRepairDuplicate = true; @@ -2656,141 +3485,290 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi } } /* paranoia... */ - if (fInconsistent || cHardDisks == 1) + if (fInconsistent || cDisks == 1) fRepairDuplicate = false; /* - * * step 2: scan the machine config for media attachments - * */ + /* get VM name from virtual system description. Only one record is possible (size of list is equal 1). */ + std::list<VirtualSystemDescriptionEntry*> vmName = vsdescThis->findByType(VirtualSystemDescriptionType_Name); + std::list<VirtualSystemDescriptionEntry*>::iterator vmNameIt = vmName.begin(); + VirtualSystemDescriptionEntry* vmNameEntry = *vmNameIt; + /* Get all hard disk descriptions. */ std::list<VirtualSystemDescriptionEntry*> avsdeHDs = vsdescThis->findByType(VirtualSystemDescriptionType_HardDiskImage); std::list<VirtualSystemDescriptionEntry*>::iterator avsdeHDsIt = avsdeHDs.begin(); /* paranoia - if there is no 1:1 match do not try to repair. */ - if (cHardDisks != avsdeHDs.size()) + if (cDisks != avsdeHDs.size()) fRepairDuplicate = false; - // for each storage controller... - for (settings::StorageControllersList::iterator sit = config.storageMachine.llStorageControllers.begin(); - sit != config.storageMachine.llStorageControllers.end(); - ++sit) - { - settings::StorageController &sc = *sit; + // there must be an image in the OVF disk structs with the same UUID - // find the OVF virtual system description entry for this storage controller - switch (sc.storageBus) - { - case StorageBus_SATA: - break; - case StorageBus_SCSI: - break; - case StorageBus_IDE: - break; - case StorageBus_SAS: - break; - } + ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin(); + std::set<RTCString> disksResolvedNames; - // for each medium attachment to this controller... - for (settings::AttachedDevicesList::iterator dit = sc.llAttachedDevices.begin(); - dit != sc.llAttachedDevices.end(); - ++dit) - { - settings::AttachedDevice &d = *dit; + uint32_t cImportedDisks = 0; - if (d.uuid.isEmpty()) - // empty DVD and floppy media - continue; + while(oit != stack.mapDisks.end() && cImportedDisks != avsdeHDs.size()) + { + ovf::DiskImage diCurrent = oit->second; - // When repairing a broken VirtualBox xml config section (written - // by VirtualBox versions earlier than 3.2.10) assume the disks - // show up in the same order as in the OVF description. - if (fRepairDuplicate) + VirtualSystemDescriptionEntry *vsdeTargetHD = 0; + + { + /* Iterate over all given disk images of the virtual system + * disks description. We need to find the target disk path, + * which could be changed by the user. */ + list<VirtualSystemDescriptionEntry*>::const_iterator itHD; + for (itHD = avsdeHDs.begin(); + itHD != avsdeHDs.end(); + ++itHD) { - VirtualSystemDescriptionEntry *vsdeHD = *avsdeHDsIt; - ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef); - if (itDiskImage != stack.mapDisks.end()) + VirtualSystemDescriptionEntry *vsdeHD = *itHD; + if (vsdeHD->strRef == oit->first) { - const ovf::DiskImage &di = itDiskImage->second; - d.uuid = Guid(di.uuidVbox); + vsdeTargetHD = vsdeHD; + break; } - ++avsdeHDsIt; } - // convert the Guid to string - Utf8Str strUuid = d.uuid.toString(); + if (!vsdeTargetHD) + { + /* possible case if a disk image belongs to other virtual system (OVF package with multiple VMs inside) */ + LogWarning(("OVA/OVF import: Disk image %s was missed during import of VM %s\n", + oit->first.c_str(), vmNameEntry->strOvf.c_str())); + ++oit; + continue; + } + } + + /* + * preliminary check availability of the image + * This step is useful if image is placed in the OVA (TAR) package + */ - // there must be an image in the OVF disk structs with the same UUID - bool fFound = false; - for (ovf::DiskImagesMap::const_iterator oit = stack.mapDisks.begin(); - oit != stack.mapDisks.end(); - ++oit) + Utf8Str name = applianceIOName(applianceIOTar); + + if (strncmp(pStorage->pVDImageIfaces->pszInterfaceName, name.c_str(), name.length()) == 0) + { + /* It means that we possibly have imported the storage earlier on the previous loop steps*/ + std::set<RTCString>::const_iterator h = disksResolvedNames.find(diCurrent.strHref); + if (h != disksResolvedNames.end()) { - const ovf::DiskImage &di = oit->second; + /* Yes, disk name was found, we can skip it*/ + ++oit; + continue; + } + + RTCString availableImage(diCurrent.strHref); - if (di.uuidVbox == strUuid) + rc = preCheckImageAvailability(pStorage, + availableImage + ); + + if (SUCCEEDED(rc)) + { + /* current opened file isn't the same as passed one */ + if(availableImage.compare(diCurrent.strHref, Utf8Str::CaseInsensitive) != 0) { - VirtualSystemDescriptionEntry *vsdeTargetHD = 0; + // availableImage contains the disk identifier (e.g. "vmdisk1"), which should exist + // in the virtual system's disks map under that ID and also in the global images map + // and find the disk from the OVF's disk list + ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.begin(); + while (++itDiskImage != stack.mapDisks.end()) + { + if(itDiskImage->second.strHref.compare(availableImage, Utf8Str::CaseInsensitive) == 0 ) + break; + } + if (itDiskImage == stack.mapDisks.end()) + { + throw setError(E_FAIL, + tr("Internal inconsistency looking up disk image '%s'. " + "Check compliance OVA package structure and file names " + "references in the section <References> in the OVF file."), + availableImage.c_str()); + } - /* Iterate over all given disk images of the virtual system - * disks description. We need to find the target disk path, - * which could be changed by the user. */ + /* replace with a new found disk image */ + diCurrent = *(&itDiskImage->second); + + /* + * Again iterate over all given disk images of the virtual system + * disks description using the found disk image + */ list<VirtualSystemDescriptionEntry*>::const_iterator itHD; for (itHD = avsdeHDs.begin(); itHD != avsdeHDs.end(); ++itHD) { VirtualSystemDescriptionEntry *vsdeHD = *itHD; - if (vsdeHD->strRef == oit->first) + if (vsdeHD->strRef == diCurrent.strDiskId) { vsdeTargetHD = vsdeHD; break; } } if (!vsdeTargetHD) + /* + * in this case it's an error because something wrong with OVF description file. + * May be VBox imports OVA package with wrong file sequence inside the archive. + */ throw setError(E_FAIL, tr("Internal inconsistency looking up disk image '%s'"), - oit->first.c_str()); + diCurrent.strHref.c_str()); + } + else + { + ++oit; + } + } + else + { + ++oit; + continue; + } + } + else + { + /* just continue with normal files*/ + ++oit; + } - /* - * - * step 3: import disk - * - */ - ComObjPtr<Medium> pTargetHD; - importOneDiskImage(di, - vsdeTargetHD->strVboxCurrent, - pTargetHD, - stack, - pCallbacks, - pStorage); + /* Important! to store disk name for the next checks */ + disksResolvedNames.insert(diCurrent.strHref); + + // there must be an image in the OVF disk structs with the same UUID + bool fFound = false; + Utf8Str strUuid; + + // for each storage controller... + for (settings::StorageControllersList::iterator sit = config.storageMachine.llStorageControllers.begin(); + sit != config.storageMachine.llStorageControllers.end(); + ++sit) + { + settings::StorageController &sc = *sit; + + // find the OVF virtual system description entry for this storage controller + switch (sc.storageBus) + { + case StorageBus_SATA: + break; + case StorageBus_SCSI: + break; + case StorageBus_IDE: + break; + case StorageBus_SAS: + break; + } + + // for each medium attachment to this controller... + for (settings::AttachedDevicesList::iterator dit = sc.llAttachedDevices.begin(); + dit != sc.llAttachedDevices.end(); + ++dit) + { + settings::AttachedDevice &d = *dit; + + if (d.uuid.isZero()) + // empty DVD and floppy media + continue; + + // When repairing a broken VirtualBox xml config section (written + // by VirtualBox versions earlier than 3.2.10) assume the disks + // show up in the same order as in the OVF description. + if (fRepairDuplicate) + { + VirtualSystemDescriptionEntry *vsdeHD = *avsdeHDsIt; + ovf::DiskImagesMap::const_iterator itDiskImage = stack.mapDisks.find(vsdeHD->strRef); + if (itDiskImage != stack.mapDisks.end()) + { + const ovf::DiskImage &di = itDiskImage->second; + d.uuid = Guid(di.uuidVBox); + } + ++avsdeHDsIt; + } + + // convert the Guid to string + strUuid = d.uuid.toString(); + + if (diCurrent.uuidVBox != strUuid) + { + continue; + } + + /* + * step 3: import disk + */ + Utf8Str savedVBoxCurrent = vsdeTargetHD->strVBoxCurrent; + ComObjPtr<Medium> pTargetHD; + importOneDiskImage(diCurrent, + &vsdeTargetHD->strVBoxCurrent, + pTargetHD, + stack, + pCallbacks, + pStorage); + + Bstr hdId; + + Utf8Str vdf = typeOfVirtualDiskFormatFromURI(diCurrent.strFormat); + + if (vdf.compare("RAW", Utf8Str::CaseInsensitive) == 0) + { + ComPtr<IMedium> dvdImage(pTargetHD); + + rc = mVirtualBox->OpenMedium(Bstr(vsdeTargetHD->strVBoxCurrent).raw(), + DeviceType_DVD, + AccessMode_ReadWrite, + false, + dvdImage.asOutParam()); + + if (FAILED(rc)) throw rc; // ... and replace the old UUID in the machine config with the one of // the imported disk that was just created - Bstr hdId; + rc = dvdImage->COMGETTER(Id)(hdId.asOutParam()); + if (FAILED(rc)) throw rc; + } + else + { + // ... and replace the old UUID in the machine config with the one of + // the imported disk that was just created rc = pTargetHD->COMGETTER(Id)(hdId.asOutParam()); if (FAILED(rc)) throw rc; + } - d.uuid = hdId; + /* restore */ + vsdeTargetHD->strVBoxCurrent = savedVBoxCurrent; - fFound = true; - break; - } - } + d.uuid = hdId; + fFound = true; + break; + } // for (settings::AttachedDevicesList::const_iterator dit = sc.llAttachedDevices.begin(); + } // for (settings::StorageControllersList::const_iterator sit = config.storageMachine.llStorageControllers.begin(); // no disk with such a UUID found: - if (!fFound) - throw setError(E_FAIL, - tr("<vbox:Machine> element in OVF contains a medium attachment for the disk image %s but the OVF describes no such image"), - strUuid.c_str()); - } // for (settings::AttachedDevicesList::const_iterator dit = sc.llAttachedDevices.begin(); - } // for (settings::StorageControllersList::const_iterator sit = config.storageMachine.llStorageControllers.begin(); + if (!fFound) + throw setError(E_FAIL, + tr("<vbox:Machine> element in OVF contains a medium attachment for the disk image %s " + "but the OVF describes no such image"), + strUuid.c_str()); + + ++cImportedDisks; + + }// while(oit != stack.mapDisks.end()) + + /* + * quantity of the imported disks isn't equal to the size of the avsdeHDs list. + */ + if(cImportedDisks < avsdeHDs.size()) + { + LogWarning(("Not all disk images were imported for VM %s. Check OVF description file.", + vmNameEntry->strOvf.c_str())); + } /* - * * step 4): create the machine and have it import the config - * */ ComObjPtr<Machine> pNewMachine; @@ -2800,8 +3778,8 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi // this magic constructor fills the new machine object with the MachineConfig // instance that we created from the vbox:Machine rc = pNewMachine->init(mVirtualBox, - stack.strNameVBox, // name from OVF preparations; can be suffixed to avoid duplicates, or changed by user - config); // the whole machine config + stack.strNameVBox,// name from OVF preparations; can be suffixed to avoid duplicates + config); // the whole machine config if (FAILED(rc)) throw rc; pReturnNewMachine = ComPtr<IMachine>(pNewMachine); @@ -2815,6 +3793,8 @@ void Appliance::importVBoxMachine(ComObjPtr<VirtualSystemDescription> &vsdescThi rc = pNewMachine->COMGETTER(Id)(bstrNewMachineId.asOutParam()); if (FAILED(rc)) throw rc; m->llGuidsMachinesCreated.push_back(Guid(bstrNewMachineId)); + + LogFlowFuncLeave(); } void Appliance::importMachines(ImportStack &stack, @@ -2824,9 +3804,14 @@ void Appliance::importMachines(ImportStack &stack, HRESULT rc = S_OK; // this is safe to access because this thread only gets started - // if pReader != NULL const ovf::OVFReader &reader = *m->pReader; + /* + * get the SHA digest version that was set in accordance with the value of attribute "xmlns:ovf" + * of the element <Envelope> in the OVF file during reading operation. See readFSImpl(). + */ + pStorage->fSha256 = m->fSha256; + // create a session for the machine + disks we manipulate below rc = stack.pSession.createInprocObject(CLSID_Session); if (FAILED(rc)) throw rc; @@ -2837,7 +3822,8 @@ void Appliance::importMachines(ImportStack &stack, size_t i = 0; for (it = reader.m_llVirtualSystems.begin(), it1 = m->virtualSystemDescriptions.begin(); - it != reader.m_llVirtualSystems.end(); + it != reader.m_llVirtualSystems.end(), + it1 != m->virtualSystemDescriptions.end(); ++it, ++it1, ++i) { const ovf::VirtualSystem &vsysThis = *it; @@ -2860,7 +3846,7 @@ void Appliance::importMachines(ImportStack &stack, if (vsdeName.size() < 1) throw setError(VBOX_E_FILE_ERROR, tr("Missing VM name")); - stack.strNameVBox = vsdeName.front()->strVboxCurrent; + stack.strNameVBox = vsdeName.front()->strVBoxCurrent; // have VirtualBox suggest where the filename would be placed so we can // put the disk images in the same directory @@ -2874,6 +3860,7 @@ void Appliance::importMachines(ImportStack &stack, // and determine the machine folder from that stack.strMachineFolder = bstrMachineFilename; stack.strMachineFolder.stripFilename(); + LogFunc(("i=%zu strName=%s bstrMachineFilename=%ls\n", i, stack.strNameVBox.c_str(), bstrMachineFilename.raw())); // guest OS type std::list<VirtualSystemDescriptionEntry*> vsdeOS; @@ -2881,14 +3868,14 @@ void Appliance::importMachines(ImportStack &stack, if (vsdeOS.size() < 1) throw setError(VBOX_E_FILE_ERROR, tr("Missing guest OS type")); - stack.strOsTypeVBox = vsdeOS.front()->strVboxCurrent; + stack.strOsTypeVBox = vsdeOS.front()->strVBoxCurrent; // CPU count std::list<VirtualSystemDescriptionEntry*> vsdeCPU = vsdescThis->findByType(VirtualSystemDescriptionType_CPU); if (vsdeCPU.size() != 1) throw setError(VBOX_E_FILE_ERROR, tr("CPU count missing")); - stack.cCPUs = vsdeCPU.front()->strVboxCurrent.toUInt32(); + stack.cCPUs = vsdeCPU.front()->strVBoxCurrent.toUInt32(); // We need HWVirt & IO-APIC if more than one CPU is requested if (stack.cCPUs > 1) { @@ -2900,7 +3887,7 @@ void Appliance::importMachines(ImportStack &stack, std::list<VirtualSystemDescriptionEntry*> vsdeRAM = vsdescThis->findByType(VirtualSystemDescriptionType_Memory); if (vsdeRAM.size() != 1) throw setError(VBOX_E_FILE_ERROR, tr("RAM size missing")); - stack.ulMemorySizeMB = (ULONG)vsdeRAM.front()->strVboxCurrent.toUInt64(); + stack.ulMemorySizeMB = (ULONG)vsdeRAM.front()->strVBoxCurrent.toUInt64(); #ifdef VBOX_WITH_USB // USB controller @@ -2913,12 +3900,12 @@ void Appliance::importMachines(ImportStack &stack, std::list<VirtualSystemDescriptionEntry*> vsdeAudioAdapter = vsdescThis->findByType(VirtualSystemDescriptionType_SoundCard); /* @todo: we support one audio adapter only */ if (vsdeAudioAdapter.size() > 0) - stack.strAudioAdapter = vsdeAudioAdapter.front()->strVboxCurrent; + stack.strAudioAdapter = vsdeAudioAdapter.front()->strVBoxCurrent; // for the description of the new machine, always use the OVF entry, the user may have changed it in the import config std::list<VirtualSystemDescriptionEntry*> vsdeDescription = vsdescThis->findByType(VirtualSystemDescriptionType_Description); if (vsdeDescription.size()) - stack.strDescription = vsdeDescription.front()->strVboxCurrent; + stack.strDescription = vsdeDescription.front()->strVBoxCurrent; // import vbox:machine or OVF now if (vsdescThis->m->pConfig) diff --git a/src/VBox/Main/src-server/AudioAdapterImpl.cpp b/src/VBox/Main/src-server/AudioAdapterImpl.cpp index 9b6dbf17..089359b2 100644 --- a/src/VBox/Main/src-server/AudioAdapterImpl.cpp +++ b/src/VBox/Main/src-server/AudioAdapterImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/BIOSSettingsImpl.cpp b/src/VBox/Main/src-server/BIOSSettingsImpl.cpp index bc40820b..1cc0a5e2 100644 --- a/src/VBox/Main/src-server/BIOSSettingsImpl.cpp +++ b/src/VBox/Main/src-server/BIOSSettingsImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -507,6 +507,23 @@ STDMETHODIMP BIOSSettings::COMSETTER(TimeOffset)(LONG64 offset) return S_OK; } +STDMETHODIMP BIOSSettings::COMGETTER(NonVolatileStorageFile)(BSTR *pbstrPath) +{ + CheckComArgOutPointerValid(pbstrPath); + + AutoCaller autoCaller(this); + HRESULT hrc = autoCaller.rc(); + if (SUCCEEDED(hrc)) + { + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + Bstr bstrEmpty(""); + hrc = bstrEmpty.cloneToEx(pbstrPath); + } + + return hrc; +} + + // IBIOSSettings methods ///////////////////////////////////////////////////////////////////////////// diff --git a/src/VBox/Main/src-server/BandwidthControlImpl.cpp b/src/VBox/Main/src-server/BandwidthControlImpl.cpp index 70e2d243..5235bb6b 100644 --- a/src/VBox/Main/src-server/BandwidthControlImpl.cpp +++ b/src/VBox/Main/src-server/BandwidthControlImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -25,6 +25,8 @@ #include "Logging.h" #include <iprt/cpp/utils.h> +#include <VBox/com/array.h> +#include <algorithm> // defines ///////////////////////////////////////////////////////////////////////////// @@ -72,8 +74,6 @@ void BandwidthControl::FinalRelease() * * @returns COM result indicator. * @param aParent Pointer to our parent object. - * @param aName Name of the storage controller. - * @param aInstance Instance number of the storage controller. */ HRESULT BandwidthControl::init(Machine *aParent) { @@ -147,7 +147,7 @@ HRESULT BandwidthControl::init(Machine *aParent, } /** - * Initializes the storage controller object given another guest object + * Initializes the bandwidth control object given another guest object * (a kind of copy constructor). This object makes a private copy of data * of the original object passed as an argument. */ @@ -176,7 +176,7 @@ HRESULT BandwidthControl::initCopy(Machine *aParent, BandwidthControl *aThat) { ComObjPtr<BandwidthGroup> group; group.createObject(); - group->init(this, *it); + group->initCopy(this, *it); m->llBandwidthGroups->push_back(group); ++ it; } @@ -215,7 +215,7 @@ void BandwidthControl::copyFrom (BandwidthControl *aThat) AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS); AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS); - /* create private copies of all filters */ + /* create private copies of all bandwidth groups */ m->llBandwidthGroups.backup(); m->llBandwidthGroups->clear(); for (BandwidthGroupList::const_iterator it = aThat->m->llBandwidthGroups->begin(); @@ -285,7 +285,7 @@ void BandwidthControl::commit() { AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS); - /* Commit all changes to new controllers (this will reshare data with + /* Commit all changes to new groups (this will reshare data with * peers for those who have peers) */ BandwidthGroupList *newList = new BandwidthGroupList(); BandwidthGroupList::const_iterator it = m->llBandwidthGroups->begin(); @@ -313,7 +313,7 @@ void BandwidthControl::commit() ++it; } - /* uninit old peer's controllers that are left */ + /* uninit old peer's groups that are left */ it = m->pPeer->m->llBandwidthGroups->begin(); while (it != m->pPeer->m->llBandwidthGroups->end()) { @@ -321,7 +321,7 @@ void BandwidthControl::commit() ++it; } - /* attach new list of controllers to our peer */ + /* attach new list of groups to our peer */ m->pPeer->m->llBandwidthGroups.attach(newList); } else @@ -334,7 +334,7 @@ void BandwidthControl::commit() else { /* the list of groups itself is not changed, - * just commit changes to controllers themselves */ + * just commit changes to groups themselves */ commitBandwidthGroups = true; } @@ -379,10 +379,10 @@ void BandwidthControl::uninit() } /** - * Returns a storage controller object with the given name. + * Returns a bandwidth group object with the given name. * - * @param aName storage controller name to find - * @param aStorageController where to return the found storage controller + * @param aName bandwidth group name to find + * @param aBandwidthGroup where to return the found bandwidth group * @param aSetError true to set extended error info on failure */ HRESULT BandwidthControl::getBandwidthGroupByName(const Utf8Str &aName, diff --git a/src/VBox/Main/src-server/BandwidthGroupImpl.cpp b/src/VBox/Main/src-server/BandwidthGroupImpl.cpp index aef8f4c1..22ba0c1b 100644 --- a/src/VBox/Main/src-server/BandwidthGroupImpl.cpp +++ b/src/VBox/Main/src-server/BandwidthGroupImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -80,8 +80,9 @@ void BandwidthGroup::FinalRelease() * * @returns COM result indicator. * @param aParent Pointer to our parent object. - * @param aName Name of the storage controller. - * @param aInstance Instance number of the storage controller. + * @param aName Name of the bandwidth group. + * @param aType Type of the bandwidth group (net, disk). + * @param aMaxBytesPerSec Maximum bandwidth for the bandwidth group. */ HRESULT BandwidthGroup::init(BandwidthControl *aParent, const Utf8Str &aName, @@ -175,7 +176,7 @@ HRESULT BandwidthGroup::init(BandwidthControl *aParent, } /** - * Initializes the storage controller object given another guest object + * Initializes the bandwidth group object given another guest object * (a kind of copy constructor). This object makes a private copy of data * of the original object passed as an argument. */ diff --git a/src/VBox/Main/src-server/ClientToken.cpp b/src/VBox/Main/src-server/ClientToken.cpp new file mode 100644 index 00000000..be0afb8a --- /dev/null +++ b/src/VBox/Main/src-server/ClientToken.cpp @@ -0,0 +1,262 @@ +/** @file + * + * VirtualBox API client session crash token handling + */ + +/* + * Copyright (C) 2004-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/log.h> +#include <iprt/semaphore.h> +#include <iprt/process.h> + +#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER +# include <errno.h> +# include <sys/types.h> +# include <sys/stat.h> +# include <sys/ipc.h> +# include <sys/sem.h> +#endif + +#include <VBox/com/defs.h> + +#include <vector> + +#include "VirtualBoxBase.h" +#include "AutoCaller.h" +#include "ClientToken.h" +#include "MachineImpl.h" + +Machine::ClientToken::ClientToken() +{ + AssertReleaseFailed(); +} + +Machine::ClientToken::~ClientToken() +{ +#if defined(RT_OS_WINDOWS) + if (mClientToken) + ::CloseHandle(mClientToken); +#elif defined(RT_OS_OS2) + if (mClientToken != NULLHANDLE) + ::DosCloseMutexSem(mClientToken); +#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) + if (mClientToken >= 0) + ::semctl(mClientToken, 0, IPC_RMID); +# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN + mClientTokenId = "0"; +# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ +#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER) + /* release the token, uses reference counting */ + if (mClientToken) + { + if (!mClientTokenPassed) + mClientToken->Release(); + mClientToken = NULL; + } +#else +# error "Port me!" +#endif + mClientToken = CTTOKENARG; +} + +Machine::ClientToken::ClientToken(const ComObjPtr<Machine> &pMachine, + SessionMachine *pSessionMachine) : + mMachine(pMachine) +{ +#if defined(RT_OS_WINDOWS) + NOREF(pSessionMachine); + Bstr tokenId = pMachine->mData->m_strConfigFileFull; + for (size_t i = 0; i < tokenId.length(); i++) + if (tokenId.raw()[i] == '\\') + tokenId.raw()[i] = '/'; + mClientToken = ::CreateMutex(NULL, FALSE, tokenId.raw()); + mClientTokenId = tokenId; + AssertMsg(mClientToken, + ("Cannot create token '%s', err=%d", + mClientTokenId.c_str(), ::GetLastError())); +#elif defined(RT_OS_OS2) + NOREF(pSessionMachine); + Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}", + pMachine->mData->mUuid.raw()); + mClientTokenId = ipcSem; + APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mClientToken, 0, FALSE); + AssertMsg(arc == NO_ERROR, + ("Cannot create token '%s', arc=%ld", + ipcSem.c_str(), arc)); +#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) + NOREF(pSessionMachine); +# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN +# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64) + /** @todo Check that this still works correctly. */ + AssertCompileSize(key_t, 8); +# else + AssertCompileSize(key_t, 4); +# endif + key_t key; + mClientToken = -1; + mClientTokenId = "0"; + for (uint32_t i = 0; i < 1 << 24; i++) + { + key = ((uint32_t)'V' << 24) | i; + int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL); + if (sem >= 0 || (errno != EEXIST && errno != EACCES)) + { + mClientToken = sem; + if (sem >= 0) + mClientTokenId = BstrFmt("%u", key); + break; + } + } +# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ + Utf8Str semName = pMachine->mData->m_strConfigFileFull; + char *pszSemName = NULL; + RTStrUtf8ToCurrentCP(&pszSemName, semName); + key_t key = ::ftok(pszSemName, 'V'); + RTStrFree(pszSemName); + + mClientToken = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT); +# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ + + int errnoSave = errno; + if (mClientToken < 0 && errnoSave == ENOSYS) + { + mMachine->setError(E_FAIL, + tr("Cannot create IPC semaphore. Most likely your host kernel lacks " + "support for SysV IPC. Check the host kernel configuration for " + "CONFIG_SYSVIPC=y")); + mClientToken = CTTOKENARG; + return; + } + /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing + * the token */ + if (mClientToken < 0 && errnoSave == ENOSPC) + { +#ifdef RT_OS_LINUX + mMachine->setError(E_FAIL, + tr("Cannot create IPC semaphore because the system limit for the " + "maximum number of semaphore sets (SEMMNI), or the system wide " + "maximum number of semaphores (SEMMNS) would be exceeded. The " + "current set of SysV IPC semaphores can be determined from " + "the file /proc/sysvipc/sem")); +#else + mMachine->setError(E_FAIL, + tr("Cannot create IPC semaphore because the system-imposed limit " + "on the maximum number of allowed semaphores or semaphore " + "identifiers system-wide would be exceeded")); +#endif + mClientToken = CTTOKENARG; + return; + } + AssertMsgReturnVoid(mClientToken >= 0, ("Cannot create token, errno=%d", errnoSave)); + /* set the initial value to 1 */ + int rv = ::semctl(mClientToken, 0, SETVAL, 1); + errnoSave = errno; + if (rv != 0) + { + ::semctl(mClientToken, 0, IPC_RMID); + mClientToken = CTTOKENARG; + AssertMsgFailedReturnVoid(("Cannot init token, errno=%d", errnoSave)); + } +#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER) + ComObjPtr<MachineToken> pToken; + HRESULT rc = pToken.createObject(); + if (SUCCEEDED(rc)) + { + rc = pToken->init(pSessionMachine); + if (SUCCEEDED(rc)) + { + mClientToken = pToken; + if (mClientToken) + { + rc = mClientToken->AddRef(); + if (FAILED(rc)) + mClientToken = NULL; + } + } + } + pToken.setNull(); + mClientTokenPassed = false; + /* mClientTokenId isn't really used */ + mClientTokenId = pMachine->mData->m_strConfigFileFull; + AssertMsg(mClientToken, + ("Cannot create token '%s', rc=%Rhrc", + mClientTokenId.c_str(), rc)); +#else +# error "Port me!" +#endif +} + +bool Machine::ClientToken::isReady() +{ + return mClientToken != CTTOKENARG; +} + +void Machine::ClientToken::getId(Utf8Str &strId) +{ + strId = mClientTokenId; +} + +CTTOKENTYPE Machine::ClientToken::getToken() +{ +#ifdef VBOX_WITH_GENERIC_SESSION_WATCHER + mClientTokenPassed = true; +#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */ + return mClientToken; +} + +#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER +bool Machine::ClientToken::release() +{ + bool terminated = false; + +#if defined(RT_OS_WINDOWS) + AssertMsg(mClientToken, ("semaphore must be created")); + + /* release the token */ + ::ReleaseMutex(mClientToken); + terminated = true; +#elif defined(RT_OS_OS2) + AssertMsg(mClientToken, ("semaphore must be created")); + + /* release the token */ + ::DosReleaseMutexSem(mClientToken); + terminated = true; +#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) + AssertMsg(mClientToken >= 0, ("semaphore must be created")); + int val = ::semctl(mClientToken, 0, GETVAL); + if (val > 0) + { + /* the semaphore is signaled, meaning the session is terminated */ + terminated = true; + } +#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER) + /** @todo r=klaus never tested, this code is not reached */ + AssertMsg(mClientToken, ("token must be created")); + /* release the token, uses reference counting */ + if (mClientToken) + { + if (!mClientTokenPassed) + mClientToken->Release(); + mClientToken = NULL; + } + terminated = true; +#else +# error "Port me!" +#endif + return terminated; +} +#endif /* !VBOX_WITH_GENERIC_SESSION_WATCHER */ + +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/ClientWatcher.cpp b/src/VBox/Main/src-server/ClientWatcher.cpp new file mode 100644 index 00000000..6d2cd3f1 --- /dev/null +++ b/src/VBox/Main/src-server/ClientWatcher.cpp @@ -0,0 +1,849 @@ +/** @file + * + * VirtualBox API client session crash watcher + */ + +/* + * Copyright (C) 2006-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <iprt/asm.h> +#include <iprt/assert.h> +#include <iprt/log.h> +#include <iprt/semaphore.h> +#include <iprt/process.h> + +#include <VBox/com/defs.h> + +#include <vector> + +#include "VirtualBoxBase.h" +#include "AutoCaller.h" +#include "ClientWatcher.h" +#include "ClientToken.h" +#include "VirtualBoxImpl.h" +#include "MachineImpl.h" + +#if defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER) +/** Table for adaptive timeouts. After an update the counter starts at the + * maximum value and decreases to 0, i.e. first the short timeouts are used + * and then the longer ones. This minimizes the detection latency in the + * cases where a change is expected, for crashes. */ +static const RTMSINTERVAL s_aUpdateTimeoutSteps[] = { 500, 200, 100, 50, 20, 10, 5 }; +#endif + + + +VirtualBox::ClientWatcher::ClientWatcher() : + mLock(LOCKCLASS_OBJECTSTATE) +{ + AssertReleaseFailed(); +} + +VirtualBox::ClientWatcher::~ClientWatcher() +{ + if (mThread != NIL_RTTHREAD) + { + /* signal the client watcher thread, should be exiting now */ + update(); + /* wait for termination */ + RTThreadWait(mThread, RT_INDEFINITE_WAIT, NULL); + mThread = NIL_RTTHREAD; + } + mProcesses.clear(); +#if defined(RT_OS_WINDOWS) + if (mUpdateReq != NULL) + { + ::CloseHandle(mUpdateReq); + mUpdateReq = NULL; + } +#elif defined(RT_OS_OS2) || defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER) + if (mUpdateReq != NIL_RTSEMEVENT) + { + RTSemEventDestroy(mUpdateReq); + mUpdateReq = NIL_RTSEMEVENT; + } +#else +# error "Port me!" +#endif +} + +VirtualBox::ClientWatcher::ClientWatcher(const ComObjPtr<VirtualBox> &pVirtualBox) : + mVirtualBox(pVirtualBox), + mThread(NIL_RTTHREAD), + mUpdateReq(CWUPDATEREQARG), + mLock(LOCKCLASS_OBJECTSTATE) +{ +#if defined(RT_OS_WINDOWS) + mUpdateReq = ::CreateEvent(NULL, FALSE, FALSE, NULL); +#elif defined(RT_OS_OS2) + RTSemEventCreate(&mUpdateReq); +#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) || defined(VBOX_WITH_GENERIC_SESSION_WATCHER) + RTSemEventCreate(&mUpdateReq); + /* start with high timeouts, nothing to do */ + ASMAtomicUoWriteU8(&mUpdateAdaptCtr, 0); +#else +# error "Port me!" +#endif + + int vrc = RTThreadCreate(&mThread, + worker, + (void *)this, + 0, + RTTHREADTYPE_MAIN_WORKER, + RTTHREADFLAGS_WAITABLE, + "Watcher"); + AssertRC(vrc); +} + +bool VirtualBox::ClientWatcher::isReady() +{ + return mThread != NIL_RTTHREAD; +} + +/** + * Sends a signal to the thread to rescan the clients/VMs having open sessions. + */ +void VirtualBox::ClientWatcher::update() +{ + AssertReturnVoid(mThread != NIL_RTTHREAD); + + /* sent an update request */ +#if defined(RT_OS_WINDOWS) + ::SetEvent(mUpdateReq); +#elif defined(RT_OS_OS2) + RTSemEventSignal(mUpdateReq); +#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) + /* use short timeouts, as we expect changes */ + ASMAtomicUoWriteU8(&mUpdateAdaptCtr, RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1); + RTSemEventSignal(mUpdateReq); +#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER) + RTSemEventSignal(mUpdateReq); +#else +# error "Port me!" +#endif +} + +/** + * Adds a process to the list of processes to be reaped. This call should be + * followed by a call to update() to cause the necessary actions immediately, + * in case the process crashes straight away. + */ +void VirtualBox::ClientWatcher::addProcess(RTPROCESS pid) +{ + AssertReturnVoid(mThread != NIL_RTTHREAD); + /* @todo r=klaus, do the reaping on all platforms! */ +#ifndef RT_OS_WINDOWS + AutoWriteLock alock(mLock COMMA_LOCKVAL_SRC_POS); + mProcesses.push_back(pid); +#endif +} + +/** + * Thread worker function that watches the termination of all client processes + * that have open sessions using IMachine::LockMachine() + */ +/*static*/ +DECLCALLBACK(int) VirtualBox::ClientWatcher::worker(RTTHREAD /* thread */, void *pvUser) +{ + LogFlowFuncEnter(); + + VirtualBox::ClientWatcher *that = (VirtualBox::ClientWatcher *)pvUser; + Assert(that); + + typedef std::vector<ComObjPtr<Machine> > MachineVector; + typedef std::vector<ComObjPtr<SessionMachine> > SessionMachineVector; + + SessionMachineVector machines; + MachineVector spawnedMachines; + + size_t cnt = 0; + size_t cntSpawned = 0; + + VirtualBoxBase::initializeComForThread(); + +#if defined(RT_OS_WINDOWS) + + /// @todo (dmik) processes reaping! + + HANDLE handles[MAXIMUM_WAIT_OBJECTS]; + handles[0] = that->mUpdateReq; + + do + { + AutoCaller autoCaller(that->mVirtualBox); + /* VirtualBox has been early uninitialized, terminate */ + if (!autoCaller.isOk()) + break; + + do + { + /* release the caller to let uninit() ever proceed */ + autoCaller.release(); + + DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned), + handles, + FALSE, + INFINITE); + + /* Restore the caller before using VirtualBox. If it fails, this + * means VirtualBox is being uninitialized and we must terminate. */ + autoCaller.add(); + if (!autoCaller.isOk()) + break; + + bool update = false; + + if (rc == WAIT_OBJECT_0) + { + /* update event is signaled */ + update = true; + } + else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt)) + { + /* machine mutex is released */ + (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath(); + update = true; + } + else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt)) + { + /* machine mutex is abandoned due to client process termination */ + (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath(); + update = true; + } + else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned)) + { + /* spawned VM process has terminated (normally or abnormally) */ + (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])-> + checkForSpawnFailure(); + update = true; + } + + if (update) + { + /* close old process handles */ + for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i) + CloseHandle(handles[i]); + + // get reference to the machines list in VirtualBox + VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList(); + + // lock the machines list for reading + AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); + + /* obtain a new set of opened machines */ + cnt = 0; + machines.clear(); + + for (MachinesOList::iterator it = allMachines.begin(); + it != allMachines.end(); + ++it) + { + /// @todo handle situations with more than 64 objects + AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS, + ("MAXIMUM_WAIT_OBJECTS reached")); + + ComObjPtr<SessionMachine> sm; + if ((*it)->isSessionOpenOrClosing(sm)) + { + AutoCaller smCaller(sm); + if (smCaller.isOk()) + { + AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS); + Machine::ClientToken *ct = sm->getClientToken(); + if (ct) + { + HANDLE ipcSem = ct->getToken(); + machines.push_back(sm); + handles[1 + cnt] = ipcSem; + ++cnt; + } + } + } + } + + LogFlowFunc(("UPDATE: direct session count = %d\n", cnt)); + + /* obtain a new set of spawned machines */ + cntSpawned = 0; + spawnedMachines.clear(); + + for (MachinesOList::iterator it = allMachines.begin(); + it != allMachines.end(); + ++it) + { + /// @todo handle situations with more than 64 objects + AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS, + ("MAXIMUM_WAIT_OBJECTS reached")); + + if ((*it)->isSessionSpawning()) + { + ULONG pid; + HRESULT hrc = (*it)->COMGETTER(SessionPID)(&pid); + if (SUCCEEDED(hrc)) + { + HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid); + AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n", + pid, GetLastError())); + if (ph != NULL) + { + spawnedMachines.push_back(*it); + handles[1 + cnt + cntSpawned] = ph; + ++cntSpawned; + } + } + } + } + + LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned)); + + // machines lock unwinds here + } + } + while (true); + } + while (0); + + /* close old process handles */ + for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i) + CloseHandle(handles[i]); + + /* release sets of machines if any */ + machines.clear(); + spawnedMachines.clear(); + + ::CoUninitialize(); + +#elif defined(RT_OS_OS2) + + /// @todo (dmik) processes reaping! + + /* according to PMREF, 64 is the maximum for the muxwait list */ + SEMRECORD handles[64]; + + HMUX muxSem = NULLHANDLE; + + do + { + AutoCaller autoCaller(that->mVirtualBox); + /* VirtualBox has been early uninitialized, terminate */ + if (!autoCaller.isOk()) + break; + + do + { + /* release the caller to let uninit() ever proceed */ + autoCaller.release(); + + int vrc = RTSemEventWait(that->mUpdateReq, 500); + + /* Restore the caller before using VirtualBox. If it fails, this + * means VirtualBox is being uninitialized and we must terminate. */ + autoCaller.add(); + if (!autoCaller.isOk()) + break; + + bool update = false; + bool updateSpawned = false; + + if (RT_SUCCESS(vrc)) + { + /* update event is signaled */ + update = true; + updateSpawned = true; + } + else + { + AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED, + ("RTSemEventWait returned %Rrc\n", vrc)); + + /* are there any mutexes? */ + if (cnt > 0) + { + /* figure out what's going on with machines */ + + unsigned long semId = 0; + APIRET arc = ::DosWaitMuxWaitSem(muxSem, + SEM_IMMEDIATE_RETURN, &semId); + + if (arc == NO_ERROR) + { + /* machine mutex is normally released */ + Assert(semId >= 0 && semId < cnt); + if (semId >= 0 && semId < cnt) + { +#if 0//def DEBUG + { + AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS); + LogFlowFunc(("released mutex: machine='%ls'\n", + machines[semId]->name().raw())); + } +#endif + machines[semId]->checkForDeath(); + } + update = true; + } + else if (arc == ERROR_SEM_OWNER_DIED) + { + /* machine mutex is abandoned due to client process + * termination; find which mutex is in the Owner Died + * state */ + for (size_t i = 0; i < cnt; ++i) + { + PID pid; TID tid; + unsigned long reqCnt; + arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt); + if (arc == ERROR_SEM_OWNER_DIED) + { + /* close the dead mutex as asked by PMREF */ + ::DosCloseMutexSem((HMTX)handles[i].hsemCur); + + Assert(i >= 0 && i < cnt); + if (i >= 0 && i < cnt) + { +#if 0//def DEBUG + { + AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS); + LogFlowFunc(("mutex owner dead: machine='%ls'\n", + machines[i]->name().raw())); + } +#endif + machines[i]->checkForDeath(); + } + } + } + update = true; + } + else + AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT, + ("DosWaitMuxWaitSem returned %d\n", arc)); + } + + /* are there any spawning sessions? */ + if (cntSpawned > 0) + { + for (size_t i = 0; i < cntSpawned; ++i) + updateSpawned |= (spawnedMachines[i])-> + checkForSpawnFailure(); + } + } + + if (update || updateSpawned) + { + // get reference to the machines list in VirtualBox + VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList(); + + // lock the machines list for reading + AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); + + if (update) + { + /* close the old muxsem */ + if (muxSem != NULLHANDLE) + ::DosCloseMuxWaitSem(muxSem); + + /* obtain a new set of opened machines */ + cnt = 0; + machines.clear(); + + for (MachinesOList::iterator it = allMachines.begin(); + it != allMachines.end(); ++it) + { + /// @todo handle situations with more than 64 objects + AssertMsg(cnt <= 64 /* according to PMREF */, + ("maximum of 64 mutex semaphores reached (%d)", + cnt)); + + ComObjPtr<SessionMachine> sm; + if ((*it)->isSessionOpenOrClosing(sm)) + { + AutoCaller smCaller(sm); + if (smCaller.isOk()) + { + AutoReadLock smLock(sm COMMA_LOCKVAL_SRC_POS); + ClientToken *ct = sm->getClientToken(); + if (ct) + { + HMTX ipcSem = ct->getToken(); + machines.push_back(sm); + handles[cnt].hsemCur = (HSEM)ipcSem; + handles[cnt].ulUser = cnt; + ++cnt; + } + } + } + } + + LogFlowFunc(("UPDATE: direct session count = %d\n", cnt)); + + if (cnt > 0) + { + /* create a new muxsem */ + APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt, + handles, + DCMW_WAIT_ANY); + AssertMsg(arc == NO_ERROR, + ("DosCreateMuxWaitSem returned %d\n", arc)); + NOREF(arc); + } + } + + if (updateSpawned) + { + /* obtain a new set of spawned machines */ + spawnedMachines.clear(); + + for (MachinesOList::iterator it = allMachines.begin(); + it != allMachines.end(); ++it) + { + if ((*it)->isSessionSpawning()) + spawnedMachines.push_back(*it); + } + + cntSpawned = spawnedMachines.size(); + LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned)); + } + } + } + while (true); + } + while (0); + + /* close the muxsem */ + if (muxSem != NULLHANDLE) + ::DosCloseMuxWaitSem(muxSem); + + /* release sets of machines if any */ + machines.clear(); + spawnedMachines.clear(); + +#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) + + bool update = false; + bool updateSpawned = false; + + do + { + AutoCaller autoCaller(that->mVirtualBox); + if (!autoCaller.isOk()) + break; + + do + { + /* release the caller to let uninit() ever proceed */ + autoCaller.release(); + + /* determine wait timeout adaptively: after updating information + * relevant to the client watcher, check a few times more + * frequently. This ensures good reaction time when the signalling + * has to be done a bit before the actual change for technical + * reasons, and saves CPU cycles when no activities are expected. */ + RTMSINTERVAL cMillies; + { + uint8_t uOld, uNew; + do + { + uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr); + uNew = uOld ? uOld - 1 : uOld; + } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld)); + Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1); + cMillies = s_aUpdateTimeoutSteps[uOld]; + } + + int rc = RTSemEventWait(that->mUpdateReq, cMillies); + + /* + * Restore the caller before using VirtualBox. If it fails, this + * means VirtualBox is being uninitialized and we must terminate. + */ + autoCaller.add(); + if (!autoCaller.isOk()) + break; + + if (RT_SUCCESS(rc) || update || updateSpawned) + { + /* RT_SUCCESS(rc) means an update event is signaled */ + + // get reference to the machines list in VirtualBox + VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList(); + + // lock the machines list for reading + AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); + + if (RT_SUCCESS(rc) || update) + { + /* obtain a new set of opened machines */ + machines.clear(); + + for (MachinesOList::iterator it = allMachines.begin(); + it != allMachines.end(); + ++it) + { + ComObjPtr<SessionMachine> sm; + if ((*it)->isSessionOpenOrClosing(sm)) + machines.push_back(sm); + } + + cnt = machines.size(); + LogFlowFunc(("UPDATE: direct session count = %d\n", cnt)); + } + + if (RT_SUCCESS(rc) || updateSpawned) + { + /* obtain a new set of spawned machines */ + spawnedMachines.clear(); + + for (MachinesOList::iterator it = allMachines.begin(); + it != allMachines.end(); + ++it) + { + if ((*it)->isSessionSpawning()) + spawnedMachines.push_back(*it); + } + + cntSpawned = spawnedMachines.size(); + LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned)); + } + + // machines lock unwinds here + } + + update = false; + for (size_t i = 0; i < cnt; ++i) + update |= (machines[i])->checkForDeath(); + + updateSpawned = false; + for (size_t i = 0; i < cntSpawned; ++i) + updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure(); + + /* reap child processes */ + { + AutoWriteLock alock(that->mLock COMMA_LOCKVAL_SRC_POS); + if (that->mProcesses.size()) + { + LogFlowFunc(("UPDATE: child process count = %d\n", + that->mProcesses.size())); + VirtualBox::ClientWatcher::ProcessList::iterator it = that->mProcesses.begin(); + while (it != that->mProcesses.end()) + { + RTPROCESS pid = *it; + RTPROCSTATUS status; + int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status); + if (vrc == VINF_SUCCESS) + { + if ( status.enmReason != RTPROCEXITREASON_NORMAL + || status.iStatus != RTEXITCODE_SUCCESS) + { + switch (status.enmReason) + { + default: + case RTPROCEXITREASON_NORMAL: + LogRel(("Reaper: Pid %d (%x) exited normally: %d (%#x)\n", + pid, pid, status.iStatus, status.iStatus)); + break; + case RTPROCEXITREASON_ABEND: + LogRel(("Reaper: Pid %d (%x) abended: %d (%#x)\n", + pid, pid, status.iStatus, status.iStatus)); + break; + case RTPROCEXITREASON_SIGNAL: + LogRel(("Reaper: Pid %d (%x) was signalled: %d (%#x)\n", + pid, pid, status.iStatus, status.iStatus)); + break; + } + } + else + LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n", + pid, pid, status.iStatus, + status.enmReason)); + it = that->mProcesses.erase(it); + } + else + { + LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n", + pid, pid, vrc)); + if (vrc != VERR_PROCESS_RUNNING) + { + /* remove the process if it is not already running */ + it = that->mProcesses.erase(it); + } + else + ++it; + } + } + } + } + } + while (true); + } + while (0); + + /* release sets of machines if any */ + machines.clear(); + spawnedMachines.clear(); + +#elif defined(VBOX_WITH_GENERIC_SESSION_WATCHER) + + bool updateSpawned = false; + + do + { + AutoCaller autoCaller(that->mVirtualBox); + if (!autoCaller.isOk()) + break; + + do + { + /* release the caller to let uninit() ever proceed */ + autoCaller.release(); + + /* determine wait timeout adaptively: after updating information + * relevant to the client watcher, check a few times more + * frequently. This ensures good reaction time when the signalling + * has to be done a bit before the actual change for technical + * reasons, and saves CPU cycles when no activities are expected. */ + RTMSINTERVAL cMillies; + { + uint8_t uOld, uNew; + do + { + uOld = ASMAtomicUoReadU8(&that->mUpdateAdaptCtr); + uNew = uOld ? uOld - 1 : uOld; + } while (!ASMAtomicCmpXchgU8(&that->mUpdateAdaptCtr, uNew, uOld)); + Assert(uOld <= RT_ELEMENTS(s_aUpdateTimeoutSteps) - 1); + cMillies = s_aUpdateTimeoutSteps[uOld]; + } + + int rc = RTSemEventWait(that->mUpdateReq, cMillies); + + /* + * Restore the caller before using VirtualBox. If it fails, this + * means VirtualBox is being uninitialized and we must terminate. + */ + autoCaller.add(); + if (!autoCaller.isOk()) + break; + + /** @todo this quite big effort for catching machines in spawning + * state which can't be caught by the token mechanism (as the token + * can't be in the other process yet) could be eliminated if the + * reaping is made smarter, having cross-reference information + * from the pid to the corresponding machine object. Both cases do + * more or less the same thing anyway. */ + if (RT_SUCCESS(rc) || updateSpawned) + { + /* RT_SUCCESS(rc) means an update event is signaled */ + + // get reference to the machines list in VirtualBox + VirtualBox::MachinesOList &allMachines = that->mVirtualBox->getMachinesList(); + + // lock the machines list for reading + AutoReadLock thatLock(allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); + + if (RT_SUCCESS(rc) || updateSpawned) + { + /* obtain a new set of spawned machines */ + spawnedMachines.clear(); + + for (MachinesOList::iterator it = allMachines.begin(); + it != allMachines.end(); + ++it) + { + if ((*it)->isSessionSpawning()) + spawnedMachines.push_back(*it); + } + + cntSpawned = spawnedMachines.size(); + LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned)); + } + + NOREF(cnt); + // machines lock unwinds here + } + + updateSpawned = false; + for (size_t i = 0; i < cntSpawned; ++i) + updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure(); + + /* reap child processes */ + { + AutoWriteLock alock(that->mLock COMMA_LOCKVAL_SRC_POS); + if (that->mProcesses.size()) + { + LogFlowFunc(("UPDATE: child process count = %d\n", + that->mProcesses.size())); + VirtualBox::ClientWatcher::ProcessList::iterator it = that->mProcesses.begin(); + while (it != that->mProcesses.end()) + { + RTPROCESS pid = *it; + RTPROCSTATUS status; + int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status); + if (vrc == VINF_SUCCESS) + { + if ( status.enmReason != RTPROCEXITREASON_NORMAL + || status.iStatus != RTEXITCODE_SUCCESS) + { + switch (status.enmReason) + { + default: + case RTPROCEXITREASON_NORMAL: + LogRel(("Reaper: Pid %d (%x) exited normally: %d (%#x)\n", + pid, pid, status.iStatus, status.iStatus)); + break; + case RTPROCEXITREASON_ABEND: + LogRel(("Reaper: Pid %d (%x) abended: %d (%#x)\n", + pid, pid, status.iStatus, status.iStatus)); + break; + case RTPROCEXITREASON_SIGNAL: + LogRel(("Reaper: Pid %d (%x) was signalled: %d (%#x)\n", + pid, pid, status.iStatus, status.iStatus)); + break; + } + } + else + LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n", + pid, pid, status.iStatus, + status.enmReason)); + it = that->mProcesses.erase(it); + } + else + { + LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n", + pid, pid, vrc)); + if (vrc != VERR_PROCESS_RUNNING) + { + /* remove the process if it is not already running */ + it = that->mProcesses.erase(it); + } + else + ++it; + } + } + } + } + } + while (true); + } + while (0); + + /* release sets of machines if any */ + machines.clear(); + spawnedMachines.clear(); + +#else +# error "Port me!" +#endif + + VirtualBoxBase::uninitializeComForThread(); + + LogFlowFuncLeave(); + return 0; +} +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/DHCPServerImpl.cpp b/src/VBox/Main/src-server/DHCPServerImpl.cpp index 45f21370..1cd7ce78 100644 --- a/src/VBox/Main/src-server/DHCPServerImpl.cpp +++ b/src/VBox/Main/src-server/DHCPServerImpl.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -17,34 +17,65 @@ * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ -#include "DHCPServerRunner.h" +#include <string> +#include "NetworkServiceRunner.h" #include "DHCPServerImpl.h" #include "AutoCaller.h" #include "Logging.h" #include <iprt/cpp/utils.h> +#include <VBox/com/array.h> #include <VBox/settings.h> #include "VirtualBoxImpl.h" // constructor / destructor ///////////////////////////////////////////////////////////////////////////// +const std::string DHCPServerRunner::kDsrKeyGateway = "--gateway"; +const std::string DHCPServerRunner::kDsrKeyLowerIp = "--lower-ip"; +const std::string DHCPServerRunner::kDsrKeyUpperIp = "--upper-ip"; + + +struct DHCPServer::Data +{ + Data() : enabled(FALSE) {} + + Bstr IPAddress; + Bstr lowerIP; + Bstr upperIP; + + BOOL enabled; + DHCPServerRunner dhcp; + + DhcpOptionMap GlobalDhcpOptions; + VmSlot2OptionsMap VmSlot2Options; +}; + DHCPServer::DHCPServer() - : mVirtualBox(NULL) + : m(NULL), mVirtualBox(NULL) { + m = new DHCPServer::Data(); } + DHCPServer::~DHCPServer() { + if (m) + { + delete m; + m = NULL; + } } + HRESULT DHCPServer::FinalConstruct() { return BaseFinalConstruct(); } + void DHCPServer::FinalRelease() { uninit (); @@ -52,6 +83,7 @@ void DHCPServer::FinalRelease() BaseFinalRelease(); } + void DHCPServer::uninit() { /* Enclose the state transition Ready->InUninit->NotReady */ @@ -62,6 +94,7 @@ void DHCPServer::uninit() unconst(mVirtualBox) = NULL; } + HRESULT DHCPServer::init(VirtualBox *aVirtualBox, IN_BSTR aName) { AssertReturn(aName != NULL, E_INVALIDARG); @@ -73,11 +106,12 @@ HRESULT DHCPServer::init(VirtualBox *aVirtualBox, IN_BSTR aName) unconst(mVirtualBox) = aVirtualBox; unconst(mName) = aName; - m.IPAddress = "0.0.0.0"; - m.networkMask = "0.0.0.0"; - m.enabled = FALSE; - m.lowerIP = "0.0.0.0"; - m.upperIP = "0.0.0.0"; + m->IPAddress = "0.0.0.0"; + m->GlobalDhcpOptions.insert(DhcpOptValuePair(DhcpOpt_SubnetMask, Bstr("0.0.0.0"))); + m->enabled = FALSE; + + m->lowerIP = "0.0.0.0"; + m->upperIP = "0.0.0.0"; /* Confirm a successful initialization */ autoInitSpan.setSucceeded(); @@ -85,6 +119,7 @@ HRESULT DHCPServer::init(VirtualBox *aVirtualBox, IN_BSTR aName) return S_OK; } + HRESULT DHCPServer::init(VirtualBox *aVirtualBox, const settings::DHCPServer &data) { @@ -96,17 +131,25 @@ HRESULT DHCPServer::init(VirtualBox *aVirtualBox, unconst(mVirtualBox) = aVirtualBox; unconst(mName) = data.strNetworkName; - m.IPAddress = data.strIPAddress; - m.networkMask = data.strIPNetworkMask; - m.enabled = data.fEnabled; - m.lowerIP = data.strIPLower; - m.upperIP = data.strIPUpper; + m->IPAddress = data.strIPAddress; + m->enabled = data.fEnabled; + m->lowerIP = data.strIPLower; + m->upperIP = data.strIPUpper; + + m->GlobalDhcpOptions.clear(); + m->GlobalDhcpOptions.insert(data.GlobalDhcpOptions.begin(), + data.GlobalDhcpOptions.end()); + + m->VmSlot2Options.clear(); + m->VmSlot2Options.insert(data.VmSlot2OptionsM.begin(), + data.VmSlot2OptionsM.end()); autoInitSpan.setSucceeded(); return S_OK; } + HRESULT DHCPServer::saveSettings(settings::DHCPServer &data) { AutoCaller autoCaller(this); @@ -115,15 +158,24 @@ HRESULT DHCPServer::saveSettings(settings::DHCPServer &data) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); data.strNetworkName = mName; - data.strIPAddress = m.IPAddress; - data.strIPNetworkMask = m.networkMask; - data.fEnabled = !!m.enabled; - data.strIPLower = m.lowerIP; - data.strIPUpper = m.upperIP; + data.strIPAddress = m->IPAddress; + + data.fEnabled = !!m->enabled; + data.strIPLower = m->lowerIP; + data.strIPUpper = m->upperIP; + + data.GlobalDhcpOptions.clear(); + data.GlobalDhcpOptions.insert(m->GlobalDhcpOptions.begin(), + m->GlobalDhcpOptions.end()); + + data.VmSlot2OptionsM.clear(); + data.VmSlot2OptionsM.insert(m->VmSlot2Options.begin(), + m->VmSlot2Options.end()); return S_OK; } + STDMETHODIMP DHCPServer::COMGETTER(NetworkName) (BSTR *aName) { CheckComArgOutPointerValid(aName); @@ -136,6 +188,7 @@ STDMETHODIMP DHCPServer::COMGETTER(NetworkName) (BSTR *aName) return S_OK; } + STDMETHODIMP DHCPServer::COMGETTER(Enabled) (BOOL *aEnabled) { CheckComArgOutPointerValid(aEnabled); @@ -143,18 +196,19 @@ STDMETHODIMP DHCPServer::COMGETTER(Enabled) (BOOL *aEnabled) AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - *aEnabled = m.enabled; + *aEnabled = m->enabled; return S_OK; } + STDMETHODIMP DHCPServer::COMSETTER(Enabled) (BOOL aEnabled) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - m.enabled = aEnabled; + m->enabled = aEnabled; // save the global settings; for that we should hold only the VirtualBox lock alock.release(); @@ -164,6 +218,7 @@ STDMETHODIMP DHCPServer::COMSETTER(Enabled) (BOOL aEnabled) return rc; } + STDMETHODIMP DHCPServer::COMGETTER(IPAddress) (BSTR *aIPAddress) { CheckComArgOutPointerValid(aIPAddress); @@ -171,11 +226,12 @@ STDMETHODIMP DHCPServer::COMGETTER(IPAddress) (BSTR *aIPAddress) AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - m.IPAddress.cloneTo(aIPAddress); + m->IPAddress.cloneTo(aIPAddress); return S_OK; } + STDMETHODIMP DHCPServer::COMGETTER(NetworkMask) (BSTR *aNetworkMask) { CheckComArgOutPointerValid(aNetworkMask); @@ -183,11 +239,12 @@ STDMETHODIMP DHCPServer::COMGETTER(NetworkMask) (BSTR *aNetworkMask) AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - m.networkMask.cloneTo(aNetworkMask); + m->GlobalDhcpOptions[DhcpOpt_SubnetMask].cloneTo(aNetworkMask); return S_OK; } + STDMETHODIMP DHCPServer::COMGETTER(LowerIP) (BSTR *aIPAddress) { CheckComArgOutPointerValid(aIPAddress); @@ -195,11 +252,12 @@ STDMETHODIMP DHCPServer::COMGETTER(LowerIP) (BSTR *aIPAddress) AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - m.lowerIP.cloneTo(aIPAddress); + m->lowerIP.cloneTo(aIPAddress); return S_OK; } + STDMETHODIMP DHCPServer::COMGETTER(UpperIP) (BSTR *aIPAddress) { CheckComArgOutPointerValid(aIPAddress); @@ -207,11 +265,12 @@ STDMETHODIMP DHCPServer::COMGETTER(UpperIP) (BSTR *aIPAddress) AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - m.upperIP.cloneTo(aIPAddress); + m->upperIP.cloneTo(aIPAddress); return S_OK; } + STDMETHODIMP DHCPServer::SetConfiguration (IN_BSTR aIPAddress, IN_BSTR aNetworkMask, IN_BSTR aLowerIP, IN_BSTR aUpperIP) { AssertReturn(aIPAddress != NULL, E_INVALIDARG); @@ -223,10 +282,11 @@ STDMETHODIMP DHCPServer::SetConfiguration (IN_BSTR aIPAddress, IN_BSTR aNetworkM if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - m.IPAddress = aIPAddress; - m.networkMask = aNetworkMask; - m.lowerIP = aLowerIP; - m.upperIP = aUpperIP; + m->IPAddress = aIPAddress; + m->GlobalDhcpOptions[DhcpOpt_SubnetMask] = aNetworkMask; + + m->lowerIP = aLowerIP; + m->upperIP = aUpperIP; // save the global settings; for that we should hold only the VirtualBox lock alock.release(); @@ -234,43 +294,251 @@ STDMETHODIMP DHCPServer::SetConfiguration (IN_BSTR aIPAddress, IN_BSTR aNetworkM return mVirtualBox->saveSettings(); } + +STDMETHODIMP DHCPServer::AddGlobalOption(DhcpOpt_T aOption, IN_BSTR aValue) +{ + CheckComArgStr(aValue); + /* store global option */ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + m->GlobalDhcpOptions.insert( + DhcpOptValuePair(aOption, Utf8Str(aValue))); + + /* Indirect way to understand that we're on NAT network */ + if (aOption == DhcpOpt_Router) + m->dhcp.setOption(NetworkServiceRunner::kNsrKeyNeedMain, "on"); + + alock.release(); + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); +} + + +STDMETHODIMP DHCPServer::COMGETTER(GlobalOptions)(ComSafeArrayOut(BSTR, aValue)) +{ + CheckComArgOutSafeArrayPointerValid(aValue); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + SafeArray<BSTR> sf(m->GlobalDhcpOptions.size()); + int i = 0; + + for (DhcpOptIterator it = m->GlobalDhcpOptions.begin(); + it != m->GlobalDhcpOptions.end(); ++it) + { + Bstr(Utf8StrFmt("%d:%s", (*it).first, (*it).second.c_str())).detachTo(&sf[i]); + i++; + } + + sf.detachTo(ComSafeArrayOutArg(aValue)); + + return S_OK; +} + + +STDMETHODIMP DHCPServer::COMGETTER(VmConfigs)(ComSafeArrayOut(BSTR, aValue)) +{ + CheckComArgOutSafeArrayPointerValid(aValue); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + SafeArray<BSTR> sf(m->VmSlot2Options.size()); + VmSlot2OptionsIterator it = m->VmSlot2Options.begin(); + int i = 0; + for (;it != m->VmSlot2Options.end(); ++it) + { + Bstr(Utf8StrFmt("[%s]:%d", + it->first.VmName.c_str(), + it->first.Slot)).detachTo(&sf[i]); + i++; + } + + sf.detachTo(ComSafeArrayOutArg(aValue)); + + return S_OK; +} + + +STDMETHODIMP DHCPServer::AddVmSlotOption(IN_BSTR aVmName, LONG aSlot, DhcpOpt_T aOption, IN_BSTR aValue) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + m->VmSlot2Options[settings::VmNameSlotKey( + com::Utf8Str(aVmName), + aSlot)][aOption] = com::Utf8Str(aValue); + + + alock.release(); + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); +} + + +STDMETHODIMP DHCPServer::RemoveVmSlotOptions(IN_BSTR aVmName, LONG aSlot) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + DhcpOptionMap& map = findOptMapByVmNameSlot(com::Utf8Str(aVmName), aSlot); + + map.clear(); + + alock.release(); + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); +} + + +/** + * this is mapping (vm, slot) + */ +STDMETHODIMP DHCPServer::GetVmSlotOptions(IN_BSTR aVmName, + LONG aSlot, + ComSafeArrayOut(BSTR, aValues)) +{ + CheckComArgOutSafeArrayPointerValid(aValues); + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + DhcpOptionMap& map = findOptMapByVmNameSlot(com::Utf8Str(aVmName), aSlot); + + SafeArray<BSTR> sf(map.size()); + int i = 0; + + for (DhcpOptIterator it = map.begin(); + it != map.end(); ++it) + { + Bstr(Utf8StrFmt("%d:%s", (*it).first, (*it).second.c_str())).detachTo(&sf[i]); + i++; + } + + sf.detachTo(ComSafeArrayOutArg(aValues)); + + return S_OK; +} + + +STDMETHODIMP DHCPServer::GetMacOptions(IN_BSTR aMAC, ComSafeArrayOut(BSTR, aValues)) +{ + CheckComArgOutSafeArrayPointerValid(aValues); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + HRESULT hrc = S_OK; + + ComPtr<IMachine> machine; + ComPtr<INetworkAdapter> nic; + + VmSlot2OptionsIterator it; + for(it = m->VmSlot2Options.begin(); + it != m->VmSlot2Options.end(); + ++it) + { + + alock.release(); + hrc = mVirtualBox->FindMachine(Bstr(it->first.VmName).raw(), machine.asOutParam()); + alock.acquire(); + + if (FAILED(hrc)) + continue; + + alock.release(); + hrc = machine->GetNetworkAdapter(it->first.Slot, nic.asOutParam()); + alock.acquire(); + + if (FAILED(hrc)) + continue; + + com::Bstr mac; + + alock.release(); + hrc = nic->COMGETTER(MACAddress)(mac.asOutParam()); + alock.acquire(); + + if (FAILED(hrc)) /* no MAC address ??? */ + break; + + if (!RTStrICmp(com::Utf8Str(mac).c_str(), com::Utf8Str(aMAC).c_str())) + return GetVmSlotOptions(Bstr(it->first.VmName).raw(), + it->first.Slot, + ComSafeArrayOutArg(aValues)); + } /* end of for */ + + return hrc; +} + + +STDMETHODIMP DHCPServer::COMGETTER(EventSource)(IEventSource **aEventSource) +{ + NOREF(aEventSource); + ReturnComNotImplemented(); +} + + STDMETHODIMP DHCPServer::Start(IN_BSTR aNetworkName, IN_BSTR aTrunkName, IN_BSTR aTrunkType) { /* Silently ignore attempts to run disabled servers. */ - if (!m.enabled) + if (!m->enabled) return S_OK; - m.dhcp.setOption(DHCPCFG_NETNAME, Utf8Str(aNetworkName), true); + /* Commmon Network Settings */ + m->dhcp.setOption(NetworkServiceRunner::kNsrKeyNetwork, Utf8Str(aNetworkName).c_str()); + Bstr tmp(aTrunkName); + if (!tmp.isEmpty()) - m.dhcp.setOption(DHCPCFG_TRUNKNAME, Utf8Str(tmp), true); - m.dhcp.setOption(DHCPCFG_TRUNKTYPE, Utf8Str(aTrunkType), true); - //temporary hack for testing - // DHCPCFG_NAME + m->dhcp.setOption(NetworkServiceRunner::kNsrTrunkName, Utf8Str(tmp).c_str()); + m->dhcp.setOption(NetworkServiceRunner::kNsrKeyTrunkType, Utf8Str(aTrunkType).c_str()); + + /* XXX: should this MAC default initialization moved to NetworkServiceRunner? */ char strMAC[32]; Guid guid; guid.create(); RTStrPrintf (strMAC, sizeof(strMAC), "08:00:27:%02X:%02X:%02X", - guid.raw()->au8[0], guid.raw()->au8[1], guid.raw()->au8[2]); - m.dhcp.setOption(DHCPCFG_MACADDRESS, strMAC, true); - m.dhcp.setOption(DHCPCFG_IPADDRESS, Utf8Str(m.IPAddress), true); - // DHCPCFG_LEASEDB, - // DHCPCFG_VERBOSE, - // DHCPCFG_GATEWAY, - m.dhcp.setOption(DHCPCFG_LOWERIP, Utf8Str(m.lowerIP), true); - m.dhcp.setOption(DHCPCFG_UPPERIP, Utf8Str(m.upperIP), true); - m.dhcp.setOption(DHCPCFG_NETMASK, Utf8Str(m.networkMask), true); - - // DHCPCFG_HELP, - // DHCPCFG_VERSION, - // DHCPCFG_NOTOPT_MAXVAL - m.dhcp.setOption(DHCPCFG_BEGINCONFIG, "", true); - - return RT_FAILURE(m.dhcp.start()) ? E_FAIL : S_OK; - //m.dhcp.detachFromServer(); /* need to do this to avoid server shutdown on runner destruction */ + guid.raw()->au8[0], + guid.raw()->au8[1], + guid.raw()->au8[2]); + m->dhcp.setOption(NetworkServiceRunner::kNsrMacAddress, strMAC); + m->dhcp.setOption(NetworkServiceRunner::kNsrIpAddress, Utf8Str(m->IPAddress).c_str()); + m->dhcp.setOption(NetworkServiceRunner::kNsrIpNetmask, Utf8Str(m->GlobalDhcpOptions[DhcpOpt_SubnetMask]).c_str()); + m->dhcp.setOption(DHCPServerRunner::kDsrKeyLowerIp, Utf8Str(m->lowerIP).c_str()); + m->dhcp.setOption(DHCPServerRunner::kDsrKeyUpperIp, Utf8Str(m->upperIP).c_str()); + + /* XXX: This parameters Dhcp Server will fetch via API */ + return RT_FAILURE(m->dhcp.start()) ? E_FAIL : S_OK; + //m->dhcp.detachFromServer(); /* need to do this to avoid server shutdown on runner destruction */ } + STDMETHODIMP DHCPServer::Stop (void) { - return RT_FAILURE(m.dhcp.stop()) ? E_FAIL : S_OK; + return RT_FAILURE(m->dhcp.stop()) ? E_FAIL : S_OK; +} + + +DhcpOptionMap& DHCPServer::findOptMapByVmNameSlot(const com::Utf8Str& aVmName, + LONG aSlot) +{ + return m->VmSlot2Options[settings::VmNameSlotKey( + com::Utf8Str(aVmName), + aSlot)]; } diff --git a/src/VBox/Main/src-server/HostDnsService.cpp b/src/VBox/Main/src-server/HostDnsService.cpp new file mode 100644 index 00000000..011528fb --- /dev/null +++ b/src/VBox/Main/src-server/HostDnsService.cpp @@ -0,0 +1,363 @@ +/* $Id: HostDnsService.cpp $ */ +/** @file + * Base class fo Host DNS & Co services. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <VBox/com/array.h> +#include <VBox/com/ptr.h> +#include <VBox/com/string.h> + +#include <iprt/cpp/utils.h> + +#include "Logging.h" +#include "VirtualBoxImpl.h" +#include <iprt/thread.h> +#include <iprt/semaphore.h> +#include <iprt/critsect.h> + +#include <algorithm> +#include <string> +#include "HostDnsService.h" + + +static HostDnsMonitor *g_monitor; + +static void dumpHostDnsInformation(const HostDnsInformation&); +static void dumpHostDnsStrVector(const std::string&, const std::vector<std::string>&); + +/* Lockee */ +Lockee::Lockee() +{ + RTCritSectInit(&mLock); +} + +Lockee::~Lockee() +{ + RTCritSectDelete(&mLock); +} + +const RTCRITSECT* Lockee::lock() const +{ + return &mLock; +} + +/* ALock */ +ALock::ALock(const Lockee *aLockee) + : lockee(aLockee) +{ + RTCritSectEnter(const_cast<PRTCRITSECT>(lockee->lock())); +} + +ALock::~ALock() +{ + RTCritSectLeave(const_cast<PRTCRITSECT>(lockee->lock())); +} + +inline static void detachVectorOfString(const std::vector<std::string>& v, + ComSafeArrayOut(BSTR, aBstrArray)) +{ + com::SafeArray<BSTR> aBstr(v.size()); + + std::vector<std::string>::const_iterator it; + + int i = 0; + it = v.begin(); + for (; it != v.end(); ++it, ++i) + Utf8Str(it->c_str()).cloneTo(&aBstr[i]); + + aBstr.detachTo(ComSafeArrayOutArg(aBstrArray)); +} + +struct HostDnsMonitor::Data +{ + Data(bool aThreaded):fThreaded(aThreaded){} + + std::vector<PCHostDnsMonitorProxy> proxies; + HostDnsInformation info; + const bool fThreaded; + RTTHREAD hMonitoringThread; + RTSEMEVENT hDnsInitEvent; +}; + +struct HostDnsMonitorProxy::Data +{ + Data(const HostDnsMonitor *aMonitor, const VirtualBox *aParent) + : info(NULL) + , virtualbox(aParent) + , monitor(aMonitor) + , fModified(true) + {} + + virtual ~Data() + { + if (info) + { + delete info; + info = NULL; + } + } + + HostDnsInformation *info; + const VirtualBox *virtualbox; + const HostDnsMonitor *monitor; + bool fModified; +}; + + +HostDnsMonitor::HostDnsMonitor(bool fThreaded) + : m(NULL) +{ + m = new HostDnsMonitor::Data(fThreaded); +} + +HostDnsMonitor::~HostDnsMonitor() +{ + if (m) + { + delete m; + m = NULL; + } +} + +const HostDnsMonitor *HostDnsMonitor::getHostDnsMonitor() +{ + /* XXX: Moved initialization from HostImpl.cpp */ + if (!g_monitor) + { +# if defined (RT_OS_DARWIN) + g_monitor = new HostDnsServiceDarwin(); +# elif defined(RT_OS_WINDOWS) + g_monitor = new HostDnsServiceWin(); +# elif defined(RT_OS_LINUX) + g_monitor = new HostDnsServiceLinux(); +# elif defined(RT_OS_SOLARIS) + g_monitor = new HostDnsServiceSolaris(); +# elif defined(RT_OS_FREEBSD) + g_monitor = new HostDnsServiceFreebsd(); +# elif defined(RT_OS_OS2) + g_monitor = new HostDnsServiceOs2(); +# else + g_monitor = new HostDnsService(); +# endif + g_monitor->init(); + } + + return g_monitor; +} + +void HostDnsMonitor::addMonitorProxy(PCHostDnsMonitorProxy proxy) const +{ + ALock l(this); + m->proxies.push_back(proxy); + proxy->notify(); +} + +void HostDnsMonitor::releaseMonitorProxy(PCHostDnsMonitorProxy proxy) const +{ + ALock l(this); + std::vector<PCHostDnsMonitorProxy>::iterator it; + it = std::find(m->proxies.begin(), m->proxies.end(), proxy); + + if (it == m->proxies.end()) + return; + + m->proxies.erase(it); +} + +void HostDnsMonitor::shutdown() +{ + if (g_monitor) + { + delete g_monitor; + g_monitor = NULL; + } +} + +const HostDnsInformation &HostDnsMonitor::getInfo() const +{ + return m->info; +} + +void HostDnsMonitor::notifyAll() const +{ + ALock l(this); + std::vector<PCHostDnsMonitorProxy>::const_iterator it; + for (it = m->proxies.begin(); it != m->proxies.end(); ++it) + (*it)->notify(); +} + +void HostDnsMonitor::setInfo(const HostDnsInformation &info) +{ + ALock l(this); + m->info = info; +} + +HRESULT HostDnsMonitor::init() +{ + if (m->fThreaded) + { + int rc = RTSemEventCreate(&m->hDnsInitEvent); + AssertRCReturn(rc, E_FAIL); + + rc = RTThreadCreate(&m->hMonitoringThread, + HostDnsMonitor::threadMonitoringRoutine, + this, 128 * _1K, RTTHREADTYPE_IO, 0, "dns-monitor"); + AssertRCReturn(rc, E_FAIL); + + RTSemEventWait(m->hDnsInitEvent, RT_INDEFINITE_WAIT); + } + return S_OK; +} + + +void HostDnsMonitor::monitorThreadInitializationDone() +{ + RTSemEventSignal(m->hDnsInitEvent); +} + + +int HostDnsMonitor::threadMonitoringRoutine(RTTHREAD, void *pvUser) +{ + HostDnsMonitor *pThis = static_cast<HostDnsMonitor *>(pvUser); + return pThis->monitorWorker(); +} + +/* HostDnsMonitorProxy */ +HostDnsMonitorProxy::HostDnsMonitorProxy() + : m(NULL) +{ +} + +HostDnsMonitorProxy::~HostDnsMonitorProxy() +{ + if (m) + { + if (m->monitor) + m->monitor->releaseMonitorProxy(this); + delete m; + m = NULL; + } +} + +void HostDnsMonitorProxy::init(const HostDnsMonitor *mon, const VirtualBox* aParent) +{ + m = new HostDnsMonitorProxy::Data(mon, aParent); + m->monitor->addMonitorProxy(this); + updateInfo(); +} + +void HostDnsMonitorProxy::notify() const +{ + m->fModified = true; + const_cast<VirtualBox *>(m->virtualbox)->onHostNameResolutionConfigurationChange(); +} + +HRESULT HostDnsMonitorProxy::GetNameServers(ComSafeArrayOut(BSTR, aNameServers)) +{ + AssertReturn(m && m->info, E_FAIL); + ALock l(this); + + if (m->fModified) + updateInfo(); + + LogRel(("HostDnsMonitorProxy::GetNameServers:\n")); + dumpHostDnsStrVector("Name Server", m->info->servers); + + detachVectorOfString(m->info->servers, ComSafeArrayOutArg(aNameServers)); + + return S_OK; +} + +HRESULT HostDnsMonitorProxy::GetDomainName(BSTR *aDomainName) +{ + AssertReturn(m && m->info, E_FAIL); + ALock l(this); + + if (m->fModified) + updateInfo(); + + LogRel(("HostDnsMonitorProxy::GetDomainName: %s\n", m->info->domain.c_str())); + + Utf8Str(m->info->domain.c_str()).cloneTo(aDomainName); + + return S_OK; +} + +HRESULT HostDnsMonitorProxy::GetSearchStrings(ComSafeArrayOut(BSTR, aSearchStrings)) +{ + AssertReturn(m && m->info, E_FAIL); + ALock l(this); + + if (m->fModified) + updateInfo(); + + LogRel(("HostDnsMonitorProxy::GetSearchStrings:\n")); + dumpHostDnsStrVector("Search String", m->info->searchList); + + detachVectorOfString(m->info->searchList, ComSafeArrayOutArg(aSearchStrings)); + + return S_OK; +} + +bool HostDnsMonitorProxy::operator==(PCHostDnsMonitorProxy& rhs) +{ + if (!m || !rhs->m) + return false; + + /** + * we've assigned to the same instance of VirtualBox. + */ + return m->virtualbox == rhs->m->virtualbox; +} + +void HostDnsMonitorProxy::updateInfo() +{ + HostDnsInformation *info = new HostDnsInformation(m->monitor->getInfo()); + HostDnsInformation *old = m->info; + + LogRel(("HostDnsMonitorProxy: Host's DNS information updated:\n")); + dumpHostDnsInformation(*info); + + m->info = info; + if (old) + { + LogRel(("HostDnsMonitorProxy: Old host information:\n")); + dumpHostDnsInformation(*old); + + delete old; + } + + m->fModified = false; +} + + +static void dumpHostDnsInformation(const HostDnsInformation& info) +{ + dumpHostDnsStrVector("DNS server", info.servers); + dumpHostDnsStrVector("SearchString", info.searchList); + + if (!info.domain.empty()) + LogRel(("DNS domain: %s\n", info.domain.c_str())); +} + + +static void dumpHostDnsStrVector(const std::string& prefix, const std::vector<std::string>& v) +{ + int i = 1; + for (std::vector<std::string>::const_iterator it = v.begin(); + it != v.end(); + ++it, ++i) + LogRel(("%s %d: %s\n", prefix.c_str(), i, it->c_str())); +} diff --git a/src/VBox/Main/src-server/HostDnsService.h b/src/VBox/Main/src-server/HostDnsService.h new file mode 100644 index 00000000..56da78d0 --- /dev/null +++ b/src/VBox/Main/src-server/HostDnsService.h @@ -0,0 +1,236 @@ +/* $Id: HostDnsService.h $ */ +/** @file + * Host DNS listener. + */ + +/* + * Copyright (C) 2005-2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#ifndef ___H_DNSHOSTSERVICE +#define ___H_DNSHOSTSERVICE +#include "VirtualBoxBase.h" + +#include <iprt/cdefs.h> +#include <iprt/critsect.h> +#include <iprt/types.h> + +#include <list> +#include <vector> + +typedef std::list<com::Utf8Str> Utf8StrList; +typedef Utf8StrList::iterator Utf8StrListIterator; + +class HostDnsMonitorProxy; +typedef const HostDnsMonitorProxy *PCHostDnsMonitorProxy; + +class Lockee +{ + public: + Lockee(); + virtual ~Lockee(); + const RTCRITSECT* lock() const; + + private: + RTCRITSECT mLock; +}; + +class ALock +{ + public: + explicit ALock(const Lockee *l); + ~ALock(); + + private: + const Lockee *lockee; +}; + +class HostDnsInformation +{ + public: + std::vector<std::string> servers; + std::string domain; + std::vector<std::string> searchList; +}; + +/** + * This class supposed to be a real DNS monitor object it should be singleton, + * it lifecycle starts and ends together with VBoxSVC. + */ +class HostDnsMonitor : public Lockee +{ + public: + static const HostDnsMonitor *getHostDnsMonitor(); + static void shutdown(); + + void addMonitorProxy(PCHostDnsMonitorProxy) const; + void releaseMonitorProxy(PCHostDnsMonitorProxy) const; + const HostDnsInformation &getInfo() const; + /* @note: method will wait till client call + HostDnsService::monitorThreadInitializationDone() */ + virtual HRESULT init(); + + protected: + explicit HostDnsMonitor(bool fThreaded = false); + virtual ~HostDnsMonitor(); + + void notifyAll() const; + void setInfo(const HostDnsInformation &); + + /* this function used only if HostDnsMonitor::HostDnsMonitor(true) */ + void monitorThreadInitializationDone(); + virtual void monitorThreadShutdown() = 0; + virtual int monitorWorker() = 0; + + private: + HostDnsMonitor(const HostDnsMonitor &); + HostDnsMonitor& operator= (const HostDnsMonitor &); + static int threadMonitoringRoutine(RTTHREAD, void *); + + public: + struct Data; + Data *m; +}; + +/** + * This class supposed to be a proxy for events on changing Host Name Resolving configurations. + */ +class HostDnsMonitorProxy : public Lockee +{ + public: + HostDnsMonitorProxy(); + ~HostDnsMonitorProxy(); + void init(const HostDnsMonitor *aMonitor, const VirtualBox *aParent); + void notify() const; + + HRESULT GetNameServers(ComSafeArrayOut(BSTR, aNameServers)); + HRESULT GetDomainName(BSTR *aDomainName); + HRESULT GetSearchStrings(ComSafeArrayOut(BSTR, aSearchStrings)); + + bool operator==(PCHostDnsMonitorProxy&); + + private: + void updateInfo(); + + private: + struct Data; + Data *m; +}; + +# ifdef RT_OS_DARWIN +class HostDnsServiceDarwin : public HostDnsMonitor +{ + public: + HostDnsServiceDarwin(); + ~HostDnsServiceDarwin(); + HRESULT init(); + + protected: + virtual void monitorThreadShutdown(); + virtual int monitorWorker(); + + private: + HRESULT updateInfo(); + static void hostDnsServiceStoreCallback(void *store, void *arrayRef, void *info); + struct Data; + Data *m; +}; +# endif +# ifdef RT_OS_WINDOWS +class HostDnsServiceWin : public HostDnsMonitor +{ + public: + HostDnsServiceWin(); + ~HostDnsServiceWin(); + HRESULT init(); + + protected: + virtual void monitorThreadShutdown(); + virtual int monitorWorker(); + + private: + void strList2List(std::vector<std::string>& lst, char *strLst); + HRESULT updateInfo(); + + private: + struct Data; + Data *m; +}; +# endif +# if defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_OS2) || defined(RT_OS_FREEBSD) +class HostDnsServiceResolvConf: public HostDnsMonitor +{ + public: + explicit HostDnsServiceResolvConf(bool fThreaded = false) : HostDnsMonitor(fThreaded), m(NULL) {} + virtual ~HostDnsServiceResolvConf(); + virtual HRESULT init(const char *aResolvConfFileName); + const std::string& resolvConf() const; + + protected: + HRESULT readResolvConf(); + /* While not all hosts supports Hosts DNS change notifiaction + * default implementation offers return VERR_IGNORE. + */ + virtual void monitorThreadShutdown() {} + virtual int monitorWorker() {return VERR_IGNORED;} + + protected: + struct Data; + Data *m; +}; +# if defined(RT_OS_SOLARIS) +/** + * XXX: https://blogs.oracle.com/praks/entry/file_events_notification + */ +class HostDnsServiceSolaris : public HostDnsServiceResolvConf +{ + public: + HostDnsServiceSolaris(){} + ~HostDnsServiceSolaris(){} + HRESULT init(){ return HostDnsServiceResolvConf::init("/etc/resolv.conf");} +}; + +# elif defined(RT_OS_LINUX) +class HostDnsServiceLinux : public HostDnsServiceResolvConf +{ + public: + HostDnsServiceLinux():HostDnsServiceResolvConf(true){} + virtual ~HostDnsServiceLinux(); + virtual HRESULT init(){ return HostDnsServiceResolvConf::init("/etc/resolv.conf");} + + protected: + virtual void monitorThreadShutdown(); + virtual int monitorWorker(); +}; + +# elif defined(RT_OS_FREEBSD) +class HostDnsServiceFreebsd: public HostDnsServiceResolvConf +{ + public: + HostDnsServiceFreebsd(){} + ~HostDnsServiceFreebsd(){} + HRESULT init(){ return HostDnsServiceResolvConf::init("/etc/resolv.conf");} +}; + +# elif defined(RT_OS_OS2) +class HostDnsServiceOs2 : public HostDnsServiceResolvConf +{ + public: + HostDnsServiceOs2(){} + ~HostDnsServiceOs2(){} + /* XXX: \\MPTN\\ETC should be taken from environment variable ETC */ + HRESULT init(){ return init("\\MPTN\\ETC\\RESOLV2");} +}; + +# endif +# endif + +#endif /* !___H_DNSHOSTSERVICE */ diff --git a/src/VBox/Main/src-server/HostDnsServiceResolvConf.cpp b/src/VBox/Main/src-server/HostDnsServiceResolvConf.cpp new file mode 100644 index 00000000..3b2ceeb8 --- /dev/null +++ b/src/VBox/Main/src-server/HostDnsServiceResolvConf.cpp @@ -0,0 +1,91 @@ +/* -*- indent-tabs-mode: nil; -*- */ +#include <VBox/com/string.h> +#include <VBox/com/ptr.h> + + +#ifdef RT_OS_OS2 +# include <sys/socket.h> +typedef int socklen_t; +#endif + +#include <stdio.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> + + +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/file.h> +#include <iprt/critsect.h> + +#include <VBox/log.h> + +#include <string> + +#include "HostDnsService.h" +#include "../../Devices/Network/slirp/resolv_conf_parser.h" + + +struct HostDnsServiceResolvConf::Data +{ + Data(const char *fileName):resolvConfFilename(fileName){}; + + std::string resolvConfFilename; +}; + +const std::string& HostDnsServiceResolvConf::resolvConf() const +{ + return m->resolvConfFilename; +} + + +HostDnsServiceResolvConf::~HostDnsServiceResolvConf() +{ + if (m) + { + delete m; + m = NULL; + } +} + +HRESULT HostDnsServiceResolvConf::init(const char *aResolvConfFileName) +{ + m = new Data(aResolvConfFileName); + + HostDnsMonitor::init(); + + readResolvConf(); + + return S_OK; +} + + +HRESULT HostDnsServiceResolvConf::readResolvConf() +{ + struct rcp_state st; + + st.rcps_flags = RCPSF_NO_STR2IPCONV; + int rc = rcp_parse(&st, m->resolvConfFilename.c_str()); + if (rc == -1) + return S_OK; + + HostDnsInformation info; + for (unsigned i = 0; i != st.rcps_num_nameserver; ++i) + { + AssertBreak(st.rcps_str_nameserver[i]); + info.servers.push_back(st.rcps_str_nameserver[i]); + } + + if (st.rcps_domain) + info.domain = st.rcps_domain; + + for (unsigned i = 0; i != st.rcps_num_searchlist; ++i) + { + AssertBreak(st.rcps_searchlist[i]); + info.searchList.push_back(st.rcps_searchlist[i]); + } + setInfo(info); + + return S_OK; +} diff --git a/src/VBox/Main/src-server/HostImpl.cpp b/src/VBox/Main/src-server/HostImpl.cpp index 03f056c6..bb94c241 100644 --- a/src/VBox/Main/src-server/HostImpl.cpp +++ b/src/VBox/Main/src-server/HostImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2004-2012 Oracle Corporation + * Copyright (C) 2004-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -30,6 +30,7 @@ #endif // VBOX_WITH_USB #include "HostNetworkInterfaceImpl.h" +#include "HostVideoInputDeviceImpl.h" #include "MachineImpl.h" #include "AutoCaller.h" #include "Logging.h" @@ -54,6 +55,11 @@ # include <VBox/VBoxNetCfg-win.h> #endif /* #if defined(RT_OS_WINDOWS) && defined(VBOX_WITH_NETFLT) */ +#if defined(RT_OS_DARWIN) && ARCH_BITS == 32 +# include <sys/types.h> +# include <sys/sysctl.h> +#endif + #ifdef RT_OS_LINUX # include <sys/ioctl.h> # include <errno.h> @@ -133,15 +139,17 @@ typedef SOLARISDVD *PSOLARISDVD; #include <iprt/env.h> #include <iprt/mem.h> #include <iprt/system.h> -#ifdef RT_OS_SOLARIS +#ifndef RT_OS_WINDOWS # include <iprt/path.h> +#endif +#ifdef RT_OS_SOLARIS # include <iprt/ctype.h> #endif #ifdef VBOX_WITH_HOSTNETIF_API # include "netif.h" #endif -/* XXX Solaris: definitions in /usr/include/sys/regset.h clash with hwacc_svm.h */ +/* XXX Solaris: definitions in /usr/include/sys/regset.h clash with hm_svm.h */ #undef DS #undef ES #undef CS @@ -150,17 +158,22 @@ typedef SOLARISDVD *PSOLARISDVD; #undef GS #include <VBox/usb.h> -#include <VBox/vmm/hwacc_svm.h> +#include <VBox/vmm/hm_svm.h> #include <VBox/err.h> #include <VBox/settings.h> #include <VBox/sup.h> #include <iprt/x86.h> #include "VBox/com/MultiResult.h" +#include "VBox/com/array.h" #include <stdio.h> #include <algorithm> +#include <string> +#include <vector> + +#include "HostDnsService.h" //////////////////////////////////////////////////////////////////////////////// // @@ -199,19 +212,24 @@ struct Host::Data /** Object with information about host drives */ VBoxMainDriveInfo hostDrives; #endif - /* Features that can be queried with GetProcessorFeature */ - BOOL fVTSupported, + /** @name Features that can be queried with GetProcessorFeature. + * @{ */ + bool fVTSupported, fLongModeSupported, fPAESupported, - fNestedPagingSupported; + fNestedPagingSupported, + fRecheckVTSupported; - /* 3D hardware acceleration supported? */ - BOOL f3DAccelerationSupported; + /** @} */ + + /** 3D hardware acceleration supported? Tristate, -1 meaning not probed. */ + int f3DAccelerationSupported; HostPowerService *pHostPowerService; + /** Host's DNS informaton fetching */ + HostDnsMonitorProxy hostDnsMonitorProxy; }; - //////////////////////////////////////////////////////////////////////////////// // // Constructor / destructor @@ -276,6 +294,8 @@ HRESULT Host::init(VirtualBox *aParent) /* Create the list of network interfaces so their metrics get registered. */ updateNetIfList(); + m->hostDnsMonitorProxy.init(HostDnsMonitor::getHostDnsMonitor(), m->pParent); + #if defined (RT_OS_WINDOWS) m->pHostPowerService = new HostPowerServiceWin(m->pParent); #elif defined (RT_OS_DARWIN) @@ -289,100 +309,104 @@ HRESULT Host::init(VirtualBox *aParent) m->fLongModeSupported = false; m->fPAESupported = false; m->fNestedPagingSupported = false; + m->fRecheckVTSupported = false; if (ASMHasCpuId()) { - uint32_t u32FeaturesECX; - uint32_t u32Dummy; - uint32_t u32FeaturesEDX; - uint32_t u32VendorEBX, u32VendorECX, u32VendorEDX, u32ExtFeatureEDX, u32ExtFeatureECX; - - ASMCpuId(0, &u32Dummy, &u32VendorEBX, &u32VendorECX, &u32VendorEDX); - ASMCpuId(1, &u32Dummy, &u32Dummy, &u32FeaturesECX, &u32FeaturesEDX); - /* Query Extended features. */ - ASMCpuId(0x80000001, &u32Dummy, &u32Dummy, &u32ExtFeatureECX, &u32ExtFeatureEDX); - - m->fLongModeSupported = !!(u32ExtFeatureEDX & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE); - m->fPAESupported = !!(u32FeaturesEDX & X86_CPUID_FEATURE_EDX_PAE); - - if ( u32VendorEBX == X86_CPUID_VENDOR_INTEL_EBX - && u32VendorECX == X86_CPUID_VENDOR_INTEL_ECX - && u32VendorEDX == X86_CPUID_VENDOR_INTEL_EDX - ) + /* Note! This code is duplicated in SUPDrv.c and other places! */ + uint32_t uMaxId, uVendorEBX, uVendorECX, uVendorEDX; + ASMCpuId(0, &uMaxId, &uVendorEBX, &uVendorECX, &uVendorEDX); + if (ASMIsValidStdRange(uMaxId)) { - /* Intel. */ - if ( (u32FeaturesECX & X86_CPUID_FEATURE_ECX_VMX) - && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_MSR) - && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_FXSR) - ) - { - int rc = SUPR3QueryVTxSupported(); - if (RT_SUCCESS(rc)) - m->fVTSupported = true; - } - } - else - if ( u32VendorEBX == X86_CPUID_VENDOR_AMD_EBX - && u32VendorECX == X86_CPUID_VENDOR_AMD_ECX - && u32VendorEDX == X86_CPUID_VENDOR_AMD_EDX - ) - { - /* AMD. */ - if ( (u32ExtFeatureECX & X86_CPUID_AMD_FEATURE_ECX_SVM) - && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_MSR) - && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_FXSR) - ) - { - uint32_t u32SVMFeatureEDX; - - m->fVTSupported = true; + /* PAE? */ + uint32_t uDummy, fFeaturesEcx, fFeaturesEdx; + ASMCpuId(1, &uDummy, &uDummy, &fFeaturesEcx, &fFeaturesEdx); + m->fPAESupported = RT_BOOL(fFeaturesEdx & X86_CPUID_FEATURE_EDX_PAE); + + /* Long Mode? */ + uint32_t uExtMaxId, fExtFeaturesEcx, fExtFeaturesEdx; + ASMCpuId(0x80000000, &uExtMaxId, &uDummy, &uDummy, &uDummy); + ASMCpuId(0x80000001, &uDummy, &uDummy, &fExtFeaturesEcx, &fExtFeaturesEdx); + m->fLongModeSupported = ASMIsValidExtRange(uExtMaxId) + && (fExtFeaturesEdx & X86_CPUID_EXT_FEATURE_EDX_LONG_MODE); + +#if defined(RT_OS_DARWIN) && ARCH_BITS == 32 /* darwin.x86 has some optimizations of 64-bit on 32-bit. */ + int f64bitCapable = 0; + size_t cbParameter = sizeof(f64bitCapable); + if (sysctlbyname("hw.cpu64bit_capable", &f64bitCapable, &cbParameter, NULL, NULL) != -1) + m->fLongModeSupported = f64bitCapable != 0; +#endif - /* Query AMD features. */ - ASMCpuId(0x8000000A, &u32Dummy, &u32Dummy, &u32Dummy, &u32SVMFeatureEDX); - if (u32SVMFeatureEDX & AMD_CPUID_SVM_FEATURE_EDX_NESTED_PAGING) - m->fNestedPagingSupported = true; + /* VT-x? */ + if ( ASMIsIntelCpuEx(uVendorEBX, uVendorECX, uVendorEDX) + || ASMIsViaCentaurCpuEx(uVendorEBX, uVendorECX, uVendorEDX)) + { + if ( (fFeaturesEcx & X86_CPUID_FEATURE_ECX_VMX) + && (fFeaturesEdx & X86_CPUID_FEATURE_EDX_MSR) + && (fFeaturesEdx & X86_CPUID_FEATURE_EDX_FXSR) + ) + { + int rc = SUPR3QueryVTxSupported(); + if (RT_SUCCESS(rc)) + m->fVTSupported = true; + } } - } - else - if ( u32VendorEBX == X86_CPUID_VENDOR_VIA_EBX - && u32VendorECX == X86_CPUID_VENDOR_VIA_ECX - && u32VendorEDX == X86_CPUID_VENDOR_VIA_EDX - ) - { - /* VIA. */ - if ( (u32FeaturesECX & X86_CPUID_FEATURE_ECX_VMX) - && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_MSR) - && (u32FeaturesEDX & X86_CPUID_FEATURE_EDX_FXSR) - ) + /* AMD-V */ + else if (ASMIsAmdCpuEx(uVendorEBX, uVendorECX, uVendorEDX)) { - int rc = SUPR3QueryVTxSupported(); - if (RT_SUCCESS(rc)) + if ( (fExtFeaturesEcx & X86_CPUID_AMD_FEATURE_ECX_SVM) + && (fFeaturesEdx & X86_CPUID_FEATURE_EDX_MSR) + && (fFeaturesEdx & X86_CPUID_FEATURE_EDX_FXSR) + && ASMIsValidExtRange(uExtMaxId) + ) + { m->fVTSupported = true; + + /* Query AMD features. */ + if (uExtMaxId >= 0x8000000a) + { + uint32_t fSVMFeaturesEdx; + ASMCpuId(0x8000000a, &uDummy, &uDummy, &uDummy, &fSVMFeaturesEdx); + if (fSVMFeaturesEdx & AMD_CPUID_SVM_FEATURE_EDX_NESTED_PAGING) + m->fNestedPagingSupported = true; + } + } } } } -#if 0 /* needs testing */ + /* Check with SUPDrv if VT-x and AMD-V are really supported (may fail). */ if (m->fVTSupported) { - uint32_t u32Caps = 0; - - int rc = SUPR3QueryVTCaps(&u32Caps); + int rc = SUPR3InitEx(false /*fUnrestricted*/, NULL); if (RT_SUCCESS(rc)) { - if (u32Caps & SUPVTCAPS_NESTED_PAGING) - m->fNestedPagingSupported = true; + uint32_t fVTCaps; + rc = SUPR3QueryVTCaps(&fVTCaps); + if (RT_SUCCESS(rc)) + { + Assert(fVTCaps & (SUPVTCAPS_AMD_V | SUPVTCAPS_VT_X)); + if (fVTCaps & SUPVTCAPS_NESTED_PAGING) + m->fNestedPagingSupported = true; + else + Assert(m->fNestedPagingSupported == false); + } + else + { + LogRel(("SUPR0QueryVTCaps -> %Rrc\n", rc)); + m->fVTSupported = m->fNestedPagingSupported = false; + } } - /* else @todo; report BIOS trouble in some way. */ + else + m->fRecheckVTSupported = true; /* Try again later when the driver is loaded. */ } -#endif - - /* Test for 3D hardware acceleration support */ - m->f3DAccelerationSupported = false; #ifdef VBOX_WITH_CROGL - m->f3DAccelerationSupported = VBoxOglIs3DAccelerationSupported(); -#endif /* VBOX_WITH_CROGL */ + /* Test for 3D hardware acceleration support later when (if ever) need. */ + m->f3DAccelerationSupported = -1; +#else + m->f3DAccelerationSupported = false; +#endif #if defined (RT_OS_LINUX) || defined(RT_OS_DARWIN) || defined(RT_OS_FREEBSD) /* Extract the list of configured host-only interfaces */ @@ -547,7 +571,7 @@ static int vboxNetWinAddComponent(std::list< ComObjPtr<HostNetworkInterface> > * HRESULT hr; int rc = VERR_GENERAL_FAILURE; - hr = pncc->GetDisplayName( &lpszName ); + hr = pncc->GetDisplayName(&lpszName); Assert(hr == S_OK); if (hr == S_OK) { @@ -640,10 +664,10 @@ STDMETHODIMP Host::COMGETTER(NetworkInterfaces)(ComSafeArrayOut(IHostNetworkInte INetCfgBindingInterface *pBi; /* we are using the INetCfg API for getting the list of miniports */ - hr = VBoxNetCfgWinQueryINetCfg( FALSE, - VBOX_APP_NAME, - &pNc, - &lpszApp ); + hr = VBoxNetCfgWinQueryINetCfg(FALSE, + VBOX_APP_NAME, + &pNc, + &lpszApp); Assert(hr == S_OK); if (hr == S_OK) { @@ -666,24 +690,24 @@ STDMETHODIMP Host::COMGETTER(NetworkInterfaces)(ComSafeArrayOut(IHostNetworkInte { hr = VBoxNetCfgWinGetBindingPathEnum(pTcpIpNcc, EBP_BELOW, &pEnumBp); Assert(hr == S_OK); - if ( hr == S_OK ) + if (hr == S_OK) { hr = VBoxNetCfgWinGetFirstBindingPath(pEnumBp, &pBp); Assert(hr == S_OK || hr == S_FALSE); - while( hr == S_OK ) + while (hr == S_OK) { /* S_OK == enabled, S_FALSE == disabled */ if (pBp->IsEnabled() == S_OK) { hr = VBoxNetCfgWinGetBindingInterfaceEnum(pBp, &pEnumBi); Assert(hr == S_OK); - if ( hr == S_OK ) + if (hr == S_OK) { hr = VBoxNetCfgWinGetFirstBindingInterface(pEnumBi, &pBi); Assert(hr == S_OK); - while(hr == S_OK) + while (hr == S_OK) { - hr = pBi->GetLowerComponent( &pMpNcc ); + hr = pBi->GetLowerComponent(&pMpNcc); Assert(hr == S_OK); if (hr == S_OK) { @@ -697,7 +721,7 @@ STDMETHODIMP Host::COMGETTER(NetworkInterfaces)(ComSafeArrayOut(IHostNetworkInte vboxNetWinAddComponent(&list, pMpNcc); } } - VBoxNetCfgWinReleaseRef( pMpNcc ); + VBoxNetCfgWinReleaseRef(pMpNcc); } VBoxNetCfgWinReleaseRef(pBi); @@ -795,6 +819,54 @@ STDMETHODIMP Host::COMGETTER(USBDevices)(ComSafeArrayOut(IHostUSBDevice*, aUSBDe #endif } + +/** + * This method return the list of registered name servers + */ +STDMETHODIMP Host::COMGETTER(NameServers)(ComSafeArrayOut(BSTR, aNameServers)) +{ + CheckComArgOutSafeArrayPointerValid(aNameServers); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + return m->hostDnsMonitorProxy.GetNameServers(ComSafeArrayOutArg(aNameServers)); +} + + +/** + * This method returns the domain name of the host + */ +STDMETHODIMP Host::COMGETTER(DomainName)(BSTR *aDomainName) +{ + /* XXX: note here should be synchronization with thread polling state + * changes in name resoving system on host */ + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + return m->hostDnsMonitorProxy.GetDomainName(aDomainName); +} + + +/** + * This method returns the search string. + */ +STDMETHODIMP Host::COMGETTER(SearchStrings)(ComSafeArrayOut(BSTR, aSearchStrings)) +{ + CheckComArgOutSafeArrayPointerValid(aSearchStrings); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + return m->hostDnsMonitorProxy.GetSearchStrings(ComSafeArrayOutArg(aSearchStrings)); +} + + STDMETHODIMP Host::COMGETTER(USBDeviceFilters)(ComSafeArrayOut(IHostUSBDeviceFilter*, aUSBDeviceFilters)) { #ifdef VBOX_WITH_USB @@ -865,7 +937,23 @@ STDMETHODIMP Host::COMGETTER(ProcessorCoreCount)(ULONG *aCount) CheckComArgOutPointerValid(aCount); // no locking required - return E_NOTIMPL; + *aCount = RTMpGetPresentCoreCount(); + return S_OK; +} + +/** + * Returns the number of installed physical processor cores. + * + * @returns COM status code + * @param count address of result variable + */ +STDMETHODIMP Host::COMGETTER(ProcessorOnlineCoreCount)(ULONG *aCount) +{ + CheckComArgOutPointerValid(aCount); + // no locking required + + *aCount = RTMpGetOnlineCoreCount(); + return S_OK; } /** @@ -913,34 +1001,82 @@ STDMETHODIMP Host::GetProcessorDescription(ULONG aCpuId, BSTR *aDescription) */ STDMETHODIMP Host::GetProcessorFeature(ProcessorFeature_T aFeature, BOOL *aSupported) { + /* Validate input. */ CheckComArgOutPointerValid(aSupported); - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - switch (aFeature) { case ProcessorFeature_HWVirtEx: - *aSupported = m->fVTSupported; - break; - case ProcessorFeature_PAE: - *aSupported = m->fPAESupported; - break; - case ProcessorFeature_LongMode: - *aSupported = m->fLongModeSupported; - break; - case ProcessorFeature_NestedPaging: - *aSupported = m->fNestedPagingSupported; break; - default: - ReturnComNotImplemented(); + return setError(E_INVALIDARG, tr("The aFeature value %d (%#x) is out of range."), (int)aFeature, (int)aFeature); } - return S_OK; + + /* Do the job. */ + AutoCaller autoCaller(this); + HRESULT hrc = autoCaller.rc(); + if (SUCCEEDED(hrc)) + { + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + if ( m->fRecheckVTSupported + && ( aFeature == ProcessorFeature_HWVirtEx + || aFeature == ProcessorFeature_NestedPaging) + ) + { + alock.release(); + + /* Perhaps the driver is available now... */ + int rc = SUPR3InitEx(false /*fUnrestricted*/, NULL); + if (RT_SUCCESS(rc)) + { + uint32_t fVTCaps; + rc = SUPR3QueryVTCaps(&fVTCaps); + + AutoWriteLock wlock(this COMMA_LOCKVAL_SRC_POS); + if (RT_SUCCESS(rc)) + { + Assert(fVTCaps & (SUPVTCAPS_AMD_V | SUPVTCAPS_VT_X)); + if (fVTCaps & SUPVTCAPS_NESTED_PAGING) + m->fNestedPagingSupported = true; + else + Assert(m->fNestedPagingSupported == false); + } + else + { + LogRel(("SUPR0QueryVTCaps -> %Rrc\n", rc)); + m->fVTSupported = m->fNestedPagingSupported = true; + } + } + + alock.acquire(); + } + + switch (aFeature) + { + case ProcessorFeature_HWVirtEx: + *aSupported = m->fVTSupported; + break; + + case ProcessorFeature_PAE: + *aSupported = m->fPAESupported; + break; + + case ProcessorFeature_LongMode: + *aSupported = m->fLongModeSupported; + break; + + case ProcessorFeature_NestedPaging: + *aSupported = m->fNestedPagingSupported; + break; + + default: + AssertFailed(); + } + } + return hrc; } /** @@ -992,15 +1128,12 @@ STDMETHODIMP Host::COMGETTER(MemorySize)(ULONG *aSize) CheckComArgOutPointerValid(aSize); // no locking required - /* @todo This is an ugly hack. There must be a function in IPRT for that. */ - pm::CollectorHAL *hal = pm::createHAL(); - if (!hal) + uint64_t cb; + int rc = RTSystemQueryTotalRam(&cb); + if (RT_FAILURE(rc)) return E_FAIL; - ULONG tmp; - int rc = hal->getHostMemoryUsage(aSize, &tmp, &tmp); - *aSize /= 1024; - delete hal; - return rc; + *aSize = (ULONG)(cb / _1M); + return S_OK; } /** @@ -1014,15 +1147,12 @@ STDMETHODIMP Host::COMGETTER(MemoryAvailable)(ULONG *aAvailable) CheckComArgOutPointerValid(aAvailable); // no locking required - /* @todo This is an ugly hack. There must be a function in IPRT for that. */ - pm::CollectorHAL *hal = pm::createHAL(); - if (!hal) + uint64_t cb; + int rc = RTSystemQueryAvailableRam(&cb); + if (RT_FAILURE(rc)) return E_FAIL; - ULONG tmp; - int rc = hal->getHostMemoryUsage(&tmp, &tmp, aAvailable); - *aAvailable /= 1024; - delete hal; - return rc; + *aAvailable = (ULONG)(cb / _1M); + return S_OK; } /** @@ -1101,17 +1231,33 @@ STDMETHODIMP Host::COMGETTER(Acceleration3DAvailable)(BOOL *aSupported) { CheckComArgOutPointerValid(aSupported); AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + HRESULT hrc = autoCaller.rc(); + if (SUCCEEDED(hrc)) + { + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + if (m->f3DAccelerationSupported != -1) + *aSupported = m->f3DAccelerationSupported; + else + { + alock.release(); +#ifdef VBOX_WITH_CROGL + bool fSupported = VBoxOglIs3DAccelerationSupported(); +#else + bool fSupported = false; /* shoudn't get here, but just in case. */ +#endif + AutoWriteLock alock2(this COMMA_LOCKVAL_SRC_POS); + m->f3DAccelerationSupported = fSupported; + alock2.release(); - *aSupported = m->f3DAccelerationSupported; + *aSupported = fSupported; + } + } #ifdef DEBUG_misha AssertMsgFailed(("should not be here any more!\n")); #endif - return S_OK; + return hrc; } STDMETHODIMP Host::CreateHostOnlyNetworkInterface(IHostNetworkInterface **aHostNetworkInterface, @@ -1433,7 +1579,7 @@ STDMETHODIMP Host::FindHostNetworkInterfaceById(IN_BSTR id, IHostNetworkInterfac #ifndef VBOX_WITH_HOSTNETIF_API return E_NOTIMPL; #else - if (Guid(id).isEmpty()) + if (!Guid(id).isValid()) return E_INVALIDARG; if (!networkInterface) return E_POINTER; @@ -1537,7 +1683,7 @@ STDMETHODIMP Host::FindUSBDeviceById(IN_BSTR aId, IHostUSBDevice **aDevice) { #ifdef VBOX_WITH_USB - CheckComArgExpr(aId, Guid (aId).isEmpty() == false); + CheckComArgExpr(aId, Guid (aId).isValid()); CheckComArgOutPointerValid(aDevice); *aDevice = NULL; @@ -1578,6 +1724,34 @@ STDMETHODIMP Host::GenerateMACAddress(BSTR *aAddress) return S_OK; } +/** + * Returns a list of host video capture devices (webcams, etc). + * + * @returns COM status code + * @param aVideoInputDevices Array of interface pointers to be filled. + */ +STDMETHODIMP Host::COMGETTER(VideoInputDevices)(ComSafeArrayOut(IHostVideoInputDevice*, aVideoInputDevices)) +{ + if (ComSafeArrayOutIsNull(aVideoInputDevices)) + return E_POINTER; + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + HostVideoInputDeviceList list; + + HRESULT hr = HostVideoInputDevice::queryHostDevices(m->pParent, &list); + + if (SUCCEEDED(hr)) + { + SafeIfaceArray<IHostVideoInputDevice> devices(list); + devices.detachTo(ComSafeArrayOutArg(aVideoInputDevices)); + } + + return hr; +} + // public methods only for internal purposes //////////////////////////////////////////////////////////////////////////////// @@ -1868,7 +2042,7 @@ HRESULT Host::findHostDriveByNameOrId(DeviceType_T mediumType, AutoWriteLock wlock(m->pParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); Guid uuid(strNameOrId); - if (!uuid.isEmpty()) + if (uuid.isValid() && !uuid.isZero()) return findHostDriveById(mediumType, uuid, true /* fRefresh */, pMedium); // string is not a syntactically valid UUID: try a name then @@ -2228,7 +2402,7 @@ static int solarisWalkDeviceNodeForDVD(di_node_t Node, void *pvArg) { char *pszSlice = solarisGetSliceFromPath(pszDevLinkPath); if ( pszSlice && !strcmp(pszSlice, "s2") - && !strncmp(pszDevLinkPath, "/dev/rdsk", sizeof("/dev/rdsk") - 1)) /* We want only raw disks */ + && !strncmp(pszDevLinkPath, RT_STR_TUPLE("/dev/rdsk"))) /* We want only raw disks */ { /* * We've got a fully qualified DVD drive. Add it to the list. @@ -2680,9 +2854,9 @@ void Host::parseMountTable(char *mountTable, std::list< ComObjPtr<Medium> > &lis { // skip devices we are not interested in if ((*mountName && mountName[0] == '/') && // skip 'fake' devices (like -hosts, proc, fd, swap) - (*mountFSType && (strncmp(mountFSType, "devfs", 5) != 0 && // skip devfs (i.e. /devices) - strncmp(mountFSType, "dev", 3) != 0 && // skip dev (i.e. /dev) - strncmp(mountFSType, "lofs", 4) != 0))) // skip loop-back file-system (lofs) + (*mountFSType && (strncmp(mountFSType, RT_STR_TUPLE("devfs")) != 0 && // skip devfs (i.e. /devices) + strncmp(mountFSType, RT_STR_TUPLE("dev")) != 0 && // skip dev (i.e. /dev) + strncmp(mountFSType, RT_STR_TUPLE("lofs")) != 0))) // skip loop-back file-system (lofs) { char *rawDevName = getfullrawname((char *)mountName); if (validateDevice(rawDevName, true)) @@ -2834,7 +3008,9 @@ HRESULT Host::updateNetIfList() /* Make a copy as the original may be partially destroyed later. */ listCopy = list; HostNetworkInterfaceList::iterator itOld, itNew; +# ifdef VBOX_WITH_RESOURCE_USAGE_API PerformanceCollector *aCollector = m->pParent->performanceCollector(); +# endif for (itOld = m->llNetIfs.begin(); itOld != m->llNetIfs.end(); ++itOld) { bool fGone = true; @@ -2852,7 +3028,11 @@ HRESULT Host::updateNetIfList() } } if (fGone) + { +# ifdef VBOX_WITH_RESOURCE_USAGE_API (*itOld)->unregisterMetrics(aCollector, this); +# endif + } } /* * Need to set the references to VirtualBox object in all interface objects @@ -2872,7 +3052,11 @@ HRESULT Host::updateNetIfList() LogRel(("Host::updateNetIfList: failed to get interface type for %ls\n", n.raw())); } else if (t == HostNetworkInterfaceType_Bridged) + { +# ifdef VBOX_WITH_RESOURCE_USAGE_API (*itNew)->registerMetrics(aCollector, this); +# endif + } } m->llNetIfs = list; return S_OK; @@ -2928,17 +3112,17 @@ void Host::registerDiskMetrics(PerformanceCollector *aCollector) new pm::AggregateMax())); /* For now we are concerned with the root file system only. */ - pm::DiskList disks; - int rc = pm::getDiskListByFs("/", disks); - if (RT_FAILURE(rc) || disks.empty()) + pm::DiskList disksUsage, disksLoad; + int rc = hal->getDiskListByFs("/", disksUsage, disksLoad); + if (RT_FAILURE(rc)) return; pm::DiskList::iterator it; - for (it = disks.begin(); it != disks.end(); ++it) + for (it = disksLoad.begin(); it != disksLoad.end(); ++it) { - Utf8StrFmt strName("Disk/%s/Load", it->c_str()); - pm::SubMetric *fsLoadUtil = new pm::SubMetric(strName + "/Util", + Utf8StrFmt strName("Disk/%s", it->c_str()); + pm::SubMetric *fsLoadUtil = new pm::SubMetric(strName + "/Load/Util", "Percentage of time disk was busy serving I/O requests."); - pm::BaseMetric *fsLoad = new pm::HostDiskLoadRaw(hal, this, strName, + pm::BaseMetric *fsLoad = new pm::HostDiskLoadRaw(hal, this, strName + "/Load", *it, fsLoadUtil); aCollector->registerBaseMetric (fsLoad); @@ -2950,6 +3134,23 @@ void Host::registerDiskMetrics(PerformanceCollector *aCollector) aCollector->registerMetric(new pm::Metric(fsLoad, fsLoadUtil, new pm::AggregateMax())); } + for (it = disksUsage.begin(); it != disksUsage.end(); ++it) + { + Utf8StrFmt strName("Disk/%s", it->c_str()); + pm::SubMetric *fsUsageTotal = new pm::SubMetric(strName + "/Usage/Total", + "Disk size."); + pm::BaseMetric *fsUsage = new pm::HostDiskUsage(hal, this, strName + "/Usage", + *it, fsUsageTotal); + aCollector->registerBaseMetric (fsUsage); + + aCollector->registerMetric(new pm::Metric(fsUsage, fsUsageTotal, 0)); + aCollector->registerMetric(new pm::Metric(fsUsage, fsUsageTotal, + new pm::AggregateAvg())); + aCollector->registerMetric(new pm::Metric(fsUsage, fsUsageTotal, + new pm::AggregateMin())); + aCollector->registerMetric(new pm::Metric(fsUsage, fsUsageTotal, + new pm::AggregateMax())); + } } void Host::registerMetrics(PerformanceCollector *aCollector) @@ -3094,6 +3295,8 @@ void Host::unregisterMetrics (PerformanceCollector *aCollector) aCollector->unregisterBaseMetricsFor(this); } +#endif /* VBOX_WITH_RESOURCE_USAGE_API */ + /* static */ void Host::generateMACAddress(Utf8Str &mac) @@ -3109,6 +3312,4 @@ void Host::generateMACAddress(Utf8Str &mac) guid.raw()->au8[0], guid.raw()->au8[1], guid.raw()->au8[2]); } -#endif /* VBOX_WITH_RESOURCE_USAGE_API */ - /* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/HostNetworkInterfaceImpl.cpp b/src/VBox/Main/src-server/HostNetworkInterfaceImpl.cpp index 1eeef5b5..58c5fd3b 100644 --- a/src/VBox/Main/src-server/HostNetworkInterfaceImpl.cpp +++ b/src/VBox/Main/src-server/HostNetworkInterfaceImpl.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,8 +21,10 @@ #include "AutoCaller.h" #include "Logging.h" #include "netif.h" -#include "Performance.h" -#include "PerformanceImpl.h" +#ifdef VBOX_WITH_RESOURCE_USAGE_API +# include "Performance.h" +# include "PerformanceImpl.h" +#endif #include <iprt/cpp/utils.h> @@ -69,7 +71,7 @@ HRESULT HostNetworkInterface::init(Bstr aInterfaceName, Bstr aShortName, Guid aG aInterfaceName.raw(), aGuid.toString().c_str())); ComAssertRet(!aInterfaceName.isEmpty(), E_INVALIDARG); - ComAssertRet(!aGuid.isEmpty(), E_INVALIDARG); + ComAssertRet(aGuid.isValid(), E_INVALIDARG); /* Enclose the state transition NotReady->InInit->Ready */ AutoInitSpan autoInitSpan(this); @@ -77,6 +79,7 @@ HRESULT HostNetworkInterface::init(Bstr aInterfaceName, Bstr aShortName, Guid aG unconst(mInterfaceName) = aInterfaceName; unconst(mNetworkName) = composeNetworkName(aShortName); + unconst(mShortName) = aShortName; unconst(mGuid) = aGuid; mIfType = ifType; @@ -86,22 +89,36 @@ HRESULT HostNetworkInterface::init(Bstr aInterfaceName, Bstr aShortName, Guid aG return S_OK; } +#ifdef VBOX_WITH_RESOURCE_USAGE_API + void HostNetworkInterface::registerMetrics(PerformanceCollector *aCollector, ComPtr<IUnknown> objptr) { LogFlowThisFunc(("mShortName={%ls}, mInterfaceName={%ls}, mGuid={%s}, mSpeedMbits=%u\n", mShortName.raw(), mInterfaceName.raw(), mGuid.toString().c_str(), m.speedMbits)); pm::CollectorHAL *hal = aCollector->getHAL(); /* Create sub metrics */ - Utf8StrFmt strName("Net/%ls/Load", mShortName.raw()); - pm::SubMetric *networkLoadRx = new pm::SubMetric(strName + "/Rx", + Utf8StrFmt strName("Net/%ls", mShortName.raw()); + pm::SubMetric *networkLoadRx = new pm::SubMetric(strName + "/Load/Rx", "Percentage of network interface receive bandwidth used."); - pm::SubMetric *networkLoadTx = new pm::SubMetric(strName + "/Tx", + pm::SubMetric *networkLoadTx = new pm::SubMetric(strName + "/Load/Tx", "Percentage of network interface transmit bandwidth used."); + pm::SubMetric *networkLinkSpeed = new pm::SubMetric(strName + "/LinkSpeed", + "Physical link speed."); /* Create and register base metrics */ - pm::BaseMetric *networkLoad = new pm::HostNetworkLoadRaw(hal, objptr, strName, Utf8Str(mShortName), Utf8Str(mInterfaceName), m.speedMbits, networkLoadRx, networkLoadTx); + pm::BaseMetric *networkSpeed = new pm::HostNetworkSpeed(hal, objptr, strName + "/LinkSpeed", Utf8Str(mShortName), Utf8Str(mInterfaceName), m.speedMbits, networkLinkSpeed); + aCollector->registerBaseMetric(networkSpeed); + pm::BaseMetric *networkLoad = new pm::HostNetworkLoadRaw(hal, objptr, strName + "/Load", Utf8Str(mShortName), Utf8Str(mInterfaceName), m.speedMbits, networkLoadRx, networkLoadTx); aCollector->registerBaseMetric(networkLoad); + aCollector->registerMetric(new pm::Metric(networkSpeed, networkLinkSpeed, 0)); + aCollector->registerMetric(new pm::Metric(networkSpeed, networkLinkSpeed, + new pm::AggregateAvg())); + aCollector->registerMetric(new pm::Metric(networkSpeed, networkLinkSpeed, + new pm::AggregateMin())); + aCollector->registerMetric(new pm::Metric(networkSpeed, networkLinkSpeed, + new pm::AggregateMax())); + aCollector->registerMetric(new pm::Metric(networkLoad, networkLoadRx, 0)); aCollector->registerMetric(new pm::Metric(networkLoad, networkLoadRx, new pm::AggregateAvg())); @@ -123,11 +140,13 @@ void HostNetworkInterface::unregisterMetrics(PerformanceCollector *aCollector, C { LogFlowThisFunc(("mShortName={%ls}, mInterfaceName={%ls}, mGuid={%s}\n", mShortName.raw(), mInterfaceName.raw(), mGuid.toString().c_str())); - Utf8StrFmt name("Net/%ls/Load", mShortName.raw()); + Utf8StrFmt name("Net/%ls", mShortName.raw()); aCollector->unregisterMetricsFor(objptr, name + "/*"); aCollector->unregisterBaseMetricsFor(objptr, name); } +#endif /* VBOX_WITH_RESOURCE_USAGE_API */ + #ifdef VBOX_WITH_HOSTNETIF_API HRESULT HostNetworkInterface::updateConfig() @@ -172,7 +191,7 @@ HRESULT HostNetworkInterface::init(Bstr aInterfaceName, HostNetworkInterfaceType // aInterfaceName.raw(), aGuid.toString().raw())); // ComAssertRet(aInterfaceName, E_INVALIDARG); -// ComAssertRet(!aGuid.isEmpty(), E_INVALIDARG); +// ComAssertRet(aGuid.isValid(), E_INVALIDARG); ComAssertRet(pIf, E_INVALIDARG); /* Enclose the state transition NotReady->InInit->Ready */ @@ -237,6 +256,24 @@ STDMETHODIMP HostNetworkInterface::COMGETTER(Name)(BSTR *aInterfaceName) } /** + * Returns the short name of the host network interface. + * + * @returns COM status code + * @param aShortName address of result pointer + */ +STDMETHODIMP HostNetworkInterface::COMGETTER(ShortName)(BSTR *aShortName) +{ + CheckComArgOutPointerValid(aShortName); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + mShortName.cloneTo(aShortName); + + return S_OK; +} + +/** * Returns the GUID of the host network interface. * * @returns COM status code diff --git a/src/VBox/Main/src-server/HostPower.cpp b/src/VBox/Main/src-server/HostPower.cpp index 830729a0..3146a7ae 100644 --- a/src/VBox/Main/src-server/HostPower.cpp +++ b/src/VBox/Main/src-server/HostPower.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -26,10 +26,11 @@ #include <VBox/com/ptr.h> #include "VirtualBoxImpl.h" +#include "MachineImpl.h" #include <iprt/mem.h> -HostPowerService::HostPowerService (VirtualBox *aVirtualBox) +HostPowerService::HostPowerService(VirtualBox *aVirtualBox) { Assert(aVirtualBox != NULL); mVirtualBox = aVirtualBox; @@ -39,18 +40,18 @@ HostPowerService::~HostPowerService() { } -void HostPowerService::notify(HostPowerEvent aEvent) +void HostPowerService::notify(Reason_T aReason) { SessionMachinesList machines; VirtualBox::InternalControlList controls; HRESULT rc = S_OK; - switch (aEvent) + switch (aReason) { - case HostPowerEvent_Suspend: + case Reason_HostSuspend: { - LogFunc (("SUSPEND\n")); + LogFunc(("HOST SUSPEND\n")); #ifdef VBOX_WITH_RESOURCE_USAGE_API /* Suspend performance sampling to avoid unnecessary callbacks due to jumps in time. */ @@ -68,49 +69,41 @@ void HostPowerService::notify(HostPowerEvent aEvent) { ComPtr<IInternalSessionControl> pControl = *it; - /* get the remote console */ - ComPtr<IConsole> console; - rc = pControl->GetRemoteConsole(console.asOutParam()); - /* the VM could have been powered down and closed or whatever */ - if (FAILED(rc)) - continue; - - /* note that Pause() will simply return a failure if the VM is - * in an inappropriate state */ - rc = console->Pause(); + /* PauseWithReason() will simply return a failure if + * the VM is in an inappropriate state */ + rc = pControl->PauseWithReason(Reason_HostSuspend); if (FAILED(rc)) continue; /* save the control to un-pause the VM later */ - mConsoles.push_back(console); + mSessionControls.push_back(pControl); } - LogFunc (("Suspended %d VMs\n", mConsoles.size())); - + LogRel(("Host suspending: Paused %d VMs\n", mSessionControls.size())); break; } - case HostPowerEvent_Resume: + case Reason_HostResume: { - LogFunc (("RESUME\n")); + LogFunc(("HOST RESUME\n")); size_t resumed = 0; /* go through VMs we paused on Suspend */ - for (size_t i = 0; i < mConsoles.size(); ++i) + for (size_t i = 0; i < mSessionControls.size(); ++i) { /* note that Resume() will simply return a failure if the VM is * in an inappropriate state (it will also fail if the VM has * been somehow closed by this time already so that the * console reference we have is dead) */ - rc = mConsoles[i]->Resume(); + rc = mSessionControls[i]->ResumeWithReason(Reason_HostResume); if (FAILED(rc)) continue; - ++ resumed; + ++resumed; } - LogFunc (("Resumed %d VMs\n", resumed)); + LogRel(("Host resumed: Resumed %d VMs\n", resumed)); #ifdef VBOX_WITH_RESOURCE_USAGE_API /* Resume the performance sampling. */ @@ -120,60 +113,87 @@ void HostPowerService::notify(HostPowerEvent aEvent) perfcollector->resumeSampling(); #endif - mConsoles.clear(); - + mSessionControls.clear(); break; } - case HostPowerEvent_BatteryLow: + case Reason_HostBatteryLow: { - LogFunc (("BATTERY LOW\n")); + LogFunc(("BATTERY LOW\n")); - mVirtualBox->getOpenedMachines(machines, &controls); + Bstr value; + rc = mVirtualBox->GetExtraData(Bstr("VBoxInternal2/SavestateOnBatteryLow").raw(), + value.asOutParam()); + int fGlobal = 0; + if (SUCCEEDED(rc) && !value.isEmpty()) + { + if (value != "0") + fGlobal = 1; + else if (value == "0") + fGlobal = -1; + } + mVirtualBox->getOpenedMachines(machines, &controls); size_t saved = 0; /* save running VMs */ + SessionMachinesList::const_iterator it2 = machines.begin(); for (VirtualBox::InternalControlList::const_iterator it = controls.begin(); - it != controls.end(); - ++it) + it != controls.end() && it2 != machines.end(); + ++it, ++it2) { - ComPtr<IInternalSessionControl> pControl = *it; - /* get the remote console */ - ComPtr<IConsole> console; - rc = pControl->GetRemoteConsole (console.asOutParam()); - /* the VM could have been powered down and closed or whatever */ - if (FAILED(rc)) - continue; - - ComPtr<IProgress> progress; - - /* note that SaveState() will simply return a failure if the VM - * is in an inappropriate state */ - rc = console->SaveState (progress.asOutParam()); - if (FAILED(rc)) - continue; - - /* Wait until the operation has been completed. */ - rc = progress->WaitForCompletion(-1); - if (SUCCEEDED(rc)) + ComPtr<SessionMachine> pMachine = *it2; + rc = pMachine->GetExtraData(Bstr("VBoxInternal2/SavestateOnBatteryLow").raw(), + value.asOutParam()); + int fPerVM = 0; + if (SUCCEEDED(rc) && !value.isEmpty()) { - LONG iRc; - progress->COMGETTER(ResultCode)(&iRc); - rc = iRc; + /* per-VM overrides global */ + if (value != "0") + fPerVM = 2; + else if (value == "0") + fPerVM = -2; } - AssertMsg (SUCCEEDED(rc), ("SaveState WaitForCompletion " - "failed with %Rhrc (%#08X)\n", rc, rc)); - - if (SUCCEEDED(rc)) - ++ saved; + /* default is true */ + if (fGlobal + fPerVM >= 0) + { + ComPtr<IInternalSessionControl> pControl = *it; + ComPtr<IProgress> progress; + + /* note that SaveStateWithReason() will simply return a failure + * if the VM is in an inappropriate state */ + rc = pControl->SaveStateWithReason(Reason_HostBatteryLow, progress.asOutParam()); + if (FAILED(rc)) + { + LogRel(("SaveState '%s' failed with %Rhrc\n", pMachine->getName().c_str(), rc)); + continue; + } + + /* Wait until the operation has been completed. */ + rc = progress->WaitForCompletion(-1); + if (SUCCEEDED(rc)) + { + LONG iRc; + progress->COMGETTER(ResultCode)(&iRc); + rc = iRc; + } + + AssertMsg(SUCCEEDED(rc), ("SaveState WaitForCompletion failed with %Rhrc (%#08X)\n", rc, rc)); + + if (SUCCEEDED(rc)) + { + LogRel(("SaveState '%s' succeeded\n", pMachine->getName().c_str())); + ++saved; + } + } } - - LogFunc (("Saved %d VMs\n", saved)); - + LogRel(("Battery Low: saved %d VMs\n", saved)); break; } + + default: + /* nothing */; } } /* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/HostVideoInputDeviceImpl.cpp b/src/VBox/Main/src-server/HostVideoInputDeviceImpl.cpp new file mode 100644 index 00000000..1e7a53d2 --- /dev/null +++ b/src/VBox/Main/src-server/HostVideoInputDeviceImpl.cpp @@ -0,0 +1,235 @@ +/* $Id: HostVideoInputDeviceImpl.cpp $ */ +/** @file + * + * Host video capture device implementation. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "HostVideoInputDeviceImpl.h" +#include "Logging.h" +#include "VirtualBoxImpl.h" +#ifdef VBOX_WITH_EXTPACK +# include "ExtPackManagerImpl.h" +#endif + +#include <iprt/ldr.h> +#include <iprt/path.h> + +#include <VBox/sup.h> + +/* + * HostVideoInputDevice implementation. + */ +DEFINE_EMPTY_CTOR_DTOR(HostVideoInputDevice) + +HRESULT HostVideoInputDevice::FinalConstruct() +{ + return BaseFinalConstruct(); +} + +void HostVideoInputDevice::FinalRelease() +{ + uninit(); + + BaseFinalRelease(); +} + +/* + * Initializes the instance. + */ +HRESULT HostVideoInputDevice::init(const com::Utf8Str &name, const com::Utf8Str &path, const com::Utf8Str &alias) +{ + LogFlowThisFunc(("\n")); + + /* Enclose the state transition NotReady->InInit->Ready */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + m.name = name; + m.path = path; + m.alias = alias; + + /* Confirm a successful initialization */ + autoInitSpan.setSucceeded(); + + return S_OK; +} + +/* + * Uninitializes the instance. + * Called either from FinalRelease() or by the parent when it gets destroyed. + */ +void HostVideoInputDevice::uninit() +{ + LogFlowThisFunc(("\n")); + + m.name.setNull(); + m.path.setNull(); + m.alias.setNull(); + + /* Enclose the state transition Ready->InUninit->NotReady */ + AutoUninitSpan autoUninitSpan(this); + if (autoUninitSpan.uninitDone()) + return; +} + +static HRESULT hostVideoInputDeviceAdd(HostVideoInputDeviceList *pList, + const com::Utf8Str &name, + const com::Utf8Str &path, + const com::Utf8Str &alias) +{ + ComObjPtr<HostVideoInputDevice> obj; + HRESULT hr = obj.createObject(); + if (SUCCEEDED(hr)) + { + hr = obj->init(name, path, alias); + if (SUCCEEDED(hr)) + pList->push_back(obj); + } + return hr; +} + +static DECLCALLBACK(int) hostWebcamAdd(void *pvUser, + const char *pszName, + const char *pszPath, + const char *pszAlias, + uint64_t *pu64Result) +{ + HostVideoInputDeviceList *pList = (HostVideoInputDeviceList *)pvUser; + HRESULT hr = hostVideoInputDeviceAdd(pList, pszName, pszPath, pszAlias); + if (FAILED(hr)) + { + *pu64Result = (uint64_t)hr; + return VERR_NOT_SUPPORTED; + } + return VINF_SUCCESS; +} + +/** @todo These typedefs must be in a header. */ +typedef DECLCALLBACK(int) FNVBOXHOSTWEBCAMADD(void *pvUser, + const char *pszName, + const char *pszPath, + const char *pszAlias, + uint64_t *pu64Result); +typedef FNVBOXHOSTWEBCAMADD *PFNVBOXHOSTWEBCAMADD; + +typedef DECLCALLBACK(int) FNVBOXHOSTWEBCAMLIST(PFNVBOXHOSTWEBCAMADD pfnWebcamAdd, + void *pvUser, + uint64_t *pu64WebcamAddResult); +typedef FNVBOXHOSTWEBCAMLIST *PFNVBOXHOSTWEBCAMLIST; + +static int loadHostWebcamLibrary(const char *pszPath, RTLDRMOD *phmod, PFNVBOXHOSTWEBCAMLIST *ppfn) +{ + int rc = VINF_SUCCESS; + RTLDRMOD hmod = NIL_RTLDRMOD; + + RTERRINFOSTATIC ErrInfo; + RTErrInfoInitStatic(&ErrInfo); + if (RTPathHavePath(pszPath)) + rc = SUPR3HardenedLdrLoadPlugIn(pszPath, &hmod, &ErrInfo.Core); + else + rc = VERR_INVALID_PARAMETER; + if (RT_SUCCESS(rc)) + { + static const char *pszSymbol = "VBoxHostWebcamList"; + rc = RTLdrGetSymbol(hmod, pszSymbol, (void **)ppfn); + + if (RT_FAILURE(rc) && rc != VERR_SYMBOL_NOT_FOUND) + LogRel(("Resolving symbol '%s': %Rrc\n", pszSymbol, rc)); + } + else + { + LogRel(("Loading the library '%s': %Rrc\n", pszPath, rc)); + if (RTErrInfoIsSet(&ErrInfo.Core)) + LogRel((" %s\n", ErrInfo.Core.pszMsg)); + + hmod = NIL_RTLDRMOD; + } + + if (RT_SUCCESS(rc)) + { + *phmod = hmod; + } + else + { + if (hmod != NIL_RTLDRMOD) + { + RTLdrClose(hmod); + hmod = NIL_RTLDRMOD; + } + } + + return rc; +} + +static const Utf8Str strExtPackPuel("Oracle VM VirtualBox Extension Pack"); + +static HRESULT fillDeviceList(VirtualBox *pVirtualBox, HostVideoInputDeviceList *pList) +{ + HRESULT hr; + Utf8Str strLibrary; + +#ifdef VBOX_WITH_EXTPACK + ExtPackManager *pExtPackMgr = pVirtualBox->getExtPackManager(); + hr = pExtPackMgr->getLibraryPathForExtPack("VBoxHostWebcam", &strExtPackPuel, &strLibrary); +#else + hr = E_NOTIMPL; +#endif + + if (SUCCEEDED(hr)) + { + PFNVBOXHOSTWEBCAMLIST pfn = NULL; + RTLDRMOD hmod = NIL_RTLDRMOD; + int rc = loadHostWebcamLibrary(strLibrary.c_str(), &hmod, &pfn); + + LogRel(("Load [%s] rc %Rrc\n", strLibrary.c_str(), rc)); + + if (RT_SUCCESS(rc)) + { + uint64_t u64Result = S_OK; + rc = pfn(hostWebcamAdd, pList, &u64Result); + Log(("VBoxHostWebcamList rc %Rrc, result 0x%08X\n", rc, u64Result)); + if (RT_FAILURE(rc)) + { + hr = (HRESULT)u64Result; + } + + RTLdrClose(hmod); + hmod = NIL_RTLDRMOD; + } + + if (SUCCEEDED(hr)) + { + if (RT_FAILURE(rc)) + hr = pVirtualBox->setError(VBOX_E_IPRT_ERROR, + "Failed to get webcam list: %Rrc", rc); + } + } + + return hr; +} + +/* static */ HRESULT HostVideoInputDevice::queryHostDevices(VirtualBox *pVirtualBox, HostVideoInputDeviceList *pList) +{ + HRESULT hr = fillDeviceList(pVirtualBox, pList); + + if (FAILED(hr)) + { + pList->clear(); + } + + return hr; +} + +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/MachineImpl.cpp b/src/VBox/Main/src-server/MachineImpl.cpp index 2ffa9b51..d7905917 100644 --- a/src/VBox/Main/src-server/MachineImpl.cpp +++ b/src/VBox/Main/src-server/MachineImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2004-2012 Oracle Corporation + * Copyright (C) 2004-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,23 +23,17 @@ # define __STDC_CONSTANT_MACROS #endif -#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER -# include <errno.h> -# include <sys/types.h> -# include <sys/stat.h> -# include <sys/ipc.h> -# include <sys/sem.h> -#endif - #include "Logging.h" #include "VirtualBoxImpl.h" #include "MachineImpl.h" +#include "ClientToken.h" #include "ProgressImpl.h" #include "ProgressProxyImpl.h" #include "MediumAttachmentImpl.h" #include "MediumImpl.h" #include "MediumLock.h" #include "USBControllerImpl.h" +#include "USBDeviceFiltersImpl.h" #include "HostImpl.h" #include "SharedFolderImpl.h" #include "GuestOSTypeImpl.h" @@ -48,9 +42,9 @@ #include "StorageControllerImpl.h" #include "DisplayImpl.h" #include "DisplayUtils.h" -#include "BandwidthControlImpl.h" #include "MachineImplCloneVM.h" #include "AutostartDb.h" +#include "SystemPropertiesImpl.h" // generated header #include "VBoxEvents.h" @@ -73,6 +67,7 @@ #include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */ #include <iprt/sha.h> #include <iprt/string.h> +#include <iprt/base64.h> #include <VBox/com/array.h> #include <VBox/com/list.h> @@ -161,14 +156,18 @@ Machine::HWData::HWData() mCPUHotPlugEnabled = false; mMemoryBalloonSize = 0; mPageFusionEnabled = false; + mGraphicsControllerType = GraphicsControllerType_VBoxVGA; mVRAMSize = 8; mAccelerate3DEnabled = false; mAccelerate2DVideoEnabled = false; mMonitorCount = 1; - mVideoCaptureFile = "Test.webm"; - mVideoCaptureWidth = 640; - mVideoCaptureHeight = 480; - mVideoCaptureEnabled = true; + mVideoCaptureWidth = 1024; + mVideoCaptureHeight = 768; + mVideoCaptureRate = 512; + mVideoCaptureFPS = 25; + mVideoCaptureEnabled = false; + for (unsigned i = 0; i < RT_ELEMENTS(maVideoCaptureScreens); i++) + maVideoCaptureScreens[i] = true; mHWVirtExEnabled = true; mHWVirtExNestedPagingEnabled = true; @@ -179,18 +178,16 @@ Machine::HWData::HWData() mHWVirtExLargePagesEnabled = false; #endif mHWVirtExVPIDEnabled = true; + mHWVirtExUXEnabled = true; mHWVirtExForceEnabled = false; -#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS) - mHWVirtExExclusive = false; -#else - mHWVirtExExclusive = true; -#endif #if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) mPAEEnabled = true; #else mPAEEnabled = false; #endif + mLongMode = HC_ARCH_BITS == 64 ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled; mSyntheticCpu = false; + mTripleFaultReset = false; mHPETEnabled = false; /* default boot order: floppy - DVD - HDD */ @@ -243,13 +240,15 @@ Machine::MediaData::~MediaData() // constructor / destructor ///////////////////////////////////////////////////////////////////////////// -Machine::Machine() - : mCollectorGuest(NULL), - mPeer(NULL), - mParent(NULL), - mSerialPorts(), - mParallelPorts(), - uRegistryNeedsSaving(0) +Machine::Machine() : +#ifdef VBOX_WITH_RESOURCE_USAGE_API + mCollectorGuest(NULL), +#endif + mPeer(NULL), + mParent(NULL), + mSerialPorts(), + mParallelPorts(), + uRegistryNeedsSaving(0) {} Machine::~Machine() @@ -349,6 +348,10 @@ HRESULT Machine::init(VirtualBox *aParent, /* Apply serial port defaults */ for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot) mSerialPorts[slot]->applyDefaults(aOsType); + + /* Let the OS type select 64-bit ness. */ + mHWData->mLongMode = aOsType->is64Bit() + ? settings::Hardware::LongMode_Enabled : settings::Hardware::LongMode_Disabled; } /* At this point the changing of the current state modification @@ -568,6 +571,10 @@ HRESULT Machine::init(VirtualBox *aParent, autoInitSpan.setSucceeded(); else { + /* Ignore all errors from unregistering, they would destroy + * the more interesting error information we already have, + * pinpointing the issue with the VM config. */ + ErrorInfoKeeper eik; autoInitSpan.setLimited(); // uninit media from this machine's media registry, or else @@ -684,7 +691,7 @@ HRESULT Machine::registeredInit() { AssertReturn(!isSessionMachine(), E_FAIL); AssertReturn(!isSnapshotMachine(), E_FAIL); - AssertReturn(!mData->mUuid.isEmpty(), E_FAIL); + AssertReturn(mData->mUuid.isValid(), E_FAIL); AssertReturn(!mData->mAccessible, E_FAIL); HRESULT rc = initDataAndChildObjects(); @@ -853,7 +860,8 @@ void Machine::uninit() * "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 + + if (uuidMachine.isValid() && !uuidMachine.isZero()) // can be empty if we're called from a failure of Machine::init mParent->unregisterMachineMedia(uuidMachine); // has machine been modified? @@ -898,6 +906,10 @@ STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible) LogFlowThisFunc(("ENTER\n")); + /* In some cases (medium registry related), it is necessary to be able to + * go through the list of all machines. Happens when an inaccessible VM + * has a sensible medium registry. */ + AutoReadLock mllock(mParent->getMachinesListLockHandle() COMMA_LOCKVAL_SRC_POS); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); HRESULT rc = S_OK; @@ -999,8 +1011,9 @@ STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName) // 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")); + + if (test.isValid()) + return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name")); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); @@ -1274,8 +1287,8 @@ STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType) // Resize network adapter array, to be finalized on commit/rollback. // We must not throw away entries yet, otherwise settings are lost // without a way to roll back. - uint32_t newCount = Global::getMaxNetworkAdapters(aChipsetType); - uint32_t oldCount = mNetworkAdapters.size(); + size_t newCount = Global::getMaxNetworkAdapters(aChipsetType); + size_t oldCount = mNetworkAdapters.size(); if (newCount > oldCount) { mNetworkAdapters.resize(newCount); @@ -1337,7 +1350,7 @@ STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - if (!mHWData->mHardwareUUID.isEmpty()) + if (!mHWData->mHardwareUUID.isZero()) mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID); else mData->mUuid.toUtf16().cloneTo(aUUID); @@ -1348,7 +1361,7 @@ STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID) STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID) { Guid hardwareUUID(aUUID); - if (hardwareUUID.isEmpty()) + if (!hardwareUUID.isValid()) return E_INVALIDARG; AutoCaller autoCaller(this); @@ -1500,7 +1513,7 @@ STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap) mHWData.backup(); mHWData->mCpuExecutionCap = aExecutionCap; - /* Save settings if online - todo why is this required?? */ + /** Save settings if online - @todo why is this required? -- @bugref{6818} */ if (Global::IsOnline(mData->mMachineState)) saveSettings(NULL); @@ -1508,21 +1521,21 @@ STDMETHODIMP Machine::COMSETTER(CPUExecutionCap)(ULONG aExecutionCap) } -STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled) +STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *aEnabled) { - CheckComArgOutPointerValid(enabled); + CheckComArgOutPointerValid(aEnabled); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *enabled = mHWData->mCPUHotPlugEnabled; + *aEnabled = mHWData->mCPUHotPlugEnabled; return S_OK; } -STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled) +STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL aEnabled) { HRESULT rc = S_OK; @@ -1534,9 +1547,9 @@ STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled) rc = checkStateDependency(MutableStateDep); if (FAILED(rc)) return rc; - if (mHWData->mCPUHotPlugEnabled != enabled) + if (mHWData->mCPUHotPlugEnabled != aEnabled) { - if (enabled) + if (aEnabled) { setModified(IsModified_MachineData); mHWData.backup(); @@ -1575,31 +1588,31 @@ STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled) } } - mHWData->mCPUHotPlugEnabled = enabled; + mHWData->mCPUHotPlugEnabled = aEnabled; return rc; } -STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *enabled) +STDMETHODIMP Machine::COMGETTER(EmulatedUSBCardReaderEnabled)(BOOL *aEnabled) { #ifdef VBOX_WITH_USB_CARDREADER - CheckComArgOutPointerValid(enabled); + CheckComArgOutPointerValid(aEnabled); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *enabled = mHWData->mEmulatedUSBCardReaderEnabled; + *aEnabled = mHWData->mEmulatedUSBCardReaderEnabled; return S_OK; #else - NOREF(enabled); + NOREF(aEnabled); return E_NOTIMPL; #endif } -STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled) +STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL aEnabled) { #ifdef VBOX_WITH_USB_CARDREADER AutoCaller autoCaller(this); @@ -1611,41 +1624,29 @@ STDMETHODIMP Machine::COMSETTER(EmulatedUSBCardReaderEnabled)(BOOL enabled) setModified(IsModified_MachineData); mHWData.backup(); - mHWData->mEmulatedUSBCardReaderEnabled = enabled; + mHWData->mEmulatedUSBCardReaderEnabled = aEnabled; return S_OK; #else - NOREF(enabled); + NOREF(aEnabled); return E_NOTIMPL; #endif } -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) +STDMETHODIMP Machine::COMGETTER(HPETEnabled)(BOOL *aEnabled) { - CheckComArgOutPointerValid(enabled); + CheckComArgOutPointerValid(aEnabled); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *enabled = mHWData->mHPETEnabled; + *aEnabled = mHWData->mHPETEnabled; return S_OK; } -STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled) +STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL aEnabled) { HRESULT rc = S_OK; @@ -1659,12 +1660,12 @@ STDMETHODIMP Machine::COMSETTER(HPETEnabled)(BOOL enabled) setModified(IsModified_MachineData); mHWData.backup(); - mHWData->mHPETEnabled = enabled; + mHWData->mHPETEnabled = aEnabled; return rc; } -STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL * fEnabled) +STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL *fEnabled) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -1675,23 +1676,102 @@ STDMETHODIMP Machine::COMGETTER(VideoCaptureEnabled)(BOOL * fEnabled) return S_OK; } -STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled) +STDMETHODIMP Machine::COMSETTER(VideoCaptureEnabled)(BOOL fEnabled) { + HRESULT rc = S_OK; + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + setModified(IsModified_MachineData); + mHWData.backup(); mHWData->mVideoCaptureEnabled = fEnabled; + + alock.release(); + rc = onVideoCaptureChange(); + alock.acquire(); + if (FAILED(rc)) + { + /* + * Normally we would do the actual change _after_ onVideoCaptureChange() succeeded. + * We cannot do this because that function uses Machine::GetVideoCaptureEnabled to + * determine if it should start or stop capturing. Therefore we need to manually + * undo change. + */ + mHWData->mVideoCaptureEnabled = mHWData.backedUpData()->mVideoCaptureEnabled; + return rc; + } + + /** Save settings if online - @todo why is this required? -- @bugref{6818} */ + if (Global::IsOnline(mData->mMachineState)) + saveSettings(NULL); + + return rc; +} + +STDMETHODIMP Machine::COMGETTER(VideoCaptureScreens)(ComSafeArrayOut(BOOL, aScreens)) +{ + CheckComArgOutSafeArrayPointerValid(aScreens); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + SafeArray<BOOL> screens(mHWData->mMonitorCount); + for (unsigned i = 0; i < screens.size(); i++) + screens[i] = mHWData->maVideoCaptureScreens[i]; + screens.detachTo(ComSafeArrayOutArg(aScreens)); + return S_OK; +} + +STDMETHODIMP Machine::COMSETTER(VideoCaptureScreens)(ComSafeArrayIn(BOOL, aScreens)) +{ + SafeArray<BOOL> screens(ComSafeArrayInArg(aScreens)); + AssertReturn(screens.size() <= RT_ELEMENTS(mHWData->maVideoCaptureScreens), E_INVALIDARG); + bool fChanged = false; + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + for (unsigned i = 0; i < screens.size(); i++) + { + if (mHWData->maVideoCaptureScreens[i] != RT_BOOL(screens[i])) + { + mHWData->maVideoCaptureScreens[i] = RT_BOOL(screens[i]); + fChanged = true; + } + } + if (fChanged) + { + alock.release(); + HRESULT rc = onVideoCaptureChange(); + alock.acquire(); + if (FAILED(rc)) return rc; + setModified(IsModified_MachineData); + + /** Save settings if online - @todo why is this required? -- @bugref{6818} */ + if (Global::IsOnline(mData->mMachineState)) + saveSettings(NULL); + } + return S_OK; } -STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR * apFile) +STDMETHODIMP Machine::COMGETTER(VideoCaptureFile)(BSTR *apFile) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - mHWData->mVideoCaptureFile.cloneTo(apFile); + if (mHWData->mVideoCaptureFile.isEmpty()) + { + Utf8Str defaultFile; + getDefaultVideoCaptureFile(defaultFile); + defaultFile.cloneTo(apFile); + } + else + mHWData->mVideoCaptureFile.cloneTo(apFile); return S_OK; } @@ -1702,54 +1782,181 @@ STDMETHODIMP Machine::COMSETTER(VideoCaptureFile)(IN_BSTR aFile) if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - if(strFile.isEmpty()) - strFile = "VideoCap.webm"; + + if ( Global::IsOnline(mData->mMachineState) + && mHWData->mVideoCaptureEnabled) + return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); + + if (!RTPathStartsWithRoot(strFile.c_str())) + return setError(E_INVALIDARG, tr("Video capture file name '%s' is not absolute"), strFile.c_str()); + + if (!strFile.isEmpty()) + { + Utf8Str defaultFile; + getDefaultVideoCaptureFile(defaultFile); + if (!RTPathCompare(strFile.c_str(), defaultFile.c_str())) + strFile.setNull(); + } + + setModified(IsModified_MachineData); + mHWData.backup(); mHWData->mVideoCaptureFile = strFile; + return S_OK; } +STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *aHorzRes) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); -STDMETHODIMP Machine::COMGETTER(VideoCaptureWidth)(ULONG *ulHorzRes) + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + *aHorzRes = mHWData->mVideoCaptureWidth; + return S_OK; +} + +STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG aHorzRes) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if ( Global::IsOnline(mData->mMachineState) + && mHWData->mVideoCaptureEnabled) + return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); + + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mVideoCaptureWidth = aHorzRes; + + return S_OK; +} + +STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *aVertRes) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *ulHorzRes = mHWData->mVideoCaptureWidth; + *aVertRes = mHWData->mVideoCaptureHeight; return S_OK; } -STDMETHODIMP Machine::COMSETTER(VideoCaptureWidth)(ULONG ulHorzRes) +STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG aVertRes) { AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) - { - LogFlow(("Autolocked failed\n")); - return autoCaller.rc(); - } + if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - mHWData->mVideoCaptureWidth = ulHorzRes; + + if ( Global::IsOnline(mData->mMachineState) + && mHWData->mVideoCaptureEnabled) + return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); + + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mVideoCaptureHeight = aVertRes; + return S_OK; } -STDMETHODIMP Machine::COMGETTER(VideoCaptureHeight)(ULONG *ulVertRes) +STDMETHODIMP Machine::COMGETTER(VideoCaptureRate)(ULONG *aRate) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *ulVertRes = mHWData->mVideoCaptureHeight; + *aRate = mHWData->mVideoCaptureRate; + return S_OK; +} + +STDMETHODIMP Machine::COMSETTER(VideoCaptureRate)(ULONG aRate) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if ( Global::IsOnline(mData->mMachineState) + && mHWData->mVideoCaptureEnabled) + return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); + + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mVideoCaptureRate = aRate; + + return S_OK; +} + +STDMETHODIMP Machine::COMGETTER(VideoCaptureFPS)(ULONG *aFPS) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + *aFPS = mHWData->mVideoCaptureFPS; + return S_OK; +} + +STDMETHODIMP Machine::COMSETTER(VideoCaptureFPS)(ULONG aFPS) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if ( Global::IsOnline(mData->mMachineState) + && mHWData->mVideoCaptureEnabled) + return setError(E_INVALIDARG, tr("Cannot change parameters while capturing is enabled")); + + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mVideoCaptureFPS = aFPS; + return S_OK; } -STDMETHODIMP Machine::COMSETTER(VideoCaptureHeight)(ULONG ulVertRes) +STDMETHODIMP Machine::COMGETTER(GraphicsControllerType)(GraphicsControllerType_T *aGraphicsControllerType) { + CheckComArgOutPointerValid(aGraphicsControllerType); + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - mHWData->mVideoCaptureHeight = ulVertRes; + + *aGraphicsControllerType = mHWData->mGraphicsControllerType; + + return S_OK; +} + +STDMETHODIMP Machine::COMSETTER(GraphicsControllerType)(GraphicsControllerType_T aGraphicsControllerType) +{ + switch (aGraphicsControllerType) + { + case GraphicsControllerType_Null: + case GraphicsControllerType_VBoxVGA: +#ifdef VBOX_WITH_VMSVGA + case GraphicsControllerType_VMSVGA: +#endif + break; + default: + return setError(E_INVALIDARG, tr("The graphics controller type (%d) is invalid"), aGraphicsControllerType); + } + + 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; + + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mGraphicsControllerType = aGraphicsControllerType; + return S_OK; } @@ -1838,20 +2045,20 @@ STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize) #endif } -STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled) +STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *aEnabled) { - CheckComArgOutPointerValid(enabled); + CheckComArgOutPointerValid(aEnabled); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *enabled = mHWData->mPageFusionEnabled; + *aEnabled = mHWData->mPageFusionEnabled; return S_OK; } -STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled) +STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL aEnabled) { #ifdef VBOX_WITH_PAGE_SHARING AutoCaller autoCaller(this); @@ -1862,24 +2069,24 @@ STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled) /** @todo must support changes for running vms and keep this in sync with IGuest. */ setModified(IsModified_MachineData); mHWData.backup(); - mHWData->mPageFusionEnabled = enabled; + mHWData->mPageFusionEnabled = aEnabled; return S_OK; #else - NOREF(enabled); + NOREF(aEnabled); return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts")); #endif } -STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled) +STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *aEnabled) { - CheckComArgOutPointerValid(enabled); + CheckComArgOutPointerValid(aEnabled); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *enabled = mHWData->mAccelerate3DEnabled; + *aEnabled = mHWData->mAccelerate3DEnabled; return S_OK; } @@ -1904,16 +2111,16 @@ STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable) } -STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled) +STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *aEnabled) { - CheckComArgOutPointerValid(enabled); + CheckComArgOutPointerValid(aEnabled); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *enabled = mHWData->mAccelerate2DVideoEnabled; + *aEnabled = mHWData->mAccelerate2DVideoEnabled; return S_OK; } @@ -1996,18 +2203,55 @@ STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - switch(property) + switch (property) { - case CPUPropertyType_PAE: - *aVal = mHWData->mPAEEnabled; - break; + case CPUPropertyType_PAE: + *aVal = mHWData->mPAEEnabled; + break; - case CPUPropertyType_Synthetic: - *aVal = mHWData->mSyntheticCpu; - break; + case CPUPropertyType_Synthetic: + *aVal = mHWData->mSyntheticCpu; + break; - default: - return E_INVALIDARG; + case CPUPropertyType_LongMode: + if (mHWData->mLongMode == settings::Hardware::LongMode_Enabled) + *aVal = TRUE; + else if (mHWData->mLongMode == settings::Hardware::LongMode_Disabled) + *aVal = FALSE; +#if HC_ARCH_BITS == 64 + else + *aVal = TRUE; +#else + else + { + *aVal = FALSE; + + ComPtr<IGuestOSType> ptrGuestOSType; + HRESULT hrc2 = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), ptrGuestOSType.asOutParam()); + if (SUCCEEDED(hrc2)) + { + BOOL fIs64Bit = FALSE; + hrc2 = ptrGuestOSType->COMGETTER(Is64Bit)(&fIs64Bit); AssertComRC(hrc2); + if (SUCCEEDED(hrc2) && fIs64Bit) + { + ComObjPtr<Host> ptrHost = mParent->host(); + alock.release(); + + hrc2 = ptrHost->GetProcessorFeature(ProcessorFeature_LongMode, aVal); AssertComRC(hrc2); + if (FAILED(hrc2)) + *aVal = FALSE; + } + } + } +#endif + break; + + case CPUPropertyType_TripleFaultReset: + *aVal = mHWData->mTripleFaultReset; + break; + + default: + return E_INVALIDARG; } return S_OK; } @@ -2022,22 +2266,34 @@ STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal) HRESULT rc = checkStateDependency(MutableStateDep); if (FAILED(rc)) return rc; - switch(property) + switch (property) { - case CPUPropertyType_PAE: - setModified(IsModified_MachineData); - mHWData.backup(); - mHWData->mPAEEnabled = !!aVal; - break; + case CPUPropertyType_PAE: + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mPAEEnabled = !!aVal; + break; - case CPUPropertyType_Synthetic: - setModified(IsModified_MachineData); - mHWData.backup(); - mHWData->mSyntheticCpu = !!aVal; - break; + case CPUPropertyType_Synthetic: + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mSyntheticCpu = !!aVal; + break; - default: - return E_INVALIDARG; + case CPUPropertyType_LongMode: + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mLongMode = !aVal ? settings::Hardware::LongMode_Disabled : settings::Hardware::LongMode_Enabled; + break; + + case CPUPropertyType_TripleFaultReset: + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mTripleFaultReset = !!aVal; + break; + + default: + return E_INVALIDARG; } return S_OK; } @@ -2259,10 +2515,6 @@ STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal = mHWData->mHWVirtExEnabled; break; - case HWVirtExPropertyType_Exclusive: - *aVal = mHWData->mHWVirtExExclusive; - break; - case HWVirtExPropertyType_VPID: *aVal = mHWData->mHWVirtExVPIDEnabled; break; @@ -2271,6 +2523,10 @@ STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal = mHWData->mHWVirtExNestedPagingEnabled; break; + case HWVirtExPropertyType_UnrestrictedExecution: + *aVal = mHWData->mHWVirtExUXEnabled; + break; + case HWVirtExPropertyType_LargePages: *aVal = mHWData->mHWVirtExLargePagesEnabled; #if defined(DEBUG_bird) && defined(RT_OS_LINUX) /* This feature is deadly here */ @@ -2306,12 +2562,6 @@ STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL mHWData->mHWVirtExEnabled = !!aVal; break; - case HWVirtExPropertyType_Exclusive: - setModified(IsModified_MachineData); - mHWData.backup(); - mHWData->mHWVirtExExclusive = !!aVal; - break; - case HWVirtExPropertyType_VPID: setModified(IsModified_MachineData); mHWData.backup(); @@ -2324,6 +2574,12 @@ STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL mHWData->mHWVirtExNestedPagingEnabled = !!aVal; break; + case HWVirtExPropertyType_UnrestrictedExecution: + setModified(IsModified_MachineData); + mHWData.backup(); + mHWData->mHWVirtExUXEnabled = !!aVal; + break; + case HWVirtExPropertyType_LargePages: setModified(IsModified_MachineData); mHWData.backup(); @@ -2444,10 +2700,40 @@ STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter) return S_OK; } -STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController) +STDMETHODIMP Machine::COMGETTER(USBControllers)(ComSafeArrayOut(IUSBController *, aUSBControllers)) +{ +#ifdef VBOX_WITH_VUSB + CheckComArgOutPointerValid(aUSBControllers); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + clearError(); + MultiResult rc(S_OK); + +# ifdef VBOX_WITH_USB + rc = mParent->host()->checkUSBProxyService(); + if (FAILED(rc)) return rc; +# endif + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + SafeIfaceArray<IUSBController> ctrls(*mUSBControllers.data()); + ctrls.detachTo(ComSafeArrayOutArg(aUSBControllers)); + return S_OK; +#else + /* Note: The GUI depends on this method returning E_NOTIMPL with no + * extended error info to indicate that USB is simply not available + * (w/o treating it as a failure), for example, as in OSE */ + NOREF(aUSBControllers); + ReturnComNotImplemented(); +#endif /* VBOX_WITH_VUSB */ +} + +STDMETHODIMP Machine::COMGETTER(USBDeviceFilters)(IUSBDeviceFilters **aUSBDeviceFilters) { #ifdef VBOX_WITH_VUSB - CheckComArgOutPointerValid(aUSBController); + CheckComArgOutPointerValid(aUSBDeviceFilters); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -2462,12 +2748,12 @@ STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController) AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - return rc = mUSBController.queryInterfaceTo(aUSBController); + return rc = mUSBDeviceFilters.queryInterfaceTo(aUSBDeviceFilters); #else /* Note: The GUI depends on this method returning E_NOTIMPL with no * extended error info to indicate that USB is simply not available * (w/o treating it as a failure), for example, as in OSE */ - NOREF(aUSBController); + NOREF(aUSBDeviceFilters); ReturnComNotImplemented(); #endif /* VBOX_WITH_VUSB */ } @@ -2685,8 +2971,7 @@ STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode) return S_OK; } -STDMETHODIMP -Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode) +STDMETHODIMP Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode) { HRESULT rc = S_OK; @@ -2704,7 +2989,7 @@ Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode) mHWData.backup(); mHWData->mClipboardMode = aClipboardMode; - /* Save settings if online - todo why is this required?? */ + /** Save settings if online - @todo why is this required? -- @bugref{6818} */ if (Global::IsOnline(mData->mMachineState)) saveSettings(NULL); @@ -2725,8 +3010,7 @@ STDMETHODIMP Machine::COMGETTER(DragAndDropMode)(DragAndDropMode_T *aDragAndDrop return S_OK; } -STDMETHODIMP -Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode) +STDMETHODIMP Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode) { HRESULT rc = S_OK; @@ -2744,15 +3028,14 @@ Machine::COMSETTER(DragAndDropMode)(DragAndDropMode_T aDragAndDropMode) mHWData.backup(); mHWData->mDragAndDropMode = aDragAndDropMode; - /* Save settings if online - todo why is this required?? */ + /** Save settings if online - @todo why is this required? -- @bugref{6818} */ if (Global::IsOnline(mData->mMachineState)) saveSettings(NULL); return S_OK; } -STDMETHODIMP -Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns) +STDMETHODIMP Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns) { CheckComArgOutPointerValid(aPatterns); @@ -2773,8 +3056,7 @@ Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns) return S_OK; } -STDMETHODIMP -Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns) +STDMETHODIMP Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -2790,8 +3072,7 @@ Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns) return rc; } -STDMETHODIMP -Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers)) +STDMETHODIMP Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers)) { CheckComArgOutSafeArrayPointerValid(aStorageControllers); @@ -2806,8 +3087,7 @@ Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aSt return S_OK; } -STDMETHODIMP -Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled) +STDMETHODIMP Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled) { CheckComArgOutPointerValid(aEnabled); @@ -3301,7 +3581,7 @@ STDMETHODIMP Machine::LockMachine(ISession *aSession, if (FAILED(rc)) // the failure may occur w/o any error info (from RPC), so provide one return setError(VBOX_E_VM_ERROR, - tr("Failed to get a console object from the direct session (%Rrc)"), rc); + tr("Failed to get a console object from the direct session (%Rhrc)"), rc); ComAssertRet(!pConsoleW.isNull(), E_FAIL); @@ -3313,7 +3593,7 @@ STDMETHODIMP Machine::LockMachine(ISession *aSession, if (FAILED(rc)) // the failure may occur w/o any error info (from RPC), so provide one return setError(VBOX_E_VM_ERROR, - tr("Failed to assign the machine to the session (%Rrc)"), rc); + tr("Failed to assign the machine to the session (%Rhrc)"), rc); alock.acquire(); // need to revalidate the state after acquiring the lock again @@ -3354,6 +3634,17 @@ STDMETHODIMP Machine::LockMachine(ISession *aSession, if (fLaunchingVMProcess) { + if (mData->mSession.mPID == NIL_RTPROCESS) + { + // two or more clients racing for a lock, the one which set the + // session state to Spawning will win, the others will get an + // error as we can't decide here if waiting a little would help + // (only for shared locks this would avoid an error) + return setError(VBOX_E_INVALID_OBJECT_STATE, + tr("The machine '%s' already has a lock request pending"), + mUserData->s.strName.c_str()); + } + // this machine is awaiting for a spawning session to be opened: // then the calling process must be the one that got started by // LaunchVMProcess() @@ -3390,6 +3681,19 @@ STDMETHODIMP Machine::LockMachine(ISession *aSession, SessionState_T origState = mData->mSession.mState; mData->mSession.mState = SessionState_Spawning; +#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER + /* Get the client token ID to be passed to the client process */ + Utf8Str strTokenId; + sessionMachine->getTokenId(strTokenId); + Assert(!strTokenId.isEmpty()); +#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */ + /* Get the client token to be passed to the client process */ + ComPtr<IToken> pToken(sessionMachine->getToken()); + /* The token is now "owned" by pToken, fix refcount */ + if (!pToken.isNull()) + pToken->Release(); +#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */ + /* * Release the lock before calling the client process -- it will call * Machine/SessionMachine methods. Releasing the lock here is quite safe @@ -3403,13 +3707,19 @@ STDMETHODIMP Machine::LockMachine(ISession *aSession, alock.release(); LogFlowThisFunc(("Calling AssignMachine()...\n")); - rc = pSessionControl->AssignMachine(sessionMachine, lockType); +#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER + rc = pSessionControl->AssignMachine(sessionMachine, lockType, Bstr(strTokenId).raw()); +#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */ + rc = pSessionControl->AssignMachine(sessionMachine, lockType, pToken); + /* Now the token is owned by the client process. */ + pToken.setNull(); +#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */ LogFlowThisFunc(("AssignMachine() returned %08X\n", rc)); /* The failure may occur w/o any error info (from RPC), so provide one */ if (FAILED(rc)) setError(VBOX_E_VM_ERROR, - tr("Failed to assign the machine to the session (%Rrc)"), rc); + tr("Failed to assign the machine to the session (%Rhrc)"), rc); if ( SUCCEEDED(rc) && fLaunchingVMProcess @@ -3442,7 +3752,7 @@ STDMETHODIMP Machine::LockMachine(ISession *aSession, /* The failure may occur w/o any error info (from RPC), so provide one */ if (FAILED(rc)) setError(VBOX_E_VM_ERROR, - tr("Failed to assign the machine to the remote session (%Rrc)"), rc); + tr("Failed to assign the machine to the remote session (%Rhrc)"), rc); } if (FAILED(rc)) @@ -3470,7 +3780,7 @@ STDMETHODIMP Machine::LockMachine(ISession *aSession, { /* Close the remote session, remove the remote control from the list * and reset session state to Closed (@note keep the code in sync - * with the relevant part in openSession()). */ + * with the relevant part in checkForSpawnFailure()). */ Assert(mData->mSession.mRemoteControls.size() == 1); if (mData->mSession.mRemoteControls.size() == 1) @@ -3535,28 +3845,52 @@ STDMETHODIMP Machine::LockMachine(ISession *aSession, * @note Locks objects! */ STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession, - IN_BSTR aType, + IN_BSTR aFrontend, IN_BSTR aEnvironment, IProgress **aProgress) { - CheckComArgStrNotEmptyOrNull(aType); - Utf8Str strType(aType); + CheckComArgStr(aFrontend); + Utf8Str strFrontend(aFrontend); Utf8Str strEnvironment(aEnvironment); /* "emergencystop" doesn't need the session, so skip the checks/interface * retrieval. This code doesn't quite fit in here, but introducing a * special API method would be even more effort, and would require explicit * support by every API client. It's better to hide the feature a bit. */ - if (strType != "emergencystop") + if (strFrontend != "emergencystop") CheckComArgNotNull(aSession); CheckComArgOutPointerValid(aProgress); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - ComPtr<IInternalSessionControl> control; HRESULT rc = S_OK; + if (strFrontend.isEmpty()) + { + Bstr bstrFrontend; + rc = COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam()); + if (FAILED(rc)) + return rc; + strFrontend = bstrFrontend; + if (strFrontend.isEmpty()) + { + ComPtr<ISystemProperties> systemProperties; + rc = mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam()); + if (FAILED(rc)) + return rc; + rc = systemProperties->COMGETTER(DefaultFrontend)(bstrFrontend.asOutParam()); + if (FAILED(rc)) + return rc; + strFrontend = bstrFrontend; + } + /* paranoia - emergencystop is not a valid default */ + if (strFrontend == "emergencystop") + strFrontend = Utf8Str::Empty; + } + /* default frontend: Qt GUI */ + if (strFrontend.isEmpty()) + strFrontend = "GUI/Qt"; - if (strType != "emergencystop") + if (strFrontend != "emergencystop") { /* check the session state */ SessionState_T state; @@ -3569,21 +3903,18 @@ STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession, tr("The given session is busy")); /* get the IInternalSessionControl interface */ - control = aSession; + ComPtr<IInternalSessionControl> control(aSession); ComAssertMsgRet(!control.isNull(), ("No IInternalSessionControl interface"), E_INVALIDARG); - } - /* get the teleporter enable state for the progress object init. */ - BOOL fTeleporterEnabled; - rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled); - if (FAILED(rc)) - return rc; + /* get the teleporter enable state for the progress object init. */ + BOOL fTeleporterEnabled; + rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled); + if (FAILED(rc)) + return rc; - /* create a progress object */ - if (strType != "emergencystop") - { + /* create a progress object */ ComObjPtr<ProgressProxy> progress; progress.createObject(); rc = progress->init(mParent, @@ -3591,13 +3922,13 @@ STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession, Bstr(tr("Starting VM")).raw(), TRUE /* aCancelable */, fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */, - BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strType.c_str()).raw(), + BstrFmt(tr("Creating process for virtual machine \"%s\" (%s)"), mUserData->s.strName.c_str(), strFrontend.c_str()).raw(), 2 /* uFirstOperationWeight */, fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */); if (SUCCEEDED(rc)) { - rc = launchVMProcess(control, strType, strEnvironment, progress); + rc = launchVMProcess(control, strFrontend, strEnvironment, progress); if (SUCCEEDED(rc)) { progress.queryInterfaceTo(aProgress); @@ -3725,9 +4056,18 @@ STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName, tr("Could not get type of controller '%ls'"), aControllerName); + bool fSilent = false; + Utf8Str strReconfig; + + /* Check whether the flag to allow silent storage attachment reconfiguration is set. */ + strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused")); + if ( mData->mMachineState == MachineState_Paused + && strReconfig == "1") + fSilent = true; + /* 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)) + if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState)) fHotplug = true; if (fHotplug && !isControllerHotplugCapable(ctrlType)) @@ -3834,6 +4174,46 @@ STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName, /* the simplest case: restore the whole attachment * and return, nothing else to do */ mMediaData->mAttachments.push_back(pAttachTemp); + + /* Reattach the medium to the VM. */ + if (fHotplug || fSilent) + { + mediumLock.release(); + treeLock.release(); + alock.release(); + + MediumLockList *pMediumLockList(new MediumLockList()); + + rc = medium->createMediumLockList(true /* fFailIfInaccessible */, + true /* fMediumLockWrite */, + NULL, + *pMediumLockList); + alock.acquire(); + if (FAILED(rc)) + delete pMediumLockList; + else + { + mData->mSession.mLockedMedia.Unlock(); + alock.release(); + rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList); + mData->mSession.mLockedMedia.Lock(); + alock.acquire(); + } + alock.release(); + + if (SUCCEEDED(rc)) + { + rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent); + /* Remove lock list in case of error. */ + if (FAILED(rc)) + { + mData->mSession.mLockedMedia.Unlock(); + mData->mSession.mLockedMedia.Remove(pAttachTemp); + mData->mSession.mLockedMedia.Lock(); + } + } + } + return S_OK; } @@ -3893,6 +4273,46 @@ STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName, /* the simplest case: restore the whole attachment * and return, nothing else to do */ mMediaData->mAttachments.push_back(*it); + + /* Reattach the medium to the VM. */ + if (fHotplug || fSilent) + { + mediumLock.release(); + treeLock.release(); + alock.release(); + + MediumLockList *pMediumLockList(new MediumLockList()); + + rc = medium->createMediumLockList(true /* fFailIfInaccessible */, + true /* fMediumLockWrite */, + NULL, + *pMediumLockList); + alock.acquire(); + if (FAILED(rc)) + delete pMediumLockList; + else + { + mData->mSession.mLockedMedia.Unlock(); + alock.release(); + rc = mData->mSession.mLockedMedia.Insert(pAttachTemp, pMediumLockList); + mData->mSession.mLockedMedia.Lock(); + alock.acquire(); + } + alock.release(); + + if (SUCCEEDED(rc)) + { + rc = onStorageDeviceChange(pAttachTemp, FALSE /* aRemove */, fSilent); + /* Remove lock list in case of error. */ + if (FAILED(rc)) + { + mData->mSession.mLockedMedia.Unlock(); + mData->mSession.mLockedMedia.Remove(pAttachTemp); + mData->mSession.mLockedMedia.Lock(); + } + } + } + return S_OK; } else if ( foundIt == oldAtts.end() @@ -4098,6 +4518,7 @@ STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName, false /* fTempEject */, false /* fNonRotational */, false /* fDiscard */, + false /* fHotPluggable */, Utf8Str::Empty); if (FAILED(rc)) return rc; @@ -4126,8 +4547,42 @@ STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName, treeLock.release(); alock.release(); - if (fHotplug) - rc = onStorageDeviceChange(attachment, FALSE /* aRemove */); + if (fHotplug || fSilent) + { + if (!medium.isNull()) + { + MediumLockList *pMediumLockList(new MediumLockList()); + + rc = medium->createMediumLockList(true /* fFailIfInaccessible */, + true /* fMediumLockWrite */, + NULL, + *pMediumLockList); + alock.acquire(); + if (FAILED(rc)) + delete pMediumLockList; + else + { + mData->mSession.mLockedMedia.Unlock(); + alock.release(); + rc = mData->mSession.mLockedMedia.Insert(attachment, pMediumLockList); + mData->mSession.mLockedMedia.Lock(); + alock.acquire(); + } + alock.release(); + } + + if (SUCCEEDED(rc)) + { + rc = onStorageDeviceChange(attachment, FALSE /* aRemove */, fSilent); + /* Remove lock list in case of error. */ + if (FAILED(rc)) + { + mData->mSession.mLockedMedia.Unlock(); + mData->mSession.mLockedMedia.Remove(attachment); + mData->mSession.mLockedMedia.Lock(); + } + } + } mParent->saveModifiedRegistries(); @@ -4164,9 +4619,18 @@ STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort tr("Could not get type of controller '%ls'"), aControllerName); + bool fSilent = false; + Utf8Str strReconfig; + + /* Check whether the flag to allow silent storage attachment reconfiguration is set. */ + strReconfig = getExtraData(Utf8Str("VBoxInternal2/SilentReconfigureWhilePaused")); + if ( mData->mMachineState == MachineState_Paused + && strReconfig == "1") + fSilent = true; + /* 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)) + if (!fSilent && Global::IsOnlineOrTransient(mData->mMachineState)) fHotplug = true; if (fHotplug && !isControllerHotplugCapable(ctrlType)) @@ -4187,10 +4651,10 @@ STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort * 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) + if (fHotplug || fSilent) { alock.release(); - rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */); + rc = onStorageDeviceChange(pAttach, TRUE /* aRemove */, fSilent); alock.acquire(); } if (FAILED(rc)) return rc; @@ -4386,6 +4850,58 @@ STDMETHODIMP Machine::SetAutoDiscardForDevice(IN_BSTR aControllerName, LONG aCon return S_OK; } +STDMETHODIMP Machine::SetHotPluggableForDevice(IN_BSTR aControllerName, LONG aControllerPort, + LONG aDevice, BOOL aHotPluggable) +{ + CheckComArgStrNotEmptyOrNull(aControllerName); + + LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aHotPluggable=%d\n", + aControllerName, aControllerPort, aDevice, aHotPluggable)); + + 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); + + /** @todo remove this blocker and add the missing code to support this + * flag properly in all code areas, with proper support checks below. */ + return setError(VBOX_E_NOT_SUPPORTED, + tr("Controller '%ls' does not support changing the hot-pluggable device flag"), + aControllerName); + + setModified(IsModified_Storage); + mMediaData.backup(); + + AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); + + if (pAttach->getType() == DeviceType_Floppy) + return setError(E_INVALIDARG, + tr("Setting the hot-pluggable device flag rejected as the device attached to device slot %d on port %d of controller '%ls' is a floppy drive"), + aDevice, aControllerPort, aControllerName); + pAttach->updateHotPluggable(!!aHotPluggable); + + return S_OK; +} + STDMETHODIMP Machine::SetNoBandwidthGroupForDevice(IN_BSTR aControllerName, LONG aControllerPort, LONG aDevice) { @@ -4833,6 +5349,18 @@ STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue) return S_OK; } +STDMETHODIMP Machine::SetSettingsFilePath(IN_BSTR aFilePath, IProgress **aProgress) +{ + CheckComArgStrNotEmptyOrNull(aFilePath); + CheckComArgOutPointerValid(aProgress); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aProgress = NULL; + ReturnComNotImplemented(); +} + STDMETHODIMP Machine::SaveSettings() { AutoCaller autoCaller(this); @@ -5024,7 +5552,7 @@ struct Machine::DeleteTask ComObjPtr<Progress> pProgress; }; -STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress) +STDMETHODIMP Machine::DeleteConfig(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress) { LogFlowFuncEnter(); @@ -5169,7 +5697,7 @@ HRESULT Machine::deleteTaskWorker(DeleteTask &task) if (FAILED(rc)) throw rc; rc = task.pProgress->WaitForAsyncProgressCompletion(pProgress2); if (FAILED(rc)) throw rc; - /* Check the result of the asynchrony process. */ + /* Check the result of the asynchronous process. */ LONG iRc; rc = pProgress2->COMGETTER(ResultCode)(&iRc); if (FAILED(rc)) throw rc; @@ -5177,6 +5705,15 @@ HRESULT Machine::deleteTaskWorker(DeleteTask &task) * retrieve the error info from there, or it'll be lost. */ if (FAILED(iRc)) throw setError(ProgressErrorInfo(pProgress2)); + + /* Close the medium, deliberately without checking the return + * code, and without leaving any trace in the error info, as + * a failure here is a very minor issue, which shouldn't happen + * as above we even managed to delete the medium. */ + { + ErrorInfoKeeper eik; + pMedium->Close(); + } } setMachineState(oldState); alock.acquire(); @@ -5292,7 +5829,7 @@ STDMETHODIMP Machine::FindSnapshot(IN_BSTR aNameOrId, ISnapshot **aSnapshot) else { Guid uuid(aNameOrId); - if (!uuid.isEmpty()) + if (uuid.isValid()) rc = findSnapshotById(uuid, pSnapshot, true /* aSetError */); else rc = findSnapshotByName(Utf8Str(aNameOrId), pSnapshot, true /* aSetError */); @@ -5441,21 +5978,17 @@ HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName, AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); Utf8Str strName(aName); - HWData::GuestPropertyList::const_iterator it; + HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.find(strName); - for (it = mHWData->mGuestProperties.begin(); - it != mHWData->mGuestProperties.end(); ++it) + if (it != mHWData->mGuestProperties.end()) { - if (it->strName == strName) - { - char szFlags[MAX_FLAGS_LEN + 1]; - it->strValue.cloneTo(aValue); - *aTimestamp = it->mTimestamp; - writeFlags(it->mFlags, szFlags); - Bstr(szFlags).cloneTo(aFlags); - break; - } + char szFlags[MAX_FLAGS_LEN + 1]; + it->second.strValue.cloneTo(aValue); + *aTimestamp = it->second.mTimestamp; + writeFlags(it->second.mFlags, szFlags); + Bstr(szFlags).cloneTo(aFlags); } + return S_OK; } @@ -5477,6 +6010,9 @@ HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName, /* fail if we were called after #OnSessionEnd() is called. This is a * silly race condition. */ + /** @todo This code is bothering API clients (like python script clients) with + * the AccessGuestProperty call, creating unncessary IPC. Need to + * have a way of figuring out which kind of direct session it is... */ if (!directControl) rc = E_ACCESSDENIED; else @@ -5536,9 +6072,6 @@ HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue, AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); HRESULT rc = S_OK; - HWData::GuestProperty property; - property.mFlags = NILFLAG; - bool found = false; rc = checkStateDependency(MutableStateDep); if (FAILED(rc)) return rc; @@ -5548,64 +6081,59 @@ HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue, Utf8Str utf8Name(aName); Utf8Str utf8Flags(aFlags); uint32_t fFlags = NILFLAG; - if ( (aFlags != NULL) - && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)) - ) + if ( aFlags != NULL + && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags))) return setError(E_INVALIDARG, - tr("Invalid flag values: '%ls'"), + tr("Invalid guest property flag values: '%ls'"), aFlags); - /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I - * know, this is simple and do an OK job atm.) */ - HWData::GuestPropertyList::iterator it; - for (it = mHWData->mGuestProperties.begin(); - it != mHWData->mGuestProperties.end(); ++it) - if (it->strName == utf8Name) - { - property = *it; - if (it->mFlags & (RDONLYHOST)) - rc = setError(E_ACCESSDENIED, - tr("The property '%ls' cannot be changed by the host"), - aName); - else - { - setModified(IsModified_MachineData); - mHWData.backup(); // @todo r=dj backup in a loop?!? - - /* The backup() operation invalidates our iterator, so - * get a new one. */ - for (it = mHWData->mGuestProperties.begin(); - it->strName != utf8Name; - ++it) - ; - mHWData->mGuestProperties.erase(it); - } - found = true; - break; - } - if (found && SUCCEEDED(rc)) + bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0'; + HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name); + if (it == mHWData->mGuestProperties.end()) { - if (aValue) + if (!fDelete) { + setModified(IsModified_MachineData); + mHWData.backupEx(); + RTTIMESPEC time; - property.strValue = aValue; - property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); - if (aFlags != NULL) - property.mFlags = fFlags; - mHWData->mGuestProperties.push_back(property); + HWData::GuestProperty prop; + prop.strValue = aValue; + prop.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); + prop.mFlags = fFlags; + mHWData->mGuestProperties[Utf8Str(aName)] = prop; } } - else if (SUCCEEDED(rc) && aValue) + else { - RTTIMESPEC time; - setModified(IsModified_MachineData); - mHWData.backup(); - property.strName = aName; - property.strValue = aValue; - property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); - property.mFlags = fFlags; - mHWData->mGuestProperties.push_back(property); + if (it->second.mFlags & (RDONLYHOST)) + { + rc = setError(E_ACCESSDENIED, + tr("The property '%ls' cannot be changed by the host"), + aName); + } + else + { + setModified(IsModified_MachineData); + mHWData.backupEx(); + + /* The backupEx() operation invalidates our iterator, + * so get a new one. */ + it = mHWData->mGuestProperties.find(utf8Name); + Assert(it != mHWData->mGuestProperties.end()); + + if (!fDelete) + { + RTTIMESPEC time; + it->second.strValue = aValue; + it->second.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); + it->second.mFlags = fFlags; + } + else + mHWData->mGuestProperties.erase(it); + } } + if ( SUCCEEDED(rc) && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty() || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(), @@ -5616,8 +6144,8 @@ HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue, ) ) { - /** @todo r=bird: Why aren't we leaving the lock here? The - * same code in PushGuestProperty does... */ + alock.release(); + mParent->onGuestPropertyChange(mData->mUuid, aName, aValue ? aValue : Bstr("").raw(), aFlags ? aFlags : Bstr("").raw()); @@ -5651,8 +6179,7 @@ HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue, if (!directControl) rc = E_ACCESSDENIED; else - /** @todo Fix when adding DeleteGuestProperty(), - see defect. */ + /** @todo Fix when adding DeleteGuestProperty(), see defect. */ rc = directControl->AccessGuestProperty(aName, aValue, aFlags, true /* isSetter */, &dummy, &dummy64, &dummy); @@ -5713,42 +6240,50 @@ HRESULT Machine::enumerateGuestPropertiesInService AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); Utf8Str strPatterns(aPatterns); + HWData::GuestPropertyMap propMap; + /* * Look for matching patterns and build up a list. */ - HWData::GuestPropertyList propList; - for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin(); - it != mHWData->mGuestProperties.end(); - ++it) + HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin(); + while (it != mHWData->mGuestProperties.end()) + { if ( strPatterns.isEmpty() || RTStrSimplePatternMultiMatch(strPatterns.c_str(), RTSTR_MAX, - it->strName.c_str(), + it->first.c_str(), RTSTR_MAX, NULL) ) - propList.push_back(*it); + { + propMap.insert(*it); + } + + it++; + } + + alock.release(); /* * And build up the arrays for returning the property information. */ - size_t cEntries = propList.size(); + size_t cEntries = propMap.size(); SafeArray<BSTR> names(cEntries); SafeArray<BSTR> values(cEntries); SafeArray<LONG64> timestamps(cEntries); SafeArray<BSTR> flags(cEntries); size_t iProp = 0; - for (HWData::GuestPropertyList::iterator it = propList.begin(); - it != propList.end(); - ++it) + + it = propMap.begin(); + while (it != propMap.end()) { char szFlags[MAX_FLAGS_LEN + 1]; - it->strName.cloneTo(&names[iProp]); - it->strValue.cloneTo(&values[iProp]); - timestamps[iProp] = it->mTimestamp; - writeFlags(it->mFlags, szFlags); - Bstr(szFlags).cloneTo(&flags[iProp]); - ++iProp; + it->first.cloneTo(&names[iProp]); + it->second.strValue.cloneTo(&values[iProp]); + timestamps[iProp] = it->second.mTimestamp; + writeFlags(it->second.mFlags, szFlags); + Bstr(szFlags).cloneTo(&flags[iProp++]); + it++; } names.detachTo(ComSafeArrayOutArg(aNames)); values.detachTo(ComSafeArrayOutArg(aValues)); @@ -6083,6 +6618,132 @@ STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName) return S_OK; } +STDMETHODIMP Machine::AddUSBController(IN_BSTR aName, USBControllerType_T aType, + IUSBController **controller) +{ + if ( (aType <= USBControllerType_Null) + || (aType >= USBControllerType_Last)) + return setError(E_INVALIDARG, + tr("Invalid USB controller type: %d"), + aType); + + 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; + + /* try to find one with the same type first. */ + ComObjPtr<USBController> ctrl; + + rc = getUSBControllerByName(aName, ctrl, false /* aSetError */); + if (SUCCEEDED(rc)) + return setError(VBOX_E_OBJECT_IN_USE, + tr("USB controller named '%ls' already exists"), + aName); + + /* Check that we don't exceed the maximum number of USB controllers for the given type. */ + ULONG maxInstances; + rc = mParent->getSystemProperties()->GetMaxInstancesOfUSBControllerType(mHWData->mChipsetType, aType, &maxInstances); + if (FAILED(rc)) + return rc; + + ULONG cInstances = getUSBControllerCountByType(aType); + if (cInstances >= maxInstances) + return setError(E_INVALIDARG, + tr("Too many USB controllers of this type")); + + ctrl.createObject(); + + rc = ctrl->init(this, aName, aType); + if (FAILED(rc)) return rc; + + setModified(IsModified_USB); + mUSBControllers.backup(); + mUSBControllers->push_back(ctrl); + + ctrl.queryInterfaceTo(controller); + + /* inform the direct session if any */ + alock.release(); + onUSBControllerChange(); + + return S_OK; +} + +STDMETHODIMP Machine::GetUSBControllerByName(IN_BSTR aName, IUSBController **aUSBController) +{ + CheckComArgStrNotEmptyOrNull(aName); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + ComObjPtr<USBController> ctrl; + + HRESULT rc = getUSBControllerByName(aName, ctrl, true /* aSetError */); + if (SUCCEEDED(rc)) + ctrl.queryInterfaceTo(aUSBController); + + return rc; +} + +STDMETHODIMP Machine::GetUSBControllerCountByType(USBControllerType_T aType, + ULONG *aControllers) +{ + CheckComArgOutPointerValid(aControllers); + + if ( (aType <= USBControllerType_Null) + || (aType >= USBControllerType_Last)) + return setError(E_INVALIDARG, + tr("Invalid USB controller type: %d"), + aType); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + ComObjPtr<USBController> ctrl; + + *aControllers = getUSBControllerCountByType(aType); + + return S_OK; +} + +STDMETHODIMP Machine::RemoveUSBController(IN_BSTR aName) +{ + CheckComArgStrNotEmptyOrNull(aName); + + 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; + + ComObjPtr<USBController> ctrl; + rc = getUSBControllerByName(aName, ctrl, true /* aSetError */); + if (FAILED(rc)) return rc; + + setModified(IsModified_USB); + mUSBControllers.backup(); + + ctrl->unshare(); + + mUSBControllers->remove(ctrl); + + /* inform the direct session if any */ + alock.release(); + onUSBControllerChange(); + + return S_OK; +} + STDMETHODIMP Machine::QuerySavedGuestScreenInfo(ULONG uScreenId, ULONG *puOriginX, ULONG *puOriginY, @@ -6397,7 +7058,7 @@ STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu) mHWData.backup(); mHWData->mCPUAttached[aCpu] = true; - /* Save settings if online */ + /** Save settings if online - @todo why is this required? -- @bugref{6818} */ if (Global::IsOnline(mData->mMachineState)) saveSettings(NULL); @@ -6438,7 +7099,7 @@ STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu) mHWData.backup(); mHWData->mCPUAttached[aCpu] = false; - /* Save settings if online */ + /** Save settings if online - @todo why is this required? -- @bugref{6818} */ if (Global::IsOnline(mData->mMachineState)) saveSettings(NULL); @@ -6934,6 +7595,92 @@ STDMETHODIMP Machine::COMSETTER(AutostopType)(AutostopType_T enmAutostopType) return hrc; } +STDMETHODIMP Machine::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend) +{ + CheckComArgOutPointerValid(aDefaultFrontend); + AutoCaller autoCaller(this); + HRESULT hrc = autoCaller.rc(); + if (SUCCEEDED(hrc)) + { + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + mHWData->mDefaultFrontend.cloneTo(aDefaultFrontend); + } + return hrc; +} + +STDMETHODIMP Machine::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend) +{ + CheckComArgStr(aDefaultFrontend); + AutoCaller autoCaller(this); + HRESULT hrc = autoCaller.rc(); + if (SUCCEEDED(hrc)) + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + hrc = checkStateDependency(MutableOrSavedStateDep); + if (SUCCEEDED(hrc)) + { + hrc = mHWData.backupEx(); + if (SUCCEEDED(hrc)) + { + setModified(IsModified_MachineData); + mHWData->mDefaultFrontend = aDefaultFrontend; + } + } + } + return hrc; +} + +STDMETHODIMP Machine::COMGETTER(Icon)(ComSafeArrayOut(BYTE, aIcon)) +{ + CheckComArgSafeArrayNotNull(aIcon); + CheckComArgOutSafeArrayPointerValid(aIcon); + AutoCaller autoCaller(this); + HRESULT hrc = autoCaller.rc(); + if (SUCCEEDED(hrc)) + { + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + com::SafeArray<BYTE> icon(mUserData->mIcon.size()); + memcpy(icon.raw(), &mUserData->mIcon[0], mUserData->mIcon.size()); + icon.detachTo(ComSafeArrayOutArg(aIcon)); + } + return hrc; +} + +STDMETHODIMP Machine::COMSETTER(Icon)(ComSafeArrayIn(BYTE, aIcon)) +{ + CheckComArgSafeArrayNotNull(aIcon); + AutoCaller autoCaller(this); + HRESULT hrc = autoCaller.rc(); + if (SUCCEEDED(hrc)) + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + hrc = checkStateDependency(MutableOrSavedStateDep); + if (SUCCEEDED(hrc)) + { + setModified(IsModified_MachineData); + mUserData.backup(); + com::SafeArray<BYTE> icon(ComSafeArrayInArg(aIcon)); + mUserData->mIcon.resize(icon.size()); + memcpy(&mUserData->mIcon[0], icon.raw(), mUserData->mIcon.size()); + } + } + return hrc; +} + +STDMETHODIMP Machine::COMGETTER(USBProxyAvailable)(BOOL *aAvailable) +{ + CheckComArgOutPointerValid(aAvailable); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + +#ifdef VBOX_WITH_USB + *aAvailable = true; +#else + *aAvailable = false; +#endif + return S_OK; +} STDMETHODIMP Machine::CloneTo(IMachine *pTarget, CloneMode_T mode, ComSafeArrayIn(CloneOptions_T, options), IProgress **pProgress) { @@ -7180,11 +7927,39 @@ void Machine::composeSavedStateFilename(Utf8Str &strStateFilePath) } /** + * Returns the full path to the default video capture file. + */ +void Machine::getDefaultVideoCaptureFile(Utf8Str &strFile) +{ + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + strFile = mData->m_strConfigFileFull; // path/to/machinesfolder/vmname/vmname.vbox + strFile.stripExt(); // path/to/machinesfolder/vmname/vmname + strFile.append(".webm"); // path/to/machinesfolder/vmname/vmname.webm +} + +/** + * Returns whether at least one USB controller is present for the VM. + */ +bool Machine::isUSBControllerPresent() +{ + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), false); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + return (mUSBControllers->size() > 0); +} + +/** * @note Locks this object for writing, calls the client process * (inside the lock). */ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, - const Utf8Str &strType, + const Utf8Str &strFrontend, const Utf8Str &strEnvironment, ProgressProxy *aProgress) { @@ -7192,6 +7967,7 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, AssertReturn(aControl, E_FAIL); AssertReturn(aProgress, E_FAIL); + AssertReturn(!strFrontend.isEmpty(), E_FAIL); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -7222,7 +7998,7 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, szPath[sz++] = RTPATH_DELIMITER; szPath[sz] = 0; char *cmd = szPath + sz; - sz = RTPATH_MAX - sz; + sz = sizeof(szPath) - sz; int vrc = VINF_SUCCESS; RTPROCESS pid = NIL_RTPROCESS; @@ -7278,16 +8054,44 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, RTStrFree(newEnvStr); } - /* Qt is default */ #ifdef VBOX_WITH_QTGUI - if (strType == "gui" || strType == "GUI/Qt") + if (strFrontend == "gui" || strFrontend == "GUI/Qt") { # ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */ - const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM"; + /* Modify the base path so that we don't need to use ".." below. */ + RTPathStripTrailingSlash(szPath); + RTPathStripFilename(szPath); + sz = strlen(szPath); + cmd = szPath + sz; + sz = sizeof(szPath) - sz; + +#define OSX_APP_NAME "VirtualBoxVM" +#define OSX_APP_PATH_FMT "/Resources/%s.app/Contents/MacOS/VirtualBoxVM" + + Utf8Str strAppOverride = getExtraData(Utf8Str("VBoxInternal2/VirtualBoxVMAppOverride")); + if ( strAppOverride.contains(".") + || strAppOverride.contains("/") + || strAppOverride.contains("\\") + || strAppOverride.contains(":")) + strAppOverride.setNull(); + Utf8Str strAppPath; + if (!strAppOverride.isEmpty()) + { + strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, strAppOverride.c_str()); + Utf8Str strFullPath(szPath); + strFullPath.append(strAppPath); + /* there is a race, but people using this deserve the failure */ + if (!RTFileExists(strFullPath.c_str())) + strAppOverride.setNull(); + } + if (strAppOverride.isEmpty()) + strAppPath = Utf8StrFmt(OSX_APP_PATH_FMT, OSX_APP_NAME); + const char *VirtualBox_exe = strAppPath.c_str(); + AssertReturn(sz >= strlen(VirtualBox_exe), E_UNEXPECTED); # else const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE; -# endif Assert(sz >= sizeof(VirtualBox_exe)); +# endif strcpy(cmd, VirtualBox_exe); Utf8Str idStr = mData->mUuid.toString(); @@ -7302,7 +8106,7 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, else #ifdef VBOX_WITH_VBOXSDL - if (strType == "sdl" || strType == "GUI/SDL") + if (strFrontend == "sdl" || strFrontend == "GUI/SDL") { const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE; Assert(sz >= sizeof(VBoxSDL_exe)); @@ -7320,9 +8124,9 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, else #ifdef VBOX_WITH_HEADLESS - if ( strType == "headless" - || strType == "capture" - || strType == "vrdp" /* Deprecated. Same as headless. */ + if ( strFrontend == "headless" + || strFrontend == "capture" + || strFrontend == "vrdp" /* Deprecated. Same as headless. */ ) { /* On pre-4.0 the "headless" type was used for passing "--vrdp off" to VBoxHeadless to let it work in OSE, @@ -7343,7 +8147,7 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, "--vrde", "config", 0, /* For "--capture". */ 0 }; - if (strType == "capture") + if (strFrontend == "capture") { unsigned pos = RT_ELEMENTS(args) - 2; args[pos] = "--capture"; @@ -7364,8 +8168,8 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, { RTEnvDestroy(env); return setError(E_INVALIDARG, - tr("Invalid session type: '%s'"), - strType.c_str()); + tr("Invalid frontend name: '%s'"), + strFrontend.c_str()); } RTEnvDestroy(env); @@ -7382,22 +8186,28 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, * because it doesn't need to call us back if called with a NULL argument. * Releasing the lock here is dangerous because we didn't prepare the * launch data yet, but the client we've just started may happen to be - * too fast and call openSession() that will fail (because of PID, etc.), + * too fast and call LockMachine() that will fail (because of PID, etc.), * so that the Machine will never get out of the Spawning session state. */ /* inform the session that it will be a remote one */ LogFlowThisFunc(("Calling AssignMachine (NULL)...\n")); - HRESULT rc = aControl->AssignMachine(NULL, LockType_Write); +#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER + HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, Bstr::Empty.raw()); +#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */ + HRESULT rc = aControl->AssignMachine(NULL, LockType_Write, NULL); +#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */ LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc)); if (FAILED(rc)) { /* restore the session state */ mData->mSession.mState = SessionState_Unlocked; + alock.release(); + mParent->addProcessToReap(pid); /* The failure may occur w/o any error info (from RPC), so provide one */ return setError(VBOX_E_VM_ERROR, - tr("Failed to assign the machine to the session (%Rrc)"), rc); + tr("Failed to assign the machine to the session (%Rhrc)"), rc); } /* attach launch data to the machine */ @@ -7406,40 +8216,32 @@ HRESULT Machine::launchVMProcess(IInternalSessionControl *aControl, mData->mSession.mProgress = aProgress; mData->mSession.mPID = pid; mData->mSession.mState = SessionState_Spawning; - mData->mSession.mType = strType; + mData->mSession.mType = strFrontend; + + alock.release(); + mParent->addProcessToReap(pid); LogFlowThisFuncLeave(); return S_OK; } /** - * Returns @c true if the given machine has an open direct session and returns - * the session machine instance and additional session data (on some platforms) - * if so. + * Returns @c true if the given session machine instance has an open direct + * session (and optionally also for direct sessions which are closing) and + * returns the session control machine instance if so. * * Note that when the method returns @c false, the arguments remain unchanged. * - * @param aMachine Session machine object. - * @param aControl Direct session control object (optional). - * @param aIPCSem Mutex IPC semaphore handle for this machine (optional). + * @param aMachine Session machine object. + * @param aControl Direct session control object (optional). + * @param aAllowClosing If true then additionally a session which is currently + * being closed will also be allowed. * * @note locks this object for reading. */ -#if defined(RT_OS_WINDOWS) -bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, - ComPtr<IInternalSessionControl> *aControl /*= NULL*/, - HANDLE *aIPCSem /*= NULL*/, - bool aAllowClosing /*= false*/) -#elif defined(RT_OS_OS2) -bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, - ComPtr<IInternalSessionControl> *aControl /*= NULL*/, - HMTX *aIPCSem /*= NULL*/, - bool aAllowClosing /*= false*/) -#else bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, ComPtr<IInternalSessionControl> *aControl /*= NULL*/, bool aAllowClosing /*= false*/) -#endif { AutoLimitedCaller autoCaller(this); AssertComRCReturn(autoCaller.rc(), false); @@ -7461,11 +8263,6 @@ bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, if (aControl != NULL) *aControl = mData->mSession.mDirectControl; -#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) - /* Additional session data */ - if (aIPCSem != NULL) - *aIPCSem = aMachine->mIPCSem; -#endif return true; } @@ -7473,20 +8270,11 @@ bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, } /** - * Returns @c true if the given machine has an spawning direct session and - * returns and additional session data (on some platforms) if so. - * - * Note that when the method returns @c false, the arguments remain unchanged. - * - * @param aPID PID of the spawned direct session process. + * Returns @c true if the given machine has an spawning direct session. * * @note locks this object for reading. */ -#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) -bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/) -#else bool Machine::isSessionSpawning() -#endif { AutoLimitedCaller autoCaller(this); AssertComRCReturn(autoCaller.rc(), false); @@ -7498,17 +8286,7 @@ bool Machine::isSessionSpawning() AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); if (mData->mSession.mState == SessionState_Spawning) - { -#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) - /* Additional session data */ - if (aPID != NULL) - { - AssertReturn(mData->mSession.mPID != NIL_RTPROCESS, false); - *aPID = mData->mSession.mPID; - } -#endif return true; - } return false; } @@ -7537,8 +8315,7 @@ bool Machine::checkForSpawnFailure() return true; } - /* VirtualBox::addProcessToReap() needs a write lock */ - AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS); + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); if (mData->mSession.mState != SessionState_Spawning) { @@ -7549,22 +8326,12 @@ bool Machine::checkForSpawnFailure() HRESULT rc = S_OK; -#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) - - /* the process was already unexpectedly terminated, we just need to set an - * error and finalize session spawning */ - rc = setError(E_FAIL, - tr("The virtual machine '%s' has terminated unexpectedly during startup"), - getName().c_str()); -#else - /* PID not yet initialized, skip check. */ if (mData->mSession.mPID == NIL_RTPROCESS) return false; RTPROCSTATUS status; - int vrc = ::RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, - &status); + int vrc = RTProcWait(mData->mSession.mPID, RTPROCWAIT_FLAGS_NOBLOCK, &status); if (vrc != VERR_PROCESS_RUNNING) { @@ -7583,16 +8350,14 @@ bool Machine::checkForSpawnFailure() else rc = setError(E_FAIL, tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"), - getName().c_str(), rc); + getName().c_str(), vrc); } -#endif - if (FAILED(rc)) { /* Close the remote session, remove the remote control from the list * and reset session state to Closed (@note keep the code in sync with - * the relevant part in checkForSpawnFailure()). */ + * the relevant part in LockMachine()). */ Assert(mData->mSession.mRemoteControls.size() == 1); if (mData->mSession.mRemoteControls.size() == 1) @@ -7611,7 +8376,6 @@ bool Machine::checkForSpawnFailure() mData->mSession.mProgress.setNull(); } - mParent->addProcessToReap(mData->mSession.mPID); mData->mSession.mPID = NIL_RTPROCESS; mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked); @@ -7771,6 +8535,21 @@ void Machine::releaseStateDependency() } } +Utf8Str Machine::getExtraData(const Utf8Str &strKey) +{ + /* start with nothing found */ + Utf8Str strResult(""); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey); + if (it != mData->pMachineConfigFile->mapExtraDataItems.end()) + // found: + strResult = it->second; // source is a Utf8Str + + return strResult; +} + // protected methods ///////////////////////////////////////////////////////////////////////////// @@ -7894,6 +8673,7 @@ HRESULT Machine::initDataAndChildObjects() mHWData.allocate(); mMediaData.allocate(); mStorageControllers.allocate(); + mUSBControllers.allocate(); /* initialize mOSTypeId */ mUserData->s.strOsType = mParent->getUnknownOSType()->id(); @@ -7924,9 +8704,9 @@ HRESULT Machine::initDataAndChildObjects() unconst(mAudioAdapter).createObject(); mAudioAdapter->init(this); - /* create the USB controller object (always present, default is disabled) */ - unconst(mUSBController).createObject(); - mUSBController->init(this); + /* create the USB device filters object (always present) */ + unconst(mUSBDeviceFilters).createObject(); + mUSBDeviceFilters->init(this); /* create associated network adapter objects */ mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType)); @@ -7975,10 +8755,10 @@ void Machine::uninitDataAndChildObjects() } } - if (mUSBController) + if (mUSBDeviceFilters) { - mUSBController->uninit(); - unconst(mUSBController).setNull(); + mUSBDeviceFilters->uninit(); + unconst(mUSBDeviceFilters).setNull(); } if (mAudioAdapter) @@ -8017,13 +8797,13 @@ void Machine::uninitDataAndChildObjects() unconst(mBIOSSettings).setNull(); } - /* Deassociate hard disks (only when a real Machine or a SnapshotMachine + /* Deassociate media (only when a real Machine or a SnapshotMachine * instance is uninitialized; SessionMachine instances refer to real - * Machine hard disks). This is necessary for a clean re-initialization of + * Machine media). This is necessary for a clean re-initialization of * the VM after successfully re-checking the accessibility state. Note * that in case of normal Machine or SnapshotMachine uninitialization (as - * a result of unregistering or deleting the snapshot), outdated hard - * disk attachments will already be uninitialized and deleted, so this + * a result of unregistering or deleting the snapshot), outdated media + * attachments will already be uninitialized and deleted, so this * code will not affect them. */ if ( !!mMediaData && (!isSessionMachine()) @@ -8033,10 +8813,10 @@ void Machine::uninitDataAndChildObjects() it != mMediaData->mAttachments.end(); ++it) { - ComObjPtr<Medium> hd = (*it)->getMedium(); - if (hd.isNull()) + ComObjPtr<Medium> pMedium = (*it)->getMedium(); + if (pMedium.isNull()) continue; - HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId()); + HRESULT rc = pMedium->removeBackReference(mData->mUuid, getSnapshotId()); AssertComRC(rc); } } @@ -8061,6 +8841,7 @@ void Machine::uninitDataAndChildObjects() * since it may be still in use) */ mMediaData.free(); mStorageControllers.free(); + mUSBControllers.free(); mHWData.free(); mUserData.free(); mSSData.free(); @@ -8228,9 +9009,28 @@ HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile & // copy name, description, OS type, teleporter, UTC etc. mUserData->s = config.machineUserData; + // Decode the Icon overide data from config userdata and set onto Machine. + #define DECODE_STR_MAX _1M + const char* pszStr = config.machineUserData.ovIcon.c_str(); + ssize_t cbOut = RTBase64DecodedSize(pszStr, NULL); + if (cbOut > DECODE_STR_MAX) + return setError(E_FAIL, + tr("Icon Data too long.'%d' > '%d'"), + cbOut, + DECODE_STR_MAX); + com::SafeArray<BYTE> iconByte(cbOut); + HRESULT rc = RTBase64Decode(pszStr, iconByte.raw(), cbOut, NULL, NULL); + if (FAILED(rc)) + return setError(E_FAIL, + tr("Failure to Decode Icon Data. '%s' (%d)"), + pszStr, + rc); + mUserData->mIcon.resize(iconByte.size()); + memcpy(&mUserData->mIcon[0], iconByte.raw(), mUserData->mIcon.size()); + // look up the object by Id to check it is valid ComPtr<IGuestOSType> guestOSType; - HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), + rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType).raw(), guestOSType.asOutParam()); if (FAILED(rc)) return rc; @@ -8435,14 +9235,15 @@ HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::De mHWData->mHardwareUUID = data.uuid; mHWData->mHWVirtExEnabled = data.fHardwareVirt; - mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive; mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging; mHWData->mHWVirtExLargePagesEnabled = data.fLargePages; mHWData->mHWVirtExVPIDEnabled = data.fVPID; + mHWData->mHWVirtExUXEnabled = data.fUnrestrictedExecution; mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce; mHWData->mPAEEnabled = data.fPAE; mHWData->mSyntheticCpu = data.fSyntheticCpu; - + mHWData->mLongMode = data.enmLongMode; + mHWData->mTripleFaultReset = data.fTripleFaultReset; mHWData->mCPUCount = data.cCPUs; mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug; mHWData->mCpuExecutionCap = data.ulCpuExecutionCap; @@ -8518,6 +9319,7 @@ HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::De mHWData->mBootOrder[i] = it->second; } + mHWData->mGraphicsControllerType = data.graphicsControllerType; mHWData->mVRAMSize = data.ulVRAMSizeMB; mHWData->mMonitorCount = data.cMonitors; mHWData->mAccelerate3DEnabled = data.fAccelerate3D; @@ -8525,7 +9327,15 @@ HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::De mHWData->mVideoCaptureWidth = data.ulVideoCaptureHorzRes; mHWData->mVideoCaptureHeight = data.ulVideoCaptureVertRes; mHWData->mVideoCaptureEnabled = data.fVideoCaptureEnabled; - mHWData->mVideoCaptureFile = data.strVideoCaptureFile; + for (unsigned i = 0; i < RT_ELEMENTS(mHWData->maVideoCaptureScreens); i++) + mHWData->maVideoCaptureScreens[i] = ASMBitTest(&data.u64VideoCaptureScreens, i); + AssertCompile(RT_ELEMENTS(mHWData->maVideoCaptureScreens) == sizeof(data.u64VideoCaptureScreens) * 8); + mHWData->mVideoCaptureRate = data.ulVideoCaptureRate; + mHWData->mVideoCaptureFPS = data.ulVideoCaptureFPS; + if (!data.strVideoCaptureFile.isEmpty()) + calculateFullPath(data.strVideoCaptureFile, mHWData->mVideoCaptureFile); + else + mHWData->mVideoCaptureFile.setNull(); mHWData->mFirmwareType = data.firmwareType; mHWData->mPointingHIDType = data.pointingHIDType; mHWData->mKeyboardHIDType = data.keyboardHIDType; @@ -8545,8 +9355,21 @@ HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::De rc = mBandwidthControl->loadSettings(data.ioSettings); if (FAILED(rc)) return rc; - /* USB Controller */ - rc = mUSBController->loadSettings(data.usbController); + /* Shared folders */ + for (settings::USBControllerList::const_iterator it = data.usbSettings.llUSBControllers.begin(); + it != data.usbSettings.llUSBControllers.end(); + ++it) + { + const settings::USBController &settingsCtrl = *it; + ComObjPtr<USBController> newCtrl; + + newCtrl.createObject(); + newCtrl->init(this, settingsCtrl.strName, settingsCtrl.enmType); + mUSBControllers->push_back(newCtrl); + } + + /* USB device filters */ + rc = mUSBDeviceFilters->loadSettings(data.usbSettings); if (FAILED(rc)) return rc; // network adapters @@ -8671,8 +9494,8 @@ HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::De const settings::GuestProperty &prop = *it; uint32_t fFlags = guestProp::NILFLAG; guestProp::validateFlags(prop.strFlags.c_str(), &fFlags); - HWData::GuestProperty property = { prop.strName, prop.strValue, (LONG64) prop.timestamp, fFlags }; - mHWData->mGuestProperties.push_back(property); + HWData::GuestProperty property = { prop.strValue, (LONG64) prop.timestamp, fFlags }; + mHWData->mGuestProperties[prop.strName] = property; } mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns; @@ -8683,6 +9506,9 @@ HRESULT Machine::loadHardware(const settings::Hardware &data, const settings::De return rc; mHWData->mAutostart = *pAutostart; + + /* default frontend */ + mHWData->mDefaultFrontend = data.strDefaultFrontend; } catch(std::bad_alloc &) { @@ -8973,6 +9799,8 @@ HRESULT Machine::loadStorageDevices(StorageController *aStorageController, dev.fTempEject, dev.fNonRotational, dev.fDiscard, + /// @todo load setting once the hot-pluggable flag works + false /*dev.fHotPluggable*/, pBwGroup.isNull() ? Utf8Str::Empty : pBwGroup->getName()); if (FAILED(rc)) break; @@ -9028,7 +9856,7 @@ HRESULT Machine::findSnapshotById(const Guid &aId, return E_FAIL; } - if (aId.isEmpty()) + if (aId.isZero()) aSnapshot = mData->mFirstSnapshot; else aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId.ref()); @@ -9112,6 +9940,57 @@ HRESULT Machine::getStorageControllerByName(const Utf8Str &aName, return VBOX_E_OBJECT_NOT_FOUND; } +/** + * Returns a USB controller object with the given name. + * + * @param aName USB controller name to find + * @param aUSBController where to return the found USB controller + * @param aSetError true to set extended error info on failure + */ +HRESULT Machine::getUSBControllerByName(const Utf8Str &aName, + ComObjPtr<USBController> &aUSBController, + bool aSetError /* = false */) +{ + AssertReturn(!aName.isEmpty(), E_INVALIDARG); + + for (USBControllerList::const_iterator it = mUSBControllers->begin(); + it != mUSBControllers->end(); + ++it) + { + if ((*it)->getName() == aName) + { + aUSBController = (*it); + return S_OK; + } + } + + if (aSetError) + return setError(VBOX_E_OBJECT_NOT_FOUND, + tr("Could not find a storage controller named '%s'"), + aName.c_str()); + return VBOX_E_OBJECT_NOT_FOUND; +} + +/** + * Returns the number of USB controller instance of the given type. + * + * @param enmType USB controller type. + */ +ULONG Machine::getUSBControllerCountByType(USBControllerType_T enmType) +{ + ULONG cCtrls = 0; + + for (USBControllerList::const_iterator it = mUSBControllers->begin(); + it != mUSBControllers->end(); + ++it) + { + if ((*it)->getControllerType() == enmType) + cCtrls++; + } + + return cCtrls; +} + HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName, MediaData::AttachmentList &atts) { @@ -9534,6 +10413,26 @@ void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config) // copy name, description, OS type, teleport, UTC etc. config.machineUserData = mUserData->s; + // Encode the Icon Override data from Machine and store on config userdata. + com::SafeArray<BYTE> iconByte; + COMGETTER(Icon)(ComSafeArrayAsOutParam(iconByte)); + ssize_t cbData = iconByte.size(); + if (cbData > 0) + { + ssize_t cchOut = RTBase64EncodedLength(cbData); + Utf8Str strIconData; + strIconData.reserve(cchOut+1); + int vrc = RTBase64Encode(iconByte.raw(), cbData, + strIconData.mutableRaw(), strIconData.capacity(), + NULL); + if (RT_FAILURE(vrc)) + throw setError(E_FAIL, tr("Failure to Encode Icon Data. '%s' (%Rrc)"), strIconData.mutableRaw(), vrc); + strIconData.jolt(); + config.machineUserData.ovIcon = strIconData; + } + else + config.machineUserData.ovIcon.setNull(); + if ( mData->mMachineState == MachineState_Saved || mData->mMachineState == MachineState_Restoring // when deleting a snapshot we may or may not have a saved state in the current state, @@ -9662,13 +10561,15 @@ HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDb // CPU data.fHardwareVirt = !!mHWData->mHWVirtExEnabled; - data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive; data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled; data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled; data.fVPID = !!mHWData->mHWVirtExVPIDEnabled; + data.fUnrestrictedExecution = !!mHWData->mHWVirtExUXEnabled; data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled; data.fPAE = !!mHWData->mPAEEnabled; + data.enmLongMode = mHWData->mLongMode; data.fSyntheticCpu = !!mHWData->mSyntheticCpu; + data.fTripleFaultReset = !!mHWData->mTripleFaultReset; /* Standard and Extended CPUID leafs. */ data.llCpuIdLeafs.clear(); @@ -9728,14 +10629,25 @@ HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDb data.mapBootOrder[i] = mHWData->mBootOrder[i]; // display + data.graphicsControllerType = mHWData->mGraphicsControllerType; data.ulVRAMSizeMB = mHWData->mVRAMSize; data.cMonitors = mHWData->mMonitorCount; data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled; data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled; data.ulVideoCaptureHorzRes = mHWData->mVideoCaptureWidth; data.ulVideoCaptureVertRes = mHWData->mVideoCaptureHeight; - data.fVideoCaptureEnabled = !! mHWData->mVideoCaptureEnabled; - data.strVideoCaptureFile = mHWData->mVideoCaptureFile; + data.ulVideoCaptureRate = mHWData->mVideoCaptureRate; + data.ulVideoCaptureFPS = mHWData->mVideoCaptureFPS; + data.fVideoCaptureEnabled = !!mHWData->mVideoCaptureEnabled; + for (unsigned i = 0; i < sizeof(data.u64VideoCaptureScreens) * 8; i++) + { + if (mHWData->maVideoCaptureScreens[i]) + ASMBitSet(&data.u64VideoCaptureScreens, i); + else + ASMBitClear(&data.u64VideoCaptureScreens, i); + } + /* store relative video capture file if possible */ + copyPathRelativeToMachine(mHWData->mVideoCaptureFile, data.strVideoCaptureFile); /* VRDEServer settings (optional) */ rc = mVRDEServer->saveSettings(data.vrdeSettings); @@ -9746,7 +10658,21 @@ HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDb if (FAILED(rc)) throw rc; /* USB Controller (required) */ - rc = mUSBController->saveSettings(data.usbController); + for (USBControllerList::const_iterator it = mUSBControllers->begin(); + it != mUSBControllers->end(); + ++it) + { + ComObjPtr<USBController> ctrl = *it; + settings::USBController settingsCtrl; + + settingsCtrl.strName = ctrl->getName(); + settingsCtrl.enmType = ctrl->getControllerType(); + + data.usbSettings.llUSBControllers.push_back(settingsCtrl); + } + + /* USB device filters (required) */ + rc = mUSBDeviceFilters->saveSettings(data.usbSettings); if (FAILED(rc)) throw rc; /* Network adapters (required) */ @@ -9854,11 +10780,11 @@ HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDb // guest properties data.llGuestProperties.clear(); #ifdef VBOX_WITH_GUEST_PROPS - for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin(); + for (HWData::GuestPropertyMap::const_iterator it = mHWData->mGuestProperties.begin(); it != mHWData->mGuestProperties.end(); ++it) { - HWData::GuestProperty property = *it; + HWData::GuestProperty property = it->second; /* Remove transient guest properties at shutdown unless we * are saving state */ @@ -9869,7 +10795,7 @@ HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDb || property.mFlags & guestProp::TRANSRESET)) continue; settings::GuestProperty prop; - prop.strName = property.strName; + prop.strName = it->first; prop.strValue = property.strValue; prop.timestamp = property.mTimestamp; char szFlags[guestProp::MAX_FLAGS_LEN + 1]; @@ -9886,6 +10812,8 @@ HRESULT Machine::saveHardware(settings::Hardware &data, settings::Debugging *pDb *pDbg = mHWData->mDebugging; *pAutostart = mHWData->mAutostart; + + data.strDefaultFrontend = mHWData->mDefaultFrontend; } catch(std::bad_alloc &) { @@ -9976,14 +10904,17 @@ HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageControl dev.deviceType = pAttach->getType(); dev.lPort = pAttach->getPort(); dev.lDevice = pAttach->getDevice(); + dev.fPassThrough = pAttach->getPassthrough(); + /// @todo save setting once the hot-pluggable flag works + dev.fHotPluggable = false /* pAttach->getHotPluggable()*/; if (pMedium) { if (pMedium->isHostDrive()) dev.strHostDriveSrc = pMedium->getLocationFull(); else dev.uuid = pMedium->getId(); - dev.fPassThrough = pAttach->getPassthrough(); dev.fTempEject = pAttach->getTempEject(); + dev.fNonRotational = pAttach->getNonRotational(); dev.fDiscard = pAttach->getDiscard(); } @@ -10128,7 +11059,7 @@ void Machine::addMediumToRegistry(ComObjPtr<Medium> &pMedium) * @note The progress object is not marked as completed, neither on success nor * on failure. This is a responsibility of the caller. * - * @note Locks this object for writing. + * @note Locks this object and the media tree for writing. */ HRESULT Machine::createImplicitDiffs(IProgress *aProgress, ULONG aWeight, @@ -10151,6 +11082,7 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress, HRESULT rc = S_OK; + // use appropriate locked media map (online or offline) MediumLockListMap lockedMediaOffline; MediumLockListMap *lockedMediaMap; if (aOnline) @@ -10282,6 +11214,8 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress, if (aOnline) { alock.release(); + /* The currently attached medium will be read-only, change + * the lock type to read. */ rc = pMediumLockList->Update(pMedium, false); alock.acquire(); AssertComRCThrowRC(rc); @@ -10296,16 +11230,7 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress, alock.acquire(); if (FAILED(rc)) throw rc; - rc = lockedMediaMap->Unlock(); - AssertComRCThrowRC(rc); - alock.release(); - rc = pMediumLockList->Append(diff, true); - alock.acquire(); - AssertComRCThrowRC(rc); - alock.release(); - rc = lockedMediaMap->Lock(); - alock.acquire(); - AssertComRCThrowRC(rc); + /* actual lock list update is done in Medium::commitMedia */ rc = diff->addBackReference(mData->mUuid); AssertComRCThrowRC(rc); @@ -10324,6 +11249,7 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress, false /* aTempEject */, pAtt->getNonRotational(), pAtt->getDiscard(), + pAtt->getHotPluggable(), pAtt->getBandwidthGroup()); if (FAILED(rc)) throw rc; @@ -10334,7 +11260,7 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress, } catch (HRESULT aRC) { rc = aRC; } - /* unlock all hard disks we locked */ + /* unlock all hard disks we locked when there is no VM */ if (!aOnline) { ErrorInfoKeeper eik; @@ -10343,14 +11269,6 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress, AssertComRC(rc1); } - if (FAILED(rc)) - { - MultiResult mrc = rc; - - alock.release(); - mrc = deleteImplicitDiffs(); - } - return rc; } @@ -10361,99 +11279,225 @@ HRESULT Machine::createImplicitDiffs(IProgress *aProgress, * Note that to delete hard disks created by #AttachDevice() this method is * called from #fixupMedia() when the changes are rolled back. * - * @note Locks this object for writing. + * @note Locks this object and the media tree for writing. */ -HRESULT Machine::deleteImplicitDiffs() +HRESULT Machine::deleteImplicitDiffs(bool aOnline) { + LogFlowThisFunc(("aOnline=%d\n", aOnline)); + AutoCaller autoCaller(this); AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - LogFlowThisFuncEnter(); + AutoMultiWriteLock2 alock(this->lockHandle(), + &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); + /* We absolutely must have backed up state. */ AssertReturn(mMediaData.isBackedUp(), E_FAIL); - HRESULT rc = S_OK; - - MediaData::AttachmentList implicitAtts; - - const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; - - /* enumerate new attachments */ + /* Check if there are any implicitly created diff images. */ + bool fImplicitDiffs = false; for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); it != mMediaData->mAttachments.end(); ++it) { - ComObjPtr<Medium> hd = (*it)->getMedium(); - if (hd.isNull()) - continue; - - if ((*it)->isImplicit()) - { - /* deassociate and mark for deletion */ - LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName())); - rc = hd->removeBackReference(mData->mUuid); - AssertComRC(rc); - implicitAtts.push_back(*it); - continue; - } - - /* was this hard disk attached before? */ - if (!findAttachment(oldAtts, hd)) + const ComObjPtr<MediumAttachment> &pAtt = *it; + if (pAtt->isImplicit()) { - /* no: de-associate */ - LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName())); - rc = hd->removeBackReference(mData->mUuid); - AssertComRC(rc); - continue; + fImplicitDiffs = true; + break; } - LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName())); } + /* If there is nothing to do, leave early. This saves lots of image locking + * effort. It also avoids a MachineStateChanged event without real reason. + * This is important e.g. when loading a VM config, because there should be + * no events. Otherwise API clients can become thoroughly confused for + * inaccessible VMs (the code for loading VM configs uses this method for + * cleanup if the config makes no sense), as they take such events as an + * indication that the VM is alive, and they would force the VM config to + * be reread, leading to an endless loop. */ + if (!fImplicitDiffs) + return S_OK; - /* rollback hard disk changes */ - mMediaData.rollback(); + HRESULT rc = S_OK; + MachineState_T oldState = mData->mMachineState; + + /* will release the lock before the potentially lengthy operation, + * so protect with the special state (unless already protected) */ + if ( oldState != MachineState_Saving + && oldState != MachineState_LiveSnapshotting + && oldState != MachineState_RestoringSnapshot + && oldState != MachineState_DeletingSnapshot + && oldState != MachineState_DeletingSnapshotOnline + && oldState != MachineState_DeletingSnapshotPaused + ) + setMachineState(MachineState_SettingUp); - MultiResult mrc(S_OK); + // use appropriate locked media map (online or offline) + MediumLockListMap lockedMediaOffline; + MediumLockListMap *lockedMediaMap; + if (aOnline) + lockedMediaMap = &mData->mSession.mLockedMedia; + else + lockedMediaMap = &lockedMediaOffline; - /* delete unused implicit diffs */ - if (implicitAtts.size() != 0) + try { - /* will release the lock before the potentially lengthy - * operation, so protect with the special state (unless already - * protected) */ - MachineState_T oldState = mData->mMachineState; - if ( oldState != MachineState_Saving - && oldState != MachineState_LiveSnapshotting - && oldState != MachineState_RestoringSnapshot - && oldState != MachineState_DeletingSnapshot - && oldState != MachineState_DeletingSnapshotOnline - && oldState != MachineState_DeletingSnapshotPaused - ) - setMachineState(MachineState_SettingUp); + if (!aOnline) + { + /* lock all attached hard disks early to detect "in use" + * situations before deleting actual diffs */ + for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); + it != mMediaData->mAttachments.end(); + ++it) + { + MediumAttachment* pAtt = *it; + if (pAtt->getType() == DeviceType_HardDisk) + { + Medium* pMedium = pAtt->getMedium(); + Assert(pMedium); - alock.release(); + MediumLockList *pMediumLockList(new MediumLockList()); + alock.release(); + rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */, + false /* fMediumLockWrite */, + NULL, + *pMediumLockList); + alock.acquire(); - for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); - it != implicitAtts.end(); + if (FAILED(rc)) + { + delete pMediumLockList; + throw rc; + } + + rc = lockedMediaMap->Insert(pAtt, pMediumLockList); + if (FAILED(rc)) + throw rc; + } + } + + if (FAILED(rc)) + throw rc; + } // end of offline + + /* Lock lists are now up to date and include implicitly created media */ + + /* Go through remembered attachments and delete all implicitly created + * diffs and fix up the attachment information */ + const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; + MediaData::AttachmentList implicitAtts; + for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); + it != mMediaData->mAttachments.end(); ++it) { - LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName())); - ComObjPtr<Medium> hd = (*it)->getMedium(); + ComObjPtr<MediumAttachment> pAtt = *it; + ComObjPtr<Medium> pMedium = pAtt->getMedium(); + if (pMedium.isNull()) + continue; - rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/); - AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() )); - mrc = rc; + // Implicit attachments go on the list for deletion and back references are removed. + if (pAtt->isImplicit()) + { + /* Deassociate and mark for deletion */ + LogFlowThisFunc(("Detaching '%s', pending deletion\n", pAtt->getLogName())); + rc = pMedium->removeBackReference(mData->mUuid); + if (FAILED(rc)) + throw rc; + implicitAtts.push_back(pAtt); + continue; + } + + /* Was this medium attached before? */ + if (!findAttachment(oldAtts, pMedium)) + { + /* no: de-associate */ + LogFlowThisFunc(("Detaching '%s', no deletion\n", pAtt->getLogName())); + rc = pMedium->removeBackReference(mData->mUuid); + if (FAILED(rc)) + throw rc; + continue; + } + LogFlowThisFunc(("Not detaching '%s'\n", pAtt->getLogName())); } - alock.acquire(); + /* If there are implicit attachments to delete, throw away the lock + * map contents (which will unlock all media) since the medium + * attachments will be rolled back. Below we need to completely + * recreate the lock map anyway since it is infinitely complex to + * do this incrementally (would need reconstructing each attachment + * change, which would be extremely hairy). */ + if (implicitAtts.size() != 0) + { + ErrorInfoKeeper eik; + + HRESULT rc1 = lockedMediaMap->Clear(); + AssertComRC(rc1); + } + + /* rollback hard disk changes */ + mMediaData.rollback(); + + MultiResult mrc(S_OK); + + // Delete unused implicit diffs. + if (implicitAtts.size() != 0) + { + alock.release(); + + for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin(); + it != implicitAtts.end(); + ++it) + { + // Remove medium associated with this attachment. + ComObjPtr<MediumAttachment> pAtt = *it; + Assert(pAtt); + LogFlowThisFunc(("Deleting '%s'\n", pAtt->getLogName())); + ComObjPtr<Medium> pMedium = pAtt->getMedium(); + Assert(pMedium); + + rc = pMedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/); + // continue on delete failure, just collect error messages + AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, pAtt->getLogName(), pMedium->getLocationFull().c_str() )); + mrc = rc; + } + + alock.acquire(); + + /* if there is a VM recreate media lock map as mentioned above, + * otherwise it is a waste of time and we leave things unlocked */ + if (aOnline) + { + const ComObjPtr<SessionMachine> pMachine = mData->mSession.mMachine; + /* must never be NULL, but better safe than sorry */ + if (!pMachine.isNull()) + { + alock.release(); + rc = mData->mSession.mMachine->lockMedia(); + alock.acquire(); + if (FAILED(rc)) + throw rc; + } + } + } + } + catch (HRESULT aRC) {rc = aRC;} + + if (mData->mMachineState == MachineState_SettingUp) + setMachineState(oldState); + + /* unlock all hard disks we locked when there is no VM */ + if (!aOnline) + { + ErrorInfoKeeper eik; - if (mData->mMachineState == MachineState_SettingUp) - setMachineState(oldState); + HRESULT rc1 = lockedMediaMap->Clear(); + AssertComRC(rc1); } - return mrc; + return rc; } + /** * Looks through the given list of media attachments for one with the given parameters * and returns it, or NULL if not found. The list is a parameter so that backup lists @@ -10822,11 +11866,21 @@ void Machine::commitMedia(bool aOnline /*= false*/) /* unlock since medium is not used anymore */ MediumLockList *pMediumLockList; rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList); - AssertComRC(rc); - if (pMediumLockList) + if (RT_UNLIKELY(rc == VBOX_E_INVALID_OBJECT_STATE)) + { + /* this happens for online snapshots, there the attachment + * is changing, but only to a diff image created under + * the old one, so there is no separate lock list */ + Assert(!pMediumLockList); + } + else { - rc = mData->mSession.mLockedMedia.Remove(pAttach); AssertComRC(rc); + if (pMediumLockList) + { + rc = mData->mSession.mLockedMedia.Remove(pAttach); + AssertComRC(rc); + } } } } @@ -10881,11 +11935,10 @@ void Machine::commitMedia(bool aOnline /*= false*/) void Machine::rollbackMedia() { AutoCaller autoCaller(this); - AssertComRCReturnVoid (autoCaller.rc()); - - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + AssertComRCReturnVoid(autoCaller.rc()); - LogFlowThisFunc(("Entering\n")); + // AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + LogFlowThisFunc(("Entering rollbackMedia\n")); HRESULT rc = S_OK; @@ -10927,7 +11980,7 @@ void Machine::rollbackMedia() /** @todo convert all this Machine-based voodoo to MediumAttachment * based rollback logic. */ - deleteImplicitDiffs(); + deleteImplicitDiffs(Global::IsOnline(mData->mMachineState)); return; } @@ -11011,6 +12064,40 @@ void Machine::rollback(bool aNotify) } } + if (!mUSBControllers.isNull()) + { + if (mUSBControllers.isBackedUp()) + { + /* unitialize all new devices (absent in the backed up list). */ + USBControllerList::const_iterator it = mUSBControllers->begin(); + USBControllerList *backedList = mUSBControllers.backedUpData(); + while (it != mUSBControllers->end()) + { + if ( std::find(backedList->begin(), backedList->end(), *it) + == backedList->end() + ) + { + (*it)->uninit(); + } + ++it; + } + + /* restore the list */ + mUSBControllers.rollback(); + } + + /* rollback any changes to devices after restoring the list */ + if (mData->flModifications & IsModified_USB) + { + USBControllerList::const_iterator it = mUSBControllers->begin(); + while (it != mUSBControllers->end()) + { + (*it)->rollback(); + ++it; + } + } + } + mUserData.rollback(); mHWData.rollback(); @@ -11027,8 +12114,8 @@ void Machine::rollback(bool aNotify) if (mAudioAdapter) mAudioAdapter->rollback(); - if (mUSBController && (mData->flModifications & IsModified_USB)) - mUSBController->rollback(); + if (mUSBDeviceFilters && (mData->flModifications & IsModified_USB)) + mUSBDeviceFilters->rollback(); if (mBandwidthControl && (mData->flModifications & IsModified_BandwidthControl)) mBandwidthControl->rollback(); @@ -11128,12 +12215,12 @@ void Machine::commit() mHWData.commit(); if (mMediaData.isBackedUp()) - commitMedia(); + commitMedia(Global::IsOnline(mData->mMachineState)); mBIOSSettings->commit(); mVRDEServer->commit(); mAudioAdapter->commit(); - mUSBController->commit(); + mUSBDeviceFilters->commit(); mBandwidthControl->commit(); /* Since mNetworkAdapters is a list which might have been changed (resized) @@ -11263,6 +12350,77 @@ void Machine::commit() } } + bool commitUSBControllers = false; + + if (mUSBControllers.isBackedUp()) + { + mUSBControllers.commit(); + + if (mPeer) + { + /* Commit all changes to new controllers (this will reshare data with + * peers for those who have peers) */ + USBControllerList *newList = new USBControllerList(); + USBControllerList::const_iterator it = mUSBControllers->begin(); + while (it != mUSBControllers->end()) + { + (*it)->commit(); + + /* look if this controller has a peer device */ + ComObjPtr<USBController> peer = (*it)->getPeer(); + if (!peer) + { + /* no peer means the device is a newly created one; + * create a peer owning data this device share it with */ + peer.createObject(); + peer->init(mPeer, *it, true /* aReshare */); + } + else + { + /* remove peer from the old list */ + mPeer->mUSBControllers->remove(peer); + } + /* and add it to the new list */ + newList->push_back(peer); + + ++it; + } + + /* uninit old peer's controllers that are left */ + it = mPeer->mUSBControllers->begin(); + while (it != mPeer->mUSBControllers->end()) + { + (*it)->uninit(); + ++it; + } + + /* attach new list of controllers to our peer */ + mPeer->mUSBControllers.attach(newList); + } + else + { + /* we have no peer (our parent is the newly created machine); + * just commit changes to devices */ + commitUSBControllers = true; + } + } + else + { + /* the list of controllers itself is not changed, + * just commit changes to controllers themselves */ + commitUSBControllers = true; + } + + if (commitUSBControllers) + { + USBControllerList::const_iterator it = mUSBControllers->begin(); + while (it != mUSBControllers->end()) + { + (*it)->commit(); + ++it; + } + } + if (isSessionMachine()) { /* attach new data to the primary machine and reshare it */ @@ -11311,7 +12469,7 @@ void Machine::copyFrom(Machine *aThat) mBIOSSettings->copyFrom(aThat->mBIOSSettings); mVRDEServer->copyFrom(aThat->mVRDEServer); mAudioAdapter->copyFrom(aThat->mAudioAdapter); - mUSBController->copyFrom(aThat->mUSBController); + mUSBDeviceFilters->copyFrom(aThat->mUSBDeviceFilters); mBandwidthControl->copyFrom(aThat->mBandwidthControl); /* create private copies of all controllers */ @@ -11327,6 +12485,19 @@ void Machine::copyFrom(Machine *aThat) mStorageControllers->push_back(ctrl); } + /* create private copies of all USB controllers */ + mUSBControllers.backup(); + mUSBControllers->clear(); + for (USBControllerList::iterator it = aThat->mUSBControllers->begin(); + it != aThat->mUSBControllers->end(); + ++it) + { + ComObjPtr<USBController> ctrl; + ctrl.createObject(); + ctrl->initCopy(this, *it); + mUSBControllers->push_back(ctrl); + } + mNetworkAdapters.resize(aThat->mNetworkAdapters.size()); for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]); @@ -11363,6 +12534,26 @@ bool Machine::isControllerHotplugCapable(StorageControllerType_T enmCtrlType) #ifdef VBOX_WITH_RESOURCE_USAGE_API +void Machine::getDiskList(MediaList &list) +{ + for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); + it != mMediaData->mAttachments.end(); + ++it) + { + MediumAttachment* pAttach = *it; + /* just in case */ + AssertStmt(pAttach, continue); + + AutoCaller localAutoCallerA(pAttach); + if (FAILED(localAutoCallerA.rc())) continue; + + AutoReadLock local_alockA(pAttach COMMA_LOCKVAL_SRC_POS); + + if (pAttach->getType() == DeviceType_HardDisk) + list.push_back(pAttach->getMedium()); + } +} + void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid) { AssertReturnVoid(isWriteLockOnCurrentThread()); @@ -11376,6 +12567,12 @@ void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachin "Percentage of processor time spent in kernel mode by the VM process."); pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used", "Size of resident portion of VM process in memory."); + pm::SubMetric *diskUsageUsed = new pm::SubMetric("Disk/Usage/Used", + "Actual size of all VM disks combined."); + pm::SubMetric *machineNetRx = new pm::SubMetric("Net/Rate/Rx", + "Network receive rate."); + pm::SubMetric *machineNetTx = new pm::SubMetric("Net/Rate/Tx", + "Network transmit rate."); /* Create and register base metrics */ pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid, cpuLoadUser, cpuLoadKernel); @@ -11383,6 +12580,11 @@ void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachin pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid, ramUsageUsed); aCollector->registerBaseMetric(ramUsage); + MediaList disks; + getDiskList(disks); + pm::BaseMetric *diskUsage = new pm::MachineDiskUsage(hal, aMachine, disks, + diskUsageUsed); + aCollector->registerBaseMetric(diskUsage); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0)); aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, @@ -11407,6 +12609,14 @@ void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachin aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, new pm::AggregateMax())); + aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, 0)); + aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, + new pm::AggregateAvg())); + aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, + new pm::AggregateMin())); + aCollector->registerMetric(new pm::Metric(diskUsage, diskUsageUsed, + new pm::AggregateMax())); + /* Guest metrics collector */ mCollectorGuest = new pm::CollectorGuest(aMachine, pid); @@ -11432,6 +12642,10 @@ 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 *machineNetRate = new pm::MachineNetRate(mCollectorGuest, aMachine, + machineNetRx, machineNetTx); + aCollector->registerBaseMetric(machineNetRate); + pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mCollectorGuest, aMachine, guestLoadUser, guestLoadKernel, guestLoadIdle); aCollector->registerBaseMetric(guestCpuLoad); @@ -11442,6 +12656,16 @@ void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachin guestMemCache, guestPagedTotal); aCollector->registerBaseMetric(guestCpuMem); + aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, 0)); + aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateAvg())); + aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMin())); + aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetRx, new pm::AggregateMax())); + + aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, 0)); + aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateAvg())); + aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMin())); + aCollector->registerMetric(new pm::Metric(machineNetRate, machineNetTx, new pm::AggregateMax())); + aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0)); aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg())); aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin())); @@ -11510,15 +12734,7 @@ HRESULT SessionMachine::FinalConstruct() { LogFlowThisFunc(("\n")); -#if defined(RT_OS_WINDOWS) - mIPCSem = NULL; -#elif defined(RT_OS_OS2) - mIPCSem = NULLHANDLE; -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) - mIPCSem = -1; -#else -# error "Port me!" -#endif + mClientToken = NULL; return BaseFinalConstruct(); } @@ -11527,13 +12743,21 @@ void SessionMachine::FinalRelease() { LogFlowThisFunc(("\n")); + Assert(!mClientToken); + /* paranoia, should not hang around any more */ + if (mClientToken) + { + delete mClientToken; + mClientToken = NULL; + } + uninit(Uninit::Unexpected); BaseFinalRelease(); } /** - * @note Must be called only by Machine::openSession() from its own write lock. + * @note Must be called only by Machine::LockMachine() from its own write lock. */ HRESULT SessionMachine::init(Machine *aMachine) { @@ -11548,96 +12772,25 @@ HRESULT SessionMachine::init(Machine *aMachine) AutoInitSpan autoInitSpan(this); AssertReturn(autoInitSpan.isOk(), E_FAIL); - /* create the interprocess semaphore */ -#if defined(RT_OS_WINDOWS) - mIPCSemName = aMachine->mData->m_strConfigFileFull; - for (size_t i = 0; i < mIPCSemName.length(); i++) - if (mIPCSemName.raw()[i] == '\\') - mIPCSemName.raw()[i] = '/'; - mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName.raw()); - ComAssertMsgRet(mIPCSem, - ("Cannot create IPC mutex '%ls', err=%d", - mIPCSemName.raw(), ::GetLastError()), - E_FAIL); -#elif defined(RT_OS_OS2) - Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}", - aMachine->mData->mUuid.raw()); - mIPCSemName = ipcSem; - APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.c_str(), &mIPCSem, 0, FALSE); - ComAssertMsgRet(arc == NO_ERROR, - ("Cannot create IPC mutex '%s', arc=%ld", - ipcSem.c_str(), arc), - E_FAIL); -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) -# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN -# if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64) - /** @todo Check that this still works correctly. */ - AssertCompileSize(key_t, 8); -# else - AssertCompileSize(key_t, 4); -# endif - key_t key; - mIPCSem = -1; - mIPCKey = "0"; - for (uint32_t i = 0; i < 1 << 24; i++) - { - key = ((uint32_t)'V' << 24) | i; - int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL); - if (sem >= 0 || (errno != EEXIST && errno != EACCES)) - { - mIPCSem = sem; - if (sem >= 0) - mIPCKey = BstrFmt("%u", key); - break; - } - } -# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ - Utf8Str semName = aMachine->mData->m_strConfigFileFull; - char *pszSemName = NULL; - RTStrUtf8ToCurrentCP(&pszSemName, semName); - key_t key = ::ftok(pszSemName, 'V'); - RTStrFree(pszSemName); - - mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT); -# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ + HRESULT rc = S_OK; - int errnoSave = errno; - if (mIPCSem < 0 && errnoSave == ENOSYS) + /* create the machine client token */ + try { - setError(E_FAIL, - tr("Cannot create IPC semaphore. Most likely your host kernel lacks " - "support for SysV IPC. Check the host kernel configuration for " - "CONFIG_SYSVIPC=y")); - return E_FAIL; + mClientToken = new ClientToken(aMachine, this); + if (!mClientToken->isReady()) + { + delete mClientToken; + mClientToken = NULL; + rc = E_FAIL; + } } - /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing - * the IPC semaphores */ - if (mIPCSem < 0 && errnoSave == ENOSPC) - { -#ifdef RT_OS_LINUX - setError(E_FAIL, - tr("Cannot create IPC semaphore because the system limit for the " - "maximum number of semaphore sets (SEMMNI), or the system wide " - "maximum number of semaphores (SEMMNS) would be exceeded. The " - "current set of SysV IPC semaphores can be determined from " - "the file /proc/sysvipc/sem")); -#else - setError(E_FAIL, - tr("Cannot create IPC semaphore because the system-imposed limit " - "on the maximum number of allowed semaphores or semaphore " - "identifiers system-wide would be exceeded")); -#endif - return E_FAIL; + catch (std::bad_alloc &) + { + rc = E_OUTOFMEMORY; } - ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave), - E_FAIL); - /* set the initial value to 1 */ - int rv = ::semctl(mIPCSem, 0, SETVAL, 1); - ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno), - E_FAIL); -#else -# error "Port me!" -#endif + if (FAILED(rc)) + return rc; /* memorize the peer Machine */ unconst(mPeer) = aMachine; @@ -11663,6 +12816,17 @@ HRESULT SessionMachine::init(Machine *aMachine) mStorageControllers->push_back(ctl); } + mUSBControllers.allocate(); + for (USBControllerList::const_iterator it = aMachine->mUSBControllers->begin(); + it != aMachine->mUSBControllers->end(); + ++it) + { + ComObjPtr<USBController> ctl; + ctl.createObject(); + ctl->init(this, *it); + mUSBControllers->push_back(ctl); + } + unconst(mBIOSSettings).createObject(); mBIOSSettings->init(this, aMachine->mBIOSSettings); /* create another VRDEServer object that will be mutable */ @@ -11683,9 +12847,10 @@ HRESULT SessionMachine::init(Machine *aMachine) unconst(mParallelPorts[slot]).createObject(); mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]); } - /* create another USB controller object that will be mutable */ - unconst(mUSBController).createObject(); - mUSBController->init(this, aMachine->mUSBController); + + /* create another USB device filters object that will be mutable */ + unconst(mUSBDeviceFilters).createObject(); + mUSBDeviceFilters->init(this, aMachine->mUSBDeviceFilters); /* create a list of network adapters that will be mutable */ mNetworkAdapters.resize(aMachine->mNetworkAdapters.size()); @@ -11705,13 +12870,16 @@ HRESULT SessionMachine::init(Machine *aMachine) /* Confirm a successful initialization when it's the case */ autoInitSpan.setSucceeded(); + miNATNetworksStarted = 0; + LogFlowThisFuncLeave(); - return S_OK; + return rc; } /** * Uninitializes this session object. If the reason is other than - * Uninit::Unexpected, then this method MUST be called from #checkForDeath(). + * Uninit::Unexpected, then this method MUST be called from #checkForDeath() + * or the client watcher code. * * @param aReason uninitialization reason * @@ -11747,24 +12915,12 @@ void SessionMachine::uninit(Uninit::Reason aReason) * below, the following is enough. */ LogFlowThisFunc(("Initialization failed.\n")); -#if defined(RT_OS_WINDOWS) - if (mIPCSem) - ::CloseHandle(mIPCSem); - mIPCSem = NULL; -#elif defined(RT_OS_OS2) - if (mIPCSem != NULLHANDLE) - ::DosCloseMutexSem(mIPCSem); - mIPCSem = NULLHANDLE; -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) - if (mIPCSem >= 0) - ::semctl(mIPCSem, 0, IPC_RMID); - mIPCSem = -1; -# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN - mIPCKey = "0"; -# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ -#else -# error "Port me!" -#endif + /* destroy the machine client token */ + if (mClientToken) + { + delete mClientToken; + mClientToken = NULL; + } uninitDataAndChildObjects(); mData.free(); unconst(mParent) = NULL; @@ -11792,7 +12948,7 @@ void SessionMachine::uninit(Uninit::Reason aReason) * * This is identical to SessionMachine::DetachAllUSBDevices except * for the aAbnormal argument. */ - HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */); + HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */); AssertComRC(rc); NOREF(rc); @@ -11803,23 +12959,17 @@ void SessionMachine::uninit(Uninit::Reason aReason) #endif /* VBOX_WITH_USB */ // we need to lock this object in uninit() because the lock is shared - // with mPeer (as well as data we modify below). mParent->addProcessToReap() - // and others need mParent lock, and USB needs host lock. + // with mPeer (as well as data we modify below). mParent lock is needed + // by several calls to it, and USB needs host lock. AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS); -#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 +#ifdef VBOX_WITH_RESOURCE_USAGE_API /* * It is safe to call Machine::unregisterMetrics() here because * PerformanceCollector::samplerCallback no longer accesses guest methods * holding the lock. */ unregisterMetrics(mParent->performanceCollector(), mPeer); -#endif /* The guest must be unregistered after its metrics (@bugref{5949}). */ LogAleksey(("{%p} " LOG_FN_FMT ": mCollectorGuest=%p\n", this, __PRETTY_FUNCTION__, mCollectorGuest)); @@ -11829,6 +12979,7 @@ void SessionMachine::uninit(Uninit::Reason aReason) // delete mCollectorGuest; => CollectorGuestManager::destroyUnregistered() mCollectorGuest = NULL; } +#endif if (aReason == Uninit::Abnormal) { @@ -11870,16 +13021,6 @@ void SessionMachine::uninit(Uninit::Reason aReason) releaseSavedStateFile(strStateFile, NULL /* pSnapshotToIgnore */ ); } - if (!mData->mSession.mType.isEmpty()) - { - /* mType is not null when this machine's process has been started by - * Machine::LaunchVMProcess(), therefore it is our child. We - * need to queue the PID to reap the process (and avoid zombies on - * Linux). */ - Assert(mData->mSession.mPID != NIL_RTPROCESS); - mParent->addProcessToReap(mData->mSession.mPID); - } - mData->mSession.mPID = NIL_RTPROCESS; if (aReason == Uninit::Unexpected) @@ -11910,6 +13051,33 @@ void SessionMachine::uninit(Uninit::Reason aReason) mData->mSession.mRemoteControls.clear(); } + /* Remove all references to the NAT network service. The service will stop + * if all references (also from other VMs) are removed. */ + for (; miNATNetworksStarted > 0; miNATNetworksStarted--) + { + for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) + { + NetworkAttachmentType_T type; + HRESULT hrc; + + hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type); + if ( SUCCEEDED(hrc) + && type == NetworkAttachmentType_NATNetwork) + { + Bstr name; + hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam()); + if (SUCCEEDED(hrc)) + { + multilock.release(); + LogRel(("VM '%s' stops using NAT network '%ls'\n", + mUserData->s.strName.c_str(), name.raw())); + mParent->natNetworkRefDec(name.raw()); + multilock.acquire(); + } + } + } + } + /* * An expected uninitialization can come only from #checkForDeath(). * Otherwise it means that something's gone really wrong (for example, @@ -11957,25 +13125,12 @@ void SessionMachine::uninit(Uninit::Reason aReason) mData->mSession.mState = SessionState_Unlocked; mData->mSession.mType.setNull(); - /* close the interprocess semaphore before leaving the exclusive lock */ -#if defined(RT_OS_WINDOWS) - if (mIPCSem) - ::CloseHandle(mIPCSem); - mIPCSem = NULL; -#elif defined(RT_OS_OS2) - if (mIPCSem != NULLHANDLE) - ::DosCloseMutexSem(mIPCSem); - mIPCSem = NULLHANDLE; -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) - if (mIPCSem >= 0) - ::semctl(mIPCSem, 0, IPC_RMID); - mIPCSem = -1; -# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN - mIPCKey = "0"; -# endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */ -#else -# error "Port me!" -#endif + /* destroy the machine client token before leaving the exclusive lock */ + if (mClientToken) + { + delete mClientToken; + mClientToken = NULL; + } /* fire an event */ mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked); @@ -12013,21 +13168,42 @@ RWLockHandle *SessionMachine::lockHandle() const /** * Passes collected guest statistics to performance collector object */ -STDMETHODIMP SessionMachine::ReportGuestStatistics(ULONG aValidStats, ULONG aCpuUser, - ULONG aCpuKernel, ULONG aCpuIdle, - ULONG aMemTotal, ULONG aMemFree, - ULONG aMemBalloon, ULONG aMemShared, - ULONG aMemCache, ULONG aPageTotal, - ULONG aAllocVMM, ULONG aFreeVMM, - ULONG aBalloonedVMM, ULONG aSharedVMM) +STDMETHODIMP SessionMachine::ReportVmStatistics(ULONG aValidStats, ULONG aCpuUser, + ULONG aCpuKernel, ULONG aCpuIdle, + ULONG aMemTotal, ULONG aMemFree, + ULONG aMemBalloon, ULONG aMemShared, + ULONG aMemCache, ULONG aPageTotal, + ULONG aAllocVMM, ULONG aFreeVMM, + ULONG aBalloonedVMM, ULONG aSharedVMM, + ULONG aVmNetRx, ULONG aVmNetTx) { +#ifdef VBOX_WITH_RESOURCE_USAGE_API if (mCollectorGuest) mCollectorGuest->updateStats(aValidStats, aCpuUser, aCpuKernel, aCpuIdle, aMemTotal, aMemFree, aMemBalloon, aMemShared, aMemCache, aPageTotal, aAllocVMM, aFreeVMM, - aBalloonedVMM, aSharedVMM); + aBalloonedVMM, aSharedVMM, aVmNetRx, aVmNetTx); return S_OK; +#else + NOREF(aValidStats); + NOREF(aCpuUser); + NOREF(aCpuKernel); + NOREF(aCpuIdle); + NOREF(aMemTotal); + NOREF(aMemFree); + NOREF(aMemBalloon); + NOREF(aMemShared); + NOREF(aMemCache); + NOREF(aPageTotal); + NOREF(aAllocVMM); + NOREF(aFreeVMM); + NOREF(aBalloonedVMM); + NOREF(aSharedVMM); + NOREF(aVmNetRx); + NOREF(aVmNetTx); + return E_NOTIMPL; +#endif } /** @@ -12054,31 +13230,6 @@ STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState) } /** - * @note Locks this object for reading. - */ -STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId) -{ - AutoCaller autoCaller(this); - AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - -#if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) - mIPCSemName.cloneTo(aId); - return S_OK; -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) -# ifdef VBOX_WITH_NEW_SYS_V_KEYGEN - mIPCKey.cloneTo(aId); -# else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ - mData->m_strConfigFileFull.cloneTo(aId); -# endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */ - return S_OK; -#else -# error "Port me!" -#endif -} - -/** * @note Locks this object for writing. */ STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress) @@ -12095,6 +13246,37 @@ STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress) if (!mData->mSession.mProgress.isNull()) mData->mSession.mProgress->setOtherProgressObject(aProgress); + /* If we didn't reference the NAT network service yet, add a reference to + * force a start */ + if (miNATNetworksStarted < 1) + { + for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) + { + NetworkAttachmentType_T type; + HRESULT hrc; + hrc = mNetworkAdapters[slot]->COMGETTER(AttachmentType)(&type); + if ( SUCCEEDED(hrc) + && type == NetworkAttachmentType_NATNetwork) + { + Bstr name; + hrc = mNetworkAdapters[slot]->COMGETTER(NATNetwork)(name.asOutParam()); + if (SUCCEEDED(hrc)) + { + LogRel(("VM '%s' starts using NAT network '%ls'\n", + mUserData->s.strName.c_str(), name.raw())); + mPeer->lockHandle()->unlockWrite(); + mParent->natNetworkRefInc(name.raw()); +#ifdef RT_LOCK_STRICT + mPeer->lockHandle()->lockWrite(RT_SRC_POS); +#else + mPeer->lockHandle()->lockWrite(); +#endif + } + } + } + miNATNetworksStarted++; + } + LogFlowThisFunc(("returns S_OK.\n")); return S_OK; } @@ -12240,7 +13422,7 @@ STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice, AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); #ifdef VBOX_WITH_USB - *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs); + *aMatched = mUSBDeviceFilters->hasMatchingFilter(aUSBDevice, aMaskedIfs); #else NOREF(aUSBDevice); NOREF(aMaskedIfs); @@ -12312,7 +13494,7 @@ STDMETHODIMP SessionMachine::AutoCaptureUSBDevices() AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); #ifdef VBOX_WITH_USB - HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */); + HRESULT rc = mUSBDeviceFilters->notifyProxy(true /* aInsertFilters */); AssertComRC(rc); NOREF(rc); @@ -12342,7 +13524,7 @@ STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone) AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); #ifdef VBOX_WITH_USB - HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */); + HRESULT rc = mUSBDeviceFilters->notifyProxy(false /* aInsertFilters */); AssertComRC(rc); NOREF(rc); @@ -12591,18 +13773,18 @@ STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames), com::SafeArray<LONG64> timestamps(cEntries); com::SafeArray<BSTR> flags(cEntries); unsigned i = 0; - for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin(); + for (HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.begin(); it != mHWData->mGuestProperties.end(); ++it) { char szFlags[MAX_FLAGS_LEN + 1]; - it->strName.cloneTo(&names[i]); - it->strValue.cloneTo(&values[i]); - timestamps[i] = it->mTimestamp; + it->first.cloneTo(&names[i]); + it->second.strValue.cloneTo(&values[i]); + timestamps[i] = it->second.mTimestamp; /* If it is NULL, keep it NULL. */ - if (it->mFlags) + if (it->second.mFlags) { - writeFlags(it->mFlags, szFlags); + writeFlags(it->second.mFlags, szFlags); Bstr(szFlags).cloneTo(&flags[i]); } else @@ -12638,8 +13820,8 @@ STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName, /* * Convert input up front. */ - Utf8Str utf8Name(aName); - uint32_t fFlags = NILFLAG; + Utf8Str utf8Name(aName); + uint32_t fFlags = NILFLAG; if (aFlags) { Utf8Str utf8Flags(aFlags); @@ -12665,43 +13847,40 @@ STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName, case MachineState_DeletingSnapshotOnline: case MachineState_DeletingSnapshotPaused: case MachineState_Saving: + case MachineState_Stopping: break; default: -#ifndef DEBUG_sunlover AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)), VBOX_E_INVALID_VM_STATE); -#else - return VBOX_E_INVALID_VM_STATE; -#endif } setModified(IsModified_MachineData); mHWData.backup(); - /** @todo r=bird: The careful memory handling doesn't work out here because - * the catch block won't undo any damage we've done. So, if push_back throws - * bad_alloc then you've lost the value. - * - * Another thing. Doing a linear search here isn't extremely efficient, esp. - * since values that changes actually bubbles to the end of the list. Using - * something that has an efficient lookup and can tolerate a bit of updates - * would be nice. RTStrSpace is one suggestion (it's not perfect). Some - * combination of RTStrCache (for sharing names and getting uniqueness into - * the bargain) and hash/tree is another. */ - for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin(); - iter != mHWData->mGuestProperties.end(); - ++iter) - if (utf8Name == iter->strName) + bool fDelete = !RT_VALID_PTR(aValue) || *(aValue) == '\0'; + HWData::GuestPropertyMap::iterator it = mHWData->mGuestProperties.find(utf8Name); + if (it != mHWData->mGuestProperties.end()) + { + if (!fDelete) { - mHWData->mGuestProperties.erase(iter); - mData->mGuestPropertiesModified = TRUE; - break; + it->second.strValue = aValue; + it->second.mTimestamp = aTimestamp; + it->second.mFlags = fFlags; } - if (aValue != NULL) + else + mHWData->mGuestProperties.erase(it); + + mData->mGuestPropertiesModified = TRUE; + } + else if (!fDelete) { - HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags }; - mHWData->mGuestProperties.push_back(property); + HWData::GuestProperty prop; + prop.strValue = aValue; + prop.mTimestamp = aTimestamp; + prop.mFlags = fFlags; + + mHWData->mGuestProperties[utf8Name] = prop; mData->mGuestPropertiesModified = TRUE; } @@ -12733,6 +13912,29 @@ STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName, #endif } +STDMETHODIMP SessionMachine::LockMedia() +{ + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); + + AutoMultiWriteLock2 alock(this->lockHandle(), + &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); + + AssertReturn( mData->mMachineState == MachineState_Starting + || mData->mMachineState == MachineState_Restoring + || mData->mMachineState == MachineState_TeleportingIn, E_FAIL); + + clearError(); + alock.release(); + return lockMedia(); +} + +STDMETHODIMP SessionMachine::UnlockMedia() +{ + unlockMedia(); + return S_OK; +} + STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment, IMediumAttachment **aNewAttachment) { @@ -12815,6 +14017,7 @@ STDMETHODIMP SessionMachine::EjectMedium(IMediumAttachment *aAttachment, // public methods only for internal purposes ///////////////////////////////////////////////////////////////////////////// +#ifndef VBOX_WITH_GENERIC_SESSION_WATCHER /** * Called from the client watcher thread to check for expected or unexpected * death of the client process that has a direct session to this machine. @@ -12860,47 +14063,56 @@ bool SessionMachine::checkForDeath() Uninit::Normal : Uninit::Abnormal; -#if defined(RT_OS_WINDOWS) - - AssertMsg(mIPCSem, ("semaphore must be created")); - - /* release the IPC mutex */ - ::ReleaseMutex(mIPCSem); - - terminated = true; + if (mClientToken) + terminated = mClientToken->release(); + } /* AutoCaller block */ -#elif defined(RT_OS_OS2) + if (terminated) + uninit(reason); - AssertMsg(mIPCSem, ("semaphore must be created")); + return terminated; +} - /* release the IPC mutex */ - ::DosReleaseMutexSem(mIPCSem); +void SessionMachine::getTokenId(Utf8Str &strTokenId) +{ + LogFlowThisFunc(("\n")); - terminated = true; + strTokenId.setNull(); -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); - AssertMsg(mIPCSem >= 0, ("semaphore must be created")); + Assert(mClientToken); + if (mClientToken) + mClientToken->getId(strTokenId); +} +#else /* VBOX_WITH_GENERIC_SESSION_WATCHER */ +IToken *SessionMachine::getToken() +{ + LogFlowThisFunc(("\n")); - int val = ::semctl(mIPCSem, 0, GETVAL); - if (val > 0) - { - /* the semaphore is signaled, meaning the session is terminated */ - terminated = true; - } + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), NULL); -#else -# error "Port me!" -#endif + Assert(mClientToken); + if (mClientToken) + return mClientToken->getToken(); + else + return NULL; +} +#endif /* VBOX_WITH_GENERIC_SESSION_WATCHER */ - } /* AutoCaller block */ +Machine::ClientToken *SessionMachine::getClientToken() +{ + LogFlowThisFunc(("\n")); - if (terminated) - uninit(reason); + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), NULL); - return terminated; + return mClientToken; } + /** * @note Locks this object for reading. */ @@ -13052,7 +14264,7 @@ HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove) LogFlowThisFunc(("\n")); AutoCaller autoCaller(this); - AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); + AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); ComPtr<IInternalSessionControl> directControl; { @@ -13072,7 +14284,7 @@ HRESULT SessionMachine::onCPUExecutionCapChange(ULONG aExecutionCap) LogFlowThisFunc(("\n")); AutoCaller autoCaller(this); - AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); + AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); ComPtr<IInternalSessionControl> directControl; { @@ -13111,6 +14323,29 @@ HRESULT SessionMachine::onVRDEServerChange(BOOL aRestart) } /** + * @note Locks this object for reading. + */ +HRESULT SessionMachine::onVideoCaptureChange() +{ + 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->OnVideoCaptureChange(); +} + +/** * @note Locks this object for reading. */ HRESULT SessionMachine::onUSBControllerChange() @@ -13210,7 +14445,7 @@ HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup) LogFlowThisFunc(("\n")); AutoCaller autoCaller(this); - AssertComRCReturn (autoCaller.rc(), autoCaller.rc()); + AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); ComPtr<IInternalSessionControl> directControl; { @@ -13228,7 +14463,7 @@ HRESULT SessionMachine::onBandwidthGroupChange(IBandwidthGroup *aBandwidthGroup) /** * @note Locks this object for reading. */ -HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove) +HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BOOL aRemove, BOOL aSilent) { LogFlowThisFunc(("\n")); @@ -13245,7 +14480,7 @@ HRESULT SessionMachine::onStorageDeviceChange(IMediumAttachment *aAttachment, BO if (!directControl) return S_OK; - return directControl->OnStorageDeviceChange(aAttachment, aRemove); + return directControl->OnStorageDeviceChange(aAttachment, aRemove, aSilent); } /** @@ -13275,7 +14510,7 @@ bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevic /** @todo Live Migration: snapshoting & teleporting. Need to fend things of * elsewhere... */ alock.release(); - return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs); + return mUSBDeviceFilters->hasMatchingFilter(aDevice, aMaskedIfs); default: break; } #else @@ -13456,8 +14691,7 @@ void SessionMachine::releaseSavedStateFile(const Utf8Str &strStateFile, * locked as described above; on failure no media is locked at all (all * succeeded individual locks will be undone). * - * This method is intended to be called when the machine is in Starting or - * Restoring state and asserts otherwise. + * The caller is responsible for doing the necessary state sanity checks. * * The locks made by this method must be undone by calling #unlockMedia() when * no more needed. @@ -13470,13 +14704,9 @@ HRESULT SessionMachine::lockMedia() AutoMultiWriteLock2 alock(this->lockHandle(), &mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); - AssertReturn( mData->mMachineState == MachineState_Starting - || mData->mMachineState == MachineState_Restoring - || mData->mMachineState == MachineState_TeleportingIn, E_FAIL); /* bail out if trying to lock things with already set up locking */ AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL); - clearError(); MultiResult mrc(S_OK); /* Collect locking information for all medium objects attached to the VM. */ @@ -13749,13 +14979,13 @@ HRESULT SessionMachine::setMachineState(MachineState_T aMachineState) /* Make sure any transient guest properties get removed from the * property store on shutdown. */ - HWData::GuestPropertyList::iterator it; + HWData::GuestPropertyMap::const_iterator it; BOOL fNeedsSaving = mData->mGuestPropertiesModified; if (!fNeedsSaving) for (it = mHWData->mGuestProperties.begin(); it != mHWData->mGuestProperties.end(); ++it) - if ( (it->mFlags & guestProp::TRANSIENT) - || (it->mFlags & guestProp::TRANSRESET)) + if ( (it->second.mFlags & guestProp::TRANSIENT) + || (it->second.mFlags & guestProp::TRANSRESET)) { fNeedsSaving = true; break; diff --git a/src/VBox/Main/src-server/MachineImplCloneVM.cpp b/src/VBox/Main/src-server/MachineImplCloneVM.cpp index d010a04f..cdc71b82 100644 --- a/src/VBox/Main/src-server/MachineImplCloneVM.cpp +++ b/src/VBox/Main/src-server/MachineImplCloneVM.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2011-2012 Oracle Corporation + * Copyright (C) 2011-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -174,11 +174,11 @@ void MachineCloneVMPrivate::updateProgressStats(MEDIUMTASKCHAIN &mtc, bool fAtta * it. Adding the biggest size in the chain should balance this a * little bit more, i.e. the weight is the sum of the data which * needs to be read and written. */ - uint64_t uMaxSize = 0; + ULONG uMaxWeight = 0; for (size_t e = mtc.chain.size(); e > 0; --e) { MEDIUMTASK &mt = mtc.chain.at(e - 1); - mt.uWeight += uMaxSize; + mt.uWeight += uMaxWeight; /* Calculate progress data */ ++uCount; @@ -186,7 +186,7 @@ void MachineCloneVMPrivate::updateProgressStats(MEDIUMTASKCHAIN &mtc, bool fAtta /* Save the max size for better weighting of diff image * creation. */ - uMaxSize = RT_MAX(uMaxSize, mt.uWeight); + uMaxWeight = RT_MAX(uMaxWeight, mt.uWeight); } } } @@ -207,7 +207,7 @@ HRESULT MachineCloneVMPrivate::addSaveState(const ComObjPtr<Machine> &machine, U return p->setError(VBOX_E_IPRT_ERROR, p->tr("Could not query file size of '%s' (%Rrc)"), sst.strSaveStateFile.c_str(), vrc); /* same rule as above: count both the data which needs to * be read and written */ - sst.uWeight = 2 * (cbSize + _1M - 1) / _1M; + sst.uWeight = (ULONG)(2 * (cbSize + _1M - 1) / _1M); llSaveStateFiles.append(sst); ++uCount; uTotalWeight += sst.uWeight; @@ -289,7 +289,7 @@ HRESULT MachineCloneVMPrivate::queryMediasForMachineState(const RTCList<ComObjPt if (fAttachLinked) mt.uWeight = 0; /* dummy */ else - mt.uWeight = (lSize + _1M - 1) / _1M; + mt.uWeight = (ULONG)((lSize + _1M - 1) / _1M); mtc.chain.append(mt); /* Update the progress info. */ @@ -389,7 +389,7 @@ HRESULT MachineCloneVMPrivate::queryMediasForMachineAndChildStates(const RTCList MEDIUMTASK mt; mt.uIdx = UINT32_MAX; mt.pMedium = pSrcMedium; - mt.uWeight = (lSize + _1M - 1) / _1M; + mt.uWeight = (ULONG)((lSize + _1M - 1) / _1M); mtc.chain.append(mt); /* Query next parent. */ @@ -523,7 +523,7 @@ HRESULT MachineCloneVMPrivate::queryMediasForAllStates(const RTCList<ComObjPtr<M MEDIUMTASK mt; mt.uIdx = UINT32_MAX; mt.pMedium = pSrcMedium; - mt.uWeight = (lSize + _1M - 1) / _1M; + mt.uWeight = (ULONG)((lSize + _1M - 1) / _1M); mtc.chain.append(mt); /* Query next parent. */ @@ -950,7 +950,8 @@ HRESULT MachineCloneVM::run() /* If we got a valid snapshot id, replace the hardware/storage section * with the stuff from the snapshot. */ settings::Snapshot sn; - if (!d->snapshotId.isEmpty()) + + if (d->snapshotId.isValid() && !d->snapshotId.isZero()) if (!d->findSnapshot(trgMCF.llFirstSnapshot, d->snapshotId, sn)) throw p->setError(E_FAIL, p->tr("Could not find data to snapshots '%s'"), d->snapshotId.toString().c_str()); @@ -959,7 +960,7 @@ HRESULT MachineCloneVM::run() if (d->mode == CloneMode_MachineState) { - if (!sn.uuid.isEmpty()) + if (sn.uuid.isValid() && !sn.uuid.isZero()) { trgMCF.hardwareMachine = sn.hardware; trgMCF.storageMachine = sn.storage; @@ -970,7 +971,8 @@ HRESULT MachineCloneVM::run() trgMCF.uuidCurrentSnapshot.clear(); } else if ( d->mode == CloneMode_MachineAndChildStates - && !sn.uuid.isEmpty()) + && sn.uuid.isValid() + && !sn.uuid.isZero()) { if (!d->pOldMachineState.isNull()) { @@ -1087,14 +1089,24 @@ HRESULT MachineCloneVM::run() ComPtr<IMediumFormat> pSrcFormat; rc = pMedium->COMGETTER(MediumFormat)(pSrcFormat.asOutParam()); ULONG uSrcCaps = 0; - rc = pSrcFormat->COMGETTER(Capabilities)(&uSrcCaps); + com::SafeArray <MediumFormatCapabilities_T> mediumFormatCap; + rc = pSrcFormat->COMGETTER(Capabilities)(ComSafeArrayAsOutParam(mediumFormatCap)); + if (FAILED(rc)) throw rc; + else + { + for (ULONG j = 0; j < mediumFormatCap.size(); j++) + uSrcCaps |= mediumFormatCap[j]; + } /* Default format? */ Utf8Str strDefaultFormat; p->mParent->getDefaultHardDiskFormat(strDefaultFormat); Bstr bstrSrcFormat(strDefaultFormat); + ULONG srcVar = MediumVariant_Standard; + com::SafeArray <MediumVariant_T> mediumVariant; + /* Is the source file based? */ if ((uSrcCaps & MediumFormatCapabilities_File) == MediumFormatCapabilities_File) { @@ -1102,8 +1114,14 @@ HRESULT MachineCloneVM::run() * will be used. */ rc = pMedium->COMGETTER(Format)(bstrSrcFormat.asOutParam()); if (FAILED(rc)) throw rc; - rc = pMedium->COMGETTER(Variant)(&srcVar); + + rc = pMedium->COMGETTER(Variant)(ComSafeArrayAsOutParam(mediumVariant)); if (FAILED(rc)) throw rc; + else + { + for (size_t j = 0; j < mediumVariant.size(); j++) + srcVar |= mediumVariant[j]; + } } Guid newId; @@ -1128,7 +1146,9 @@ HRESULT MachineCloneVM::run() && strSrcTest.endsWith("}")) { strSrcTest = strSrcTest.substr(1, strSrcTest.length() - 2); - if (isValidGuid(strSrcTest)) + + Guid temp_guid(strSrcTest); + if (temp_guid.isValid() && !temp_guid.isZero()) strNewName = Utf8StrFmt("%s%s", newId.toStringCurly().c_str(), RTPathExt(strNewName.c_str())); } else @@ -1322,7 +1342,7 @@ HRESULT MachineCloneVM::run() } /* Update the path in the configuration either for the current * machine state or the snapshots. */ - if (sst.snapshotUuid.isEmpty()) + if (!sst.snapshotUuid.isValid() || sst.snapshotUuid.isZero()) trgMCF.strStateFile = strTrgSaveState; else d->updateStateFile(trgMCF.llFirstSnapshot, sst.snapshotUuid, strTrgSaveState); diff --git a/src/VBox/Main/src-server/Matching.cpp b/src/VBox/Main/src-server/Matching.cpp index 83174f92..a2e0a4aa 100644 --- a/src/VBox/Main/src-server/Matching.cpp +++ b/src/VBox/Main/src-server/Matching.cpp @@ -36,7 +36,7 @@ void ParsedIntervalFilter_base::parse (const char *aFilter, that->mValid = true; that->mErrorPosition = 0; - if (!aFilter || strncmp (aFilter, "int:", 4) != 0) + if (!aFilter || strncmp(aFilter, RT_STR_TUPLE("int:")) != 0) return; that->mNull = false; diff --git a/src/VBox/Main/src-server/MediumAttachmentImpl.cpp b/src/VBox/Main/src-server/MediumAttachmentImpl.cpp index 26dfeb34..6059ced6 100644 --- a/src/VBox/Main/src-server/MediumAttachmentImpl.cpp +++ b/src/VBox/Main/src-server/MediumAttachmentImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -42,7 +42,8 @@ struct BackupableMediumAttachmentData fTempEject(false), fNonRotational(false), fDiscard(false), - fImplicit(false) + fImplicit(false), + fHotPluggable(false) { } ComObjPtr<Medium> pMedium; @@ -62,6 +63,7 @@ struct BackupableMediumAttachmentData bool fNonRotational; bool fDiscard; bool fImplicit; + bool fHotPluggable; }; struct MediumAttachment::Data @@ -122,10 +124,11 @@ HRESULT MediumAttachment::init(Machine *aParent, bool aTempEject, bool aNonRotational, bool aDiscard, + bool aHotPluggable, const Utf8Str &strBandwidthGroup) { LogFlowThisFuncEnter(); - LogFlowThisFunc(("aParent=%p aMedium=%p aControllerName=%ls aPort=%d aDevice=%d aType=%d aImplicit=%d aPassthrough=%d aTempEject=%d aNonRotational=%d strBandwithGroup=%s\n", aParent, aMedium, aControllerName.raw(), aPort, aDevice, aType, aImplicit, aPassthrough, aTempEject, aNonRotational, strBandwidthGroup.c_str())); + LogFlowThisFunc(("aParent=%p aMedium=%p aControllerName=%ls aPort=%d aDevice=%d aType=%d aImplicit=%d aPassthrough=%d aTempEject=%d aNonRotational=%d aDiscard=%d aHotPluggable=%d strBandwithGroup=%s\n", aParent, aMedium, aControllerName.raw(), aPort, aDevice, aType, aImplicit, aPassthrough, aTempEject, aNonRotational, aDiscard, aHotPluggable, strBandwidthGroup.c_str())); if (aType == DeviceType_HardDisk) AssertReturn(aMedium, E_INVALIDARG); @@ -151,6 +154,7 @@ HRESULT MediumAttachment::init(Machine *aParent, m->bd->fNonRotational = aNonRotational; m->bd->fDiscard = aDiscard; m->bd->fImplicit = aImplicit; + m->bd->fHotPluggable = aHotPluggable; /* Confirm a successful initialization when it's the case */ autoInitSpan.setSucceeded(); @@ -416,6 +420,23 @@ STDMETHODIMP MediumAttachment::COMGETTER(BandwidthGroup) (IBandwidthGroup **aBwG return hrc; } +STDMETHODIMP MediumAttachment::COMGETTER(HotPluggable)(BOOL *aHotPluggable) +{ + LogFlowThisFuncEnter(); + + CheckComArgOutPointerValid(aHotPluggable); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS); + + *aHotPluggable = m->bd->fHotPluggable; + + LogFlowThisFuncLeave(); + return S_OK; +} + /** * @note Locks this object for writing. */ @@ -512,6 +533,12 @@ bool MediumAttachment::getDiscard() const return m->bd->fDiscard; } +bool MediumAttachment::getHotPluggable() const +{ + AutoReadLock lock(this COMMA_LOCKVAL_SRC_POS); + return m->bd->fHotPluggable; +} + const Utf8Str& MediumAttachment::getBandwidthGroup() const { return m->bd->strBandwidthGroup; @@ -582,6 +609,15 @@ void MediumAttachment::updateDiscard(bool aDiscard) m->bd->fDiscard = aDiscard; } +/** Must be called from under this object's write lock. */ +void MediumAttachment::updateHotPluggable(bool aHotPluggable) +{ + Assert(isWriteLockOnCurrentThread()); + + m->bd.backup(); + m->bd->fHotPluggable = aHotPluggable; +} + void MediumAttachment::updateBandwidthGroup(const Utf8Str &aBandwidthGroup) { LogFlowThisFuncEnter(); diff --git a/src/VBox/Main/src-server/MediumFormatImpl.cpp b/src/VBox/Main/src-server/MediumFormatImpl.cpp index 71080e43..bd07fbf1 100644 --- a/src/VBox/Main/src-server/MediumFormatImpl.cpp +++ b/src/VBox/Main/src-server/MediumFormatImpl.cpp @@ -1,11 +1,11 @@ /* $Id: MediumFormatImpl.cpp $ */ /** @file * - * VirtualBox COM class implementation + * MediumFormat COM class implementation */ /* - * Copyright (C) 2008-2011 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -75,7 +75,7 @@ HRESULT MediumFormat::init(const VDBACKENDINFO *aVDInfo) { DeviceType_T devType; - unconst(m.llFileExtensions).push_back(papExtension->pszExtension); + unconst(m.maFileExtensions).push_back(papExtension->pszExtension); switch(papExtension->enmType) { @@ -93,7 +93,7 @@ HRESULT MediumFormat::init(const VDBACKENDINFO *aVDInfo) return E_INVALIDARG; } - unconst(m.llDeviceTypes).push_back(devType); + unconst(m.maDeviceTypes).push_back(devType); ++papExtension; } } @@ -156,7 +156,7 @@ HRESULT MediumFormat::init(const VDBACKENDINFO *aVDInfo) dt, flags, defaultValue }; - unconst(m.llProperties).push_back(prop); + unconst(m.maProperties).push_back(prop); ++pa; } } @@ -180,9 +180,9 @@ void MediumFormat::uninit() if (autoUninitSpan.uninitDone()) return; - unconst(m.llProperties).clear(); - unconst(m.llFileExtensions).clear(); - unconst(m.llDeviceTypes).clear(); + unconst(m.maProperties).clear(); + unconst(m.maFileExtensions).clear(); + unconst(m.maDeviceTypes).clear(); unconst(m.capabilities) = (MediumFormatCapabilities_T)0; unconst(m.strName).setNull(); unconst(m.strId).setNull(); @@ -191,129 +191,79 @@ void MediumFormat::uninit() // IMediumFormat properties ///////////////////////////////////////////////////////////////////////////// -STDMETHODIMP MediumFormat::COMGETTER(Id)(BSTR *aId) +HRESULT MediumFormat::getId(com::Utf8Str &aId) { - CheckComArgOutPointerValid(aId); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* this is const, no need to lock */ - m.strId.cloneTo(aId); + aId = m.strId; return S_OK; } -STDMETHODIMP MediumFormat::COMGETTER(Name)(BSTR *aName) +HRESULT MediumFormat::getName(com::Utf8Str &aName) { - CheckComArgOutPointerValid(aName); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* this is const, no need to lock */ - m.strName.cloneTo(aName); + aName = m.strName; return S_OK; } -STDMETHODIMP MediumFormat::COMGETTER(Capabilities)(ULONG *aCaps) +HRESULT MediumFormat::getCapabilities(std::vector<MediumFormatCapabilities_T> &aCapabilities) { - CheckComArgOutPointerValid(aCaps); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* m.capabilities is const, no need to lock */ - /// @todo add COMGETTER(ExtendedCapabilities) when we reach the 32 bit - /// limit (or make the argument ULONG64 after checking that COM is capable - /// of defining enums (used to represent bit flags) that contain 64-bit - /// values). Or go away from the enum/ulong hack for bit sets and use - /// a safearray like elsewhere. - ComAssertRet((uint64_t)m.capabilities == ((ULONG)m.capabilities), E_FAIL); - - *aCaps = (ULONG)m.capabilities; + aCapabilities.resize(sizeof(MediumFormatCapabilities_T) * 8); + size_t cCapabilities = 0; + for (size_t i = 0; i < aCapabilities.size(); i++) + { + uint64_t tmp = m.capabilities; + tmp &= 1ULL << i; + if (tmp) + aCapabilities[cCapabilities++] = (MediumFormatCapabilities_T)tmp; + } + aCapabilities.resize(RT_MAX(cCapabilities, 1)); return S_OK; } -STDMETHODIMP MediumFormat::DescribeFileExtensions(ComSafeArrayOut(BSTR, aFileExtensions), - ComSafeArrayOut(DeviceType_T, aDeviceTypes)) -{ - CheckComArgOutSafeArrayPointerValid(aFileExtensions); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); +// IMediumFormat methods +///////////////////////////////////////////////////////////////////////////// +HRESULT MediumFormat::describeFileExtensions(std::vector<com::Utf8Str> &aExtensions, + std::vector<DeviceType_T> &aTypes) +{ /* this is const, no need to lock */ - com::SafeArray<BSTR> fileExtentions(m.llFileExtensions.size()); - int i = 0; - for (StrList::const_iterator it = m.llFileExtensions.begin(); - it != m.llFileExtensions.end(); - ++it, ++i) - (*it).cloneTo(&fileExtentions[i]); - fileExtentions.detachTo(ComSafeArrayOutArg(aFileExtensions)); - - com::SafeArray<DeviceType_T> deviceTypes(m.llDeviceTypes.size()); - i = 0; - for (DeviceTypeList::const_iterator it = m.llDeviceTypes.begin(); - it != m.llDeviceTypes.end(); - ++it, ++i) - deviceTypes[i] = (*it); - deviceTypes.detachTo(ComSafeArrayOutArg(aDeviceTypes)); + aExtensions = m.maFileExtensions; + aTypes = m.maDeviceTypes; return S_OK; } -STDMETHODIMP MediumFormat::DescribeProperties(ComSafeArrayOut(BSTR, aNames), - ComSafeArrayOut(BSTR, aDescriptions), - ComSafeArrayOut(DataType_T, aTypes), - ComSafeArrayOut(ULONG, aFlags), - ComSafeArrayOut(BSTR, aDefaults)) +HRESULT MediumFormat::describeProperties(std::vector<com::Utf8Str> &aNames, + std::vector<com::Utf8Str> &aDescriptions, + std::vector<DataType_T> &aTypes, + std::vector<ULONG> &aFlags, + std::vector<com::Utf8Str> &aDefaults) { - CheckComArgOutSafeArrayPointerValid(aNames); - CheckComArgOutSafeArrayPointerValid(aDescriptions); - CheckComArgOutSafeArrayPointerValid(aTypes); - CheckComArgOutSafeArrayPointerValid(aFlags); - CheckComArgOutSafeArrayPointerValid(aDefaults); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* this is const, no need to lock */ - size_t c = m.llProperties.size(); - com::SafeArray<BSTR> propertyNames(c); - com::SafeArray<BSTR> propertyDescriptions(c); - com::SafeArray<DataType_T> propertyTypes(c); - com::SafeArray<ULONG> propertyFlags(c); - com::SafeArray<BSTR> propertyDefaults(c); - - int i = 0; - for (PropertyList::const_iterator it = m.llProperties.begin(); - it != m.llProperties.end(); - ++it, ++i) + size_t c = m.maProperties.size(); + aNames.resize(c); + aDescriptions.resize(c); + aTypes.resize(c); + aFlags.resize(c); + aDefaults.resize(c); + for (size_t i = 0; i < c; i++) { - const Property &prop = (*it); - prop.strName.cloneTo(&propertyNames[i]); - prop.strDescription.cloneTo(&propertyDescriptions[i]); - propertyTypes[i] = prop.type; - propertyFlags[i] = prop.flags; - prop.strDefaultValue.cloneTo(&propertyDefaults[i]); + const Property &prop = m.maProperties[i]; + aNames[i] = prop.strName; + aDescriptions[i] = prop.strDescription; + aTypes[i] = prop.type; + aFlags[i] = prop.flags; + aDefaults[i] = prop.strDefaultValue; } - propertyNames.detachTo(ComSafeArrayOutArg(aNames)); - propertyDescriptions.detachTo(ComSafeArrayOutArg(aDescriptions)); - propertyTypes.detachTo(ComSafeArrayOutArg(aTypes)); - propertyFlags.detachTo(ComSafeArrayOutArg(aFlags)); - propertyDefaults.detachTo(ComSafeArrayOutArg(aDefaults)); - return S_OK; } -// IMediumFormat methods -///////////////////////////////////////////////////////////////////////////// - // public methods only for internal purposes ///////////////////////////////////////////////////////////////////////////// /* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/MediumImpl.cpp b/src/VBox/Main/src-server/MediumImpl.cpp index 64edc67f..3e5da5b3 100644 --- a/src/VBox/Main/src-server/MediumImpl.cpp +++ b/src/VBox/Main/src-server/MediumImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008-2012 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -16,6 +16,7 @@ */ #include "MediumImpl.h" +#include "TokenImpl.h" #include "ProgressImpl.h" #include "SystemPropertiesImpl.h" #include "VirtualBoxImpl.h" @@ -68,9 +69,9 @@ struct BackRef BackRef(const Guid &aMachineId, const Guid &aSnapshotId = Guid::Empty) : machineId(aMachineId), - fInCurState(aSnapshotId.isEmpty()) + fInCurState(aSnapshotId.isZero()) { - if (!aSnapshotId.isEmpty()) + if (aSnapshotId.isValid() && !aSnapshotId.isZero()) llSnapshotIds.push_back(aSnapshotId); } @@ -514,7 +515,7 @@ public: Medium *aTarget, bool fMergeForward, Medium *aParentForTarget, - const MediaList &aChildrenToReparent, + MediumLockList *aChildrenToReparent, Progress *aProgress, MediumLockList *aMediumLockList, bool fKeepMediumLockList = false) @@ -522,55 +523,29 @@ public: mTarget(aTarget), mfMergeForward(fMergeForward), mParentForTarget(aParentForTarget), - mChildrenToReparent(aChildrenToReparent), + mpChildrenToReparent(aChildrenToReparent), mpMediumLockList(aMediumLockList), mTargetCaller(aTarget), mParentForTargetCaller(aParentForTarget), - mfChildrenCaller(false), mfKeepMediumLockList(fKeepMediumLockList) { AssertReturnVoidStmt(aMediumLockList != NULL, mRC = E_FAIL); - for (MediaList::const_iterator it = mChildrenToReparent.begin(); - it != mChildrenToReparent.end(); - ++it) - { - HRESULT rc2 = (*it)->addCaller(); - if (FAILED(rc2)) - { - mRC = E_FAIL; - for (MediaList::const_iterator it2 = mChildrenToReparent.begin(); - it2 != it; - --it2) - { - (*it2)->releaseCaller(); - } - return; - } - } - mfChildrenCaller = true; } ~MergeTask() { if (!mfKeepMediumLockList && mpMediumLockList) delete mpMediumLockList; - if (mfChildrenCaller) - { - for (MediaList::const_iterator it = mChildrenToReparent.begin(); - it != mChildrenToReparent.end(); - ++it) - { - (*it)->releaseCaller(); - } - } + if (mpChildrenToReparent) + delete mpChildrenToReparent; } const ComObjPtr<Medium> mTarget; bool mfMergeForward; - /* When mChildrenToReparent is empty then mParentForTarget is non-null. - * In other words: they are used in different cases. */ + /* When mpChildrenToReparent is null then mParentForTarget is non-null and + * vice versa. In other words: they are used in different cases. */ const ComObjPtr<Medium> mParentForTarget; - MediaList mChildrenToReparent; + MediumLockList *mpChildrenToReparent; MediumLockList *mpMediumLockList; private: @@ -578,7 +553,6 @@ private: AutoCaller mTargetCaller; AutoCaller mParentForTargetCaller; - bool mfChildrenCaller; bool mfKeepMediumLockList; }; @@ -941,7 +915,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox, unconst(m->pVirtualBox) = aVirtualBox; - if (!uuidMachineRegistry.isEmpty()) + if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero()) m->llRegistryIDs.push_back(uuidMachineRegistry); /* no storage yet */ @@ -958,7 +932,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox, rc = setLocation(aLocation); if (FAILED(rc)) return rc; - if (!(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateFixed + if (!(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateFixed | MediumFormatCapabilities_CreateDynamic)) ) { @@ -973,8 +947,29 @@ HRESULT Medium::init(VirtualBox *aVirtualBox, AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); ComObjPtr<Medium> pMedium; + + /* + * Check whether the UUID is taken already and create a new one + * if required. + * Try this only a limited amount of times in case the PRNG is broken + * in some way to prevent an endless loop. + */ + for (unsigned i = 0; i < 5; i++) + { + bool fInUse; + + fInUse = m->pVirtualBox->isMediaUuidInUse(m->id, DeviceType_HardDisk); + if (fInUse) + { + // create new UUID + unconst(m->id).create(); + } + else + break; + } + rc = m->pVirtualBox->registerMedium(this, &pMedium, DeviceType_HardDisk); - Assert(this == pMedium); + Assert(this == pMedium || FAILED(rc)); } /* Confirm a successful initialization when it's the case */ @@ -1086,7 +1081,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox, } else { - AssertStmt(!m->id.isEmpty(), + AssertStmt(!m->id.isZero(), alock.release(); autoCaller.release(); uninit(); return E_FAIL); /* storage format must be detected by Medium::queryInfo if the @@ -1141,7 +1136,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox, unconst(m->pVirtualBox) = aVirtualBox; - if (!uuidMachineRegistry.isEmpty()) + if (uuidMachineRegistry.isValid() && !uuidMachineRegistry.isZero()) m->llRegistryIDs.push_back(uuidMachineRegistry); /* register with VirtualBox/parent early, since uninit() will @@ -1218,7 +1213,7 @@ HRESULT Medium::init(VirtualBox *aVirtualBox, } Utf8Str strFull; - if (m->formatObj->getCapabilities() & MediumFormatCapabilities_File) + if (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File) { // compose full path of the medium, if it's not fully qualified... // slightly convoluted logic here. If the caller has given us a @@ -1510,20 +1505,29 @@ STDMETHODIMP Medium::COMGETTER(State)(MediumState_T *aState) return S_OK; } -STDMETHODIMP Medium::COMGETTER(Variant)(ULONG *aVariant) +STDMETHODIMP Medium::COMGETTER(Variant)(ComSafeArrayOut(MediumVariant_T, aVariant)) { - CheckComArgOutPointerValid(aVariant); + CheckComArgOutSafeArrayPointerValid(aVariant); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - *aVariant = m->variant; + + SafeArray<MediumVariant_T> variants(sizeof(MediumVariant_T)*8); + + for (ULONG i = 0; i < variants.size(); ++i) + { + ULONG temp = m->variant; + temp &= 1<<i; + variants [i] = (MediumVariant_T)temp; + } + + variants.detachTo(ComSafeArrayOutArg(aVariant)); return S_OK; } - STDMETHODIMP Medium::COMGETTER(Location)(BSTR *aLocation) { CheckComArgOutPointerValid(aLocation); @@ -1538,27 +1542,6 @@ STDMETHODIMP Medium::COMGETTER(Location)(BSTR *aLocation) return S_OK; } -STDMETHODIMP Medium::COMSETTER(Location)(IN_BSTR aLocation) -{ - CheckComArgStrNotEmptyOrNull(aLocation); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - - /// @todo NEWMEDIA for file names, add the default extension if no extension - /// is present (using the information from the VD backend which also implies - /// that one more parameter should be passed to setLocation() requesting - /// that functionality since it is only allowed when called from this method - - /// @todo NEWMEDIA rename the file and set m->location on success, then save - /// the global registry (and local registries of portable VMs referring to - /// this medium), this will also require to add the mRegistered flag to data - - ReturnComNotImplemented(); -} - STDMETHODIMP Medium::COMGETTER(Name)(BSTR *aName) { CheckComArgOutPointerValid(aName); @@ -1863,30 +1846,14 @@ STDMETHODIMP Medium::COMGETTER(LogicalSize)(LONG64 *aLogicalSize) { CheckComArgOutPointerValid(aLogicalSize); - { - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - /* we access mParent */ - AutoReadLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - - if (m->pParent.isNull()) - { - *aLogicalSize = m->logicalSize; - - return S_OK; - } - } + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* We assume that some backend may decide to return a meaningless value in - * response to VDGetSize() for differencing media and therefore always - * ask the base medium ourselves. */ + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - /* base() will do callers/locking */ + *aLogicalSize = m->logicalSize; - return getBase()->COMGETTER(LogicalSize)(aLogicalSize); + return S_OK; } STDMETHODIMP Medium::COMGETTER(AutoReset)(BOOL *aAutoReset) @@ -1999,8 +1966,8 @@ STDMETHODIMP Medium::SetIds(BOOL aSetImageId, else { imageId = Guid(aImageId); - if (imageId.isEmpty()) - return setError(E_INVALIDARG, tr("Argument %s is empty"), "aImageId"); + if (!imageId.isValid()) + return setError(E_INVALIDARG, tr("Argument %s is invalid"), "aImageId"); } } if (aSetParentId) @@ -2060,7 +2027,7 @@ STDMETHODIMP Medium::RefreshState(MediumState_T *aState) STDMETHODIMP Medium::GetSnapshotIds(IN_BSTR aMachineId, ComSafeArrayOut(BSTR, aSnapshotIds)) { - CheckComArgExpr(aMachineId, Guid(aMachineId).isEmpty() == false); + CheckComArgExpr(aMachineId, Guid(aMachineId).isValid()); CheckComArgOutSafeArrayPointerValid(aSnapshotIds); AutoCaller autoCaller(this); @@ -2108,12 +2075,10 @@ STDMETHODIMP Medium::GetSnapshotIds(IN_BSTR aMachineId, return S_OK; } -/** - * @note @a aState may be NULL if the state value is not needed (only for - * in-process calls). - */ -STDMETHODIMP Medium::LockRead(MediumState_T *aState) +STDMETHODIMP Medium::LockRead(IToken **aToken) { + CheckComArgNotNull(aToken); + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -2137,10 +2102,6 @@ STDMETHODIMP Medium::LockRead(MediumState_T *aState) } } - /* return the current state before */ - if (aState) - *aState = m->state; - HRESULT rc = S_OK; switch (m->state) @@ -2160,6 +2121,19 @@ STDMETHODIMP Medium::LockRead(MediumState_T *aState) LogFlowThisFunc(("Okay - prev state=%d readers=%d\n", m->state, m->readers)); m->state = MediumState_LockedRead; + ComObjPtr<MediumLockToken> pToken; + rc = pToken.createObject(); + if (SUCCEEDED(rc)) + rc = pToken->init(this, false /* fWrite */); + if (FAILED(rc)) + { + --m->readers; + if (m->readers == 0) + m->state = m->preLockState; + return rc; + } + + pToken.queryInterfaceTo(aToken); break; } default: @@ -2177,7 +2151,7 @@ STDMETHODIMP Medium::LockRead(MediumState_T *aState) * @note @a aState may be NULL if the state value is not needed (only for * in-process calls). */ -STDMETHODIMP Medium::UnlockRead(MediumState_T *aState) +HRESULT Medium::unlockRead(MediumState_T *aState) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -2224,12 +2198,10 @@ STDMETHODIMP Medium::UnlockRead(MediumState_T *aState) return rc; } -/** - * @note @a aState may be NULL if the state value is not needed (only for - * in-process calls). - */ -STDMETHODIMP Medium::LockWrite(MediumState_T *aState) +STDMETHODIMP Medium::LockWrite(IToken **aToken) { + CheckComArgNotNull(aToken); + AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -2253,10 +2225,6 @@ STDMETHODIMP Medium::LockWrite(MediumState_T *aState) } } - /* return the current state before */ - if (aState) - *aState = m->state; - HRESULT rc = S_OK; switch (m->state) @@ -2268,6 +2236,18 @@ STDMETHODIMP Medium::LockWrite(MediumState_T *aState) LogFlowThisFunc(("Okay - prev state=%d locationFull=%s\n", m->state, getLocationFull().c_str())); m->state = MediumState_LockedWrite; + + ComObjPtr<MediumLockToken> pToken; + rc = pToken.createObject(); + if (SUCCEEDED(rc)) + rc = pToken->init(this, true /* fWrite */); + if (FAILED(rc)) + { + m->state = m->preLockState; + return rc; + } + + pToken.queryInterfaceTo(aToken); break; } default: @@ -2285,7 +2265,7 @@ STDMETHODIMP Medium::LockWrite(MediumState_T *aState) * @note @a aState may be NULL if the state value is not needed (only for * in-process calls). */ -STDMETHODIMP Medium::UnlockWrite(MediumState_T *aState) +HRESULT Medium::unlockWrite(MediumState_T *aState) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -2351,8 +2331,14 @@ STDMETHODIMP Medium::GetProperty(IN_BSTR aName, BSTR *aValue) settings::StringsMap::const_iterator it = m->mapProperties.find(Utf8Str(aName)); if (it == m->mapProperties.end()) - return setError(VBOX_E_OBJECT_NOT_FOUND, - tr("Property '%ls' does not exist"), aName); + { + if (!Utf8Str(aName).startsWith("Special/")) + return setError(VBOX_E_OBJECT_NOT_FOUND, + tr("Property '%ls' does not exist"), aName); + else + /* be more silent here */ + return VBOX_E_OBJECT_NOT_FOUND; + } it->second.cloneTo(aValue); @@ -2377,13 +2363,32 @@ STDMETHODIMP Medium::SetProperty(IN_BSTR aName, IN_BSTR aValue) return setStateError(); } - settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(aName)); - if (it == m->mapProperties.end()) - return setError(VBOX_E_OBJECT_NOT_FOUND, - tr("Property '%ls' does not exist"), - aName); - - it->second = aValue; + Utf8Str strName(aName); + Utf8Str strValue(aValue); + settings::StringsMap::iterator it = m->mapProperties.find(strName); + if (!strName.startsWith("Special/")) + { + if (it == m->mapProperties.end()) + return setError(VBOX_E_OBJECT_NOT_FOUND, + tr("Property '%s' does not exist"), + strName.c_str()); + it->second = strValue; + } + else + { + if (it == m->mapProperties.end()) + { + if (!strValue.isEmpty()) + m->mapProperties[strName] = strValue; + } + else + { + if (!strValue.isEmpty()) + it->second = aValue; + else + m->mapProperties.erase(it); + } + } // save the settings mlock.release(); @@ -2446,9 +2451,11 @@ STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames), i < names.size(); ++i) { - if (m->mapProperties.find(Utf8Str(names[i])) == m->mapProperties.end()) + Utf8Str strName(names[i]); + if ( !strName.startsWith("Special/") + && m->mapProperties.find(strName) == m->mapProperties.end()) return setError(VBOX_E_OBJECT_NOT_FOUND, - tr("Property '%ls' does not exist"), names[i]); + tr("Property '%s' does not exist"), strName.c_str()); } /* second pass: assign */ @@ -2456,10 +2463,29 @@ STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames), i < names.size(); ++i) { - settings::StringsMap::iterator it = m->mapProperties.find(Utf8Str(names[i])); - AssertReturn(it != m->mapProperties.end(), E_FAIL); - - it->second = Utf8Str(values[i]); + Utf8Str strName(names[i]); + Utf8Str strValue(values[i]); + settings::StringsMap::iterator it = m->mapProperties.find(strName); + if (!strName.startsWith("Special/")) + { + AssertReturn(it != m->mapProperties.end(), E_FAIL); + it->second = strValue; + } + else + { + if (it == m->mapProperties.end()) + { + if (!strValue.isEmpty()) + m->mapProperties[strName] = strValue; + } + else + { + if (!strValue.isEmpty()) + it->second = strValue; + else + m->mapProperties.erase(it); + } + } } // save the settings @@ -2471,9 +2497,10 @@ STDMETHODIMP Medium::SetProperties(ComSafeArrayIn(IN_BSTR, aNames), } STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize, - ULONG aVariant, + ComSafeArrayIn(MediumVariant_T, aVariant), IProgress **aProgress) { + CheckComArgSafeArrayNotNull(aVariant); CheckComArgOutPointerValid(aProgress); if (aLogicalSize < 0) return setError(E_INVALIDARG, tr("The medium size argument (%lld) is negative"), aLogicalSize); @@ -2482,21 +2509,32 @@ STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize, if (FAILED(autoCaller.rc())) return autoCaller.rc(); HRESULT rc = S_OK; - ComObjPtr <Progress> pProgress; + ComObjPtr<Progress> pProgress; Medium::Task *pTask = NULL; try { AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - aVariant = (MediumVariant_T)((unsigned)aVariant & (unsigned)~MediumVariant_Diff); - if ( !(aVariant & MediumVariant_Fixed) - && !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic)) + ULONG mediumVariantFlags = 0; + + if (aVariant) + { + com::SafeArray<MediumVariant_T> variants(ComSafeArrayInArg(aVariant)); + for (size_t i = 0; i < variants.size(); i++) + mediumVariantFlags |= variants[i]; + } + + mediumVariantFlags &= ((unsigned)~MediumVariant_Diff); + + if ( !(mediumVariantFlags & MediumVariant_Fixed) + && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic)) throw setError(VBOX_E_NOT_SUPPORTED, tr("Medium format '%s' does not support dynamic storage creation"), m->strFormat.c_str()); - if ( (aVariant & MediumVariant_Fixed) - && !(m->formatObj->getCapabilities() & MediumFormatCapabilities_CreateDynamic)) + + if ( (mediumVariantFlags & MediumVariant_Fixed) + && !(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_CreateDynamic)) throw setError(VBOX_E_NOT_SUPPORTED, tr("Medium format '%s' does not support fixed storage creation"), m->strFormat.c_str()); @@ -2507,7 +2545,7 @@ STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize, pProgress.createObject(); rc = pProgress->init(m->pVirtualBox, static_cast<IMedium*>(this), - (aVariant & MediumVariant_Fixed) + (mediumVariantFlags & MediumVariant_Fixed) ? BstrFmt(tr("Creating fixed medium storage unit '%s'"), m->strLocationFull.c_str()).raw() : BstrFmt(tr("Creating dynamic medium storage unit '%s'"), m->strLocationFull.c_str()).raw(), TRUE /* aCancelable */); @@ -2516,7 +2554,8 @@ STDMETHODIMP Medium::CreateBaseStorage(LONG64 aLogicalSize, /* setup task object to carry out the operation asynchronously */ pTask = new Medium::CreateBaseTask(this, pProgress, aLogicalSize, - (MediumVariant_T)aVariant); + (MediumVariant_T)mediumVariantFlags); + //(MediumVariant_T)aVariant); rc = pTask->rc(); AssertComRC(rc); if (FAILED(rc)) @@ -2560,11 +2599,12 @@ STDMETHODIMP Medium::DeleteStorage(IProgress **aProgress) } STDMETHODIMP Medium::CreateDiffStorage(IMedium *aTarget, - ULONG aVariant, - IProgress **aProgress) + ComSafeArrayIn(MediumVariant_T, aVariant), + IProgress **aProgress) { CheckComArgNotNull(aTarget); CheckComArgOutPointerValid(aProgress); + CheckComArgSafeArrayNotNull(aVariant); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -2625,9 +2665,18 @@ STDMETHODIMP Medium::CreateDiffStorage(IMedium *aTarget, alock.release(); - ComObjPtr <Progress> pProgress; + ComObjPtr<Progress> pProgress; + + ULONG mediumVariantFlags = 0; + + if (aVariant) + { + com::SafeArray<MediumVariant_T> variants(ComSafeArrayInArg(aVariant)); + for (size_t i = 0; i < variants.size(); i++) + mediumVariantFlags |= variants[i]; + } - rc = createDiffStorage(diff, (MediumVariant_T)aVariant, pMediumLockList, + rc = createDiffStorage(diff, (MediumVariant_T)mediumVariantFlags, pMediumLockList, &pProgress, false /* aWait */); if (FAILED(rc)) delete pMediumLockList; @@ -2650,21 +2699,21 @@ STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress) bool fMergeForward = false; ComObjPtr<Medium> pParentForTarget; - MediaList childrenToReparent; + MediumLockList *pChildrenToReparent = NULL; MediumLockList *pMediumLockList = NULL; HRESULT rc = S_OK; rc = prepareMergeTo(pTarget, NULL, NULL, true, fMergeForward, - pParentForTarget, childrenToReparent, pMediumLockList); + pParentForTarget, pChildrenToReparent, pMediumLockList); if (FAILED(rc)) return rc; - ComObjPtr <Progress> pProgress; + ComObjPtr<Progress> pProgress; - rc = mergeTo(pTarget, fMergeForward, pParentForTarget, childrenToReparent, + rc = mergeTo(pTarget, fMergeForward, pParentForTarget, pChildrenToReparent, pMediumLockList, &pProgress, false /* aWait */); if (FAILED(rc)) - cancelMergeTo(childrenToReparent, pMediumLockList); + cancelMergeTo(pChildrenToReparent, pMediumLockList); else pProgress.queryInterfaceTo(aProgress); @@ -2672,23 +2721,29 @@ STDMETHODIMP Medium::MergeTo(IMedium *aTarget, IProgress **aProgress) } STDMETHODIMP Medium::CloneToBase(IMedium *aTarget, - ULONG aVariant, - IProgress **aProgress) + ComSafeArrayIn(MediumVariant_T, aVariant), + IProgress **aProgress) { int rc = S_OK; CheckComArgNotNull(aTarget); CheckComArgOutPointerValid(aProgress); - rc = CloneTo(aTarget, aVariant, NULL, aProgress); + CheckComArgSafeArrayNotNull(aVariant); + + com::SafeArray<MediumVariant_T> variants(ComSafeArrayInArg(aVariant)); + + rc = CloneTo(aTarget, ComSafeArrayAsInParam(variants), NULL, aProgress); return rc; } STDMETHODIMP Medium::CloneTo(IMedium *aTarget, - ULONG aVariant, + ComSafeArrayIn(MediumVariant_T, aVariant), IMedium *aParent, IProgress **aProgress) { CheckComArgNotNull(aTarget); CheckComArgOutPointerValid(aProgress); + CheckComArgSafeArrayNotNull(aVariant); + ComAssertRet(aTarget != this, E_INVALIDARG); AutoCaller autoCaller(this); @@ -2786,9 +2841,18 @@ STDMETHODIMP Medium::CloneTo(IMedium *aTarget, throw rc; } + ULONG mediumVariantFlags = 0; + + if (aVariant) + { + com::SafeArray<MediumVariant_T> variants(ComSafeArrayInArg(aVariant)); + for (size_t i = 0; i < variants.size(); i++) + mediumVariantFlags |= variants[i]; + } + /* setup task object to carry out the operation asynchronously */ pTask = new Medium::CloneTask(this, pProgress, pTarget, - (MediumVariant_T)aVariant, + (MediumVariant_T)mediumVariantFlags, pParent, UINT32_MAX, UINT32_MAX, pSourceMediumLockList, pTargetMediumLockList); rc = pTask->rc(); @@ -2814,6 +2878,29 @@ STDMETHODIMP Medium::CloneTo(IMedium *aTarget, return rc; } +STDMETHODIMP Medium::SetLocation(IN_BSTR aLocation, IProgress **aProgress) +{ + CheckComArgStrNotEmptyOrNull(aLocation); + CheckComArgOutPointerValid(aProgress); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + /// @todo NEWMEDIA for file names, add the default extension if no extension + /// is present (using the information from the VD backend which also implies + /// that one more parameter should be passed to setLocation() requesting + /// that functionality since it is only allowed when called from this method + + /// @todo NEWMEDIA rename the file and set m->location on success, then save + /// the global registry (and local registries of portable VMs referring to + /// this medium), this will also require to add the mRegistered flag to data + + *aProgress = NULL; + ReturnComNotImplemented(); +} + STDMETHODIMP Medium::Compact(IProgress **aProgress) { CheckComArgOutPointerValid(aProgress); @@ -2822,7 +2909,7 @@ STDMETHODIMP Medium::Compact(IProgress **aProgress) if (FAILED(autoCaller.rc())) return autoCaller.rc(); HRESULT rc = S_OK; - ComObjPtr <Progress> pProgress; + ComObjPtr<Progress> pProgress; Medium::Task *pTask = NULL; try @@ -2895,7 +2982,7 @@ STDMETHODIMP Medium::Resize(LONG64 aLogicalSize, IProgress **aProgress) if (FAILED(autoCaller.rc())) return autoCaller.rc(); HRESULT rc = S_OK; - ComObjPtr <Progress> pProgress; + ComObjPtr<Progress> pProgress; Medium::Task *pTask = NULL; try @@ -2968,7 +3055,7 @@ STDMETHODIMP Medium::Reset(IProgress **aProgress) if (FAILED(autoCaller.rc())) return autoCaller.rc(); HRESULT rc = S_OK; - ComObjPtr <Progress> pProgress; + ComObjPtr<Progress> pProgress; Medium::Task *pTask = NULL; try @@ -3038,17 +3125,8 @@ STDMETHODIMP Medium::Reset(IProgress **aProgress) if (SUCCEEDED(rc)) pProgress.queryInterfaceTo(aProgress); } - else - { - /* Note: on success, the task will unlock this */ - { - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - HRESULT rc2 = UnlockWrite(NULL); - AssertComRC(rc2); - } - if (pTask != NULL) - delete pTask; - } + else if (pTask != NULL) + delete pTask; LogFlowThisFunc(("LEAVE, rc=%Rhrc\n", rc)); @@ -3150,7 +3228,7 @@ const ComObjPtr<MediumFormat>& Medium::getMediumFormat() const bool Medium::isMediumFormatFile() const { if ( m->formatObj - && (m->formatObj->getCapabilities() & MediumFormatCapabilities_File) + && (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File) ) return true; return false; @@ -3382,6 +3460,9 @@ void Medium::markRegistriesModified() llRegistryIDs = m->llRegistryIDs; } + /* Save the error information now, the implicit restore when this goes + * out of scope will throw away spurious additional errors created below. */ + ErrorInfoKeeper eik; for (GuidList::const_iterator it = llRegistryIDs.begin(); it != llRegistryIDs.end(); ++it) @@ -3400,7 +3481,7 @@ void Medium::markRegistriesModified() HRESULT Medium::addBackReference(const Guid &aMachineId, const Guid &aSnapshotId /*= Guid::Empty*/) { - AssertReturn(!aMachineId.isEmpty(), E_FAIL); + AssertReturn(aMachineId.isValid(), E_FAIL); LogFlowThisFunc(("ENTER, aMachineId: {%RTuuid}, aSnapshotId: {%RTuuid}\n", aMachineId.raw(), aSnapshotId.raw())); @@ -3442,7 +3523,8 @@ HRESULT Medium::addBackReference(const Guid &aMachineId, // if the caller has not supplied a snapshot ID, then we're attaching // to a machine a medium which represents the machine's current state, // so set the flag - if (aSnapshotId.isEmpty()) + + if (aSnapshotId.isZero()) { /* sanity: no duplicate attachments */ if (it->fInCurState) @@ -3479,7 +3561,8 @@ HRESULT Medium::addBackReference(const Guid &aMachineId, } it->llSnapshotIds.push_back(aSnapshotId); - it->fInCurState = false; + // Do not touch fInCurState, as the image may be attached to the current + // state *and* a snapshot, otherwise we lose the current state association! LogFlowThisFuncLeave(); @@ -3497,7 +3580,7 @@ HRESULT Medium::addBackReference(const Guid &aMachineId, HRESULT Medium::removeBackReference(const Guid &aMachineId, const Guid &aSnapshotId /*= Guid::Empty*/) { - AssertReturn(!aMachineId.isEmpty(), E_FAIL); + AssertReturn(aMachineId.isValid(), E_FAIL); AutoCaller autoCaller(this); AssertComRCReturnRC(autoCaller.rc()); @@ -3509,7 +3592,7 @@ HRESULT Medium::removeBackReference(const Guid &aMachineId, BackRef::EqualsTo(aMachineId)); AssertReturn(it != m->backRefs.end(), E_FAIL); - if (aSnapshotId.isEmpty()) + if (aSnapshotId.isZero()) { /* remove the current state attachment */ it->fInCurState = false; @@ -4091,7 +4174,7 @@ Utf8Str Medium::getPreferredDiffFormat() AssertComRCReturn(autoCaller.rc(), Utf8Str::Empty); /* check that our own format supports diffs */ - if (!(m->formatObj->getCapabilities() & MediumFormatCapabilities_Differencing)) + if (!(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_Differencing)) { /* use the default format if not */ Utf8Str tmp; @@ -4224,7 +4307,7 @@ HRESULT Medium::deleteStorage(ComObjPtr<Progress> *aProgress, COMMA_LOCKVAL_SRC_POS); LogFlowThisFunc(("aWait=%RTbool locationFull=%s\n", aWait, getLocationFull().c_str() )); - if ( !(m->formatObj->getCapabilities() & ( MediumFormatCapabilities_CreateDynamic + if ( !(m->formatObj->i_getCapabilities() & ( MediumFormatCapabilities_CreateDynamic | MediumFormatCapabilities_CreateFixed))) throw setError(VBOX_E_NOT_SUPPORTED, tr("Medium format '%s' does not support storage deletion"), @@ -4451,6 +4534,112 @@ HRESULT Medium::unmarkLockedForDeletion() } /** + * Queries the preferred merge direction from this to the other medium, i.e. + * the one which requires the least amount of I/O and therefore time and + * disk consumption. + * + * @returns Status code. + * @retval E_FAIL in case determining the merge direction fails for some reason, + * for example if getting the size of the media fails. There is no + * error set though and the caller is free to continue to find out + * what was going wrong later. Leaves fMergeForward unset. + * @retval VBOX_E_INVALID_OBJECT_STATE if both media are not related to each other + * An error is set. + * @param pOther The other medium to merge with. + * @param fMergeForward Resulting preferred merge direction (out). + */ +HRESULT Medium::queryPreferredMergeDirection(const ComObjPtr<Medium> &pOther, + bool &fMergeForward) +{ + AssertReturn(pOther != NULL, E_FAIL); + AssertReturn(pOther != this, E_FAIL); + + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + AutoCaller otherCaller(pOther); + AssertComRCReturnRC(otherCaller.rc()); + + HRESULT rc = S_OK; + bool fThisParent = false; /**<< Flag whether this medium is the parent of pOther. */ + + try + { + // locking: we need the tree lock first because we access parent pointers + AutoWriteLock treeLock(m->pVirtualBox->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); + + /* more sanity checking and figuring out the current merge direction */ + ComObjPtr<Medium> pMedium = getParent(); + while (!pMedium.isNull() && pMedium != pOther) + pMedium = pMedium->getParent(); + if (pMedium == pOther) + fThisParent = false; + else + { + pMedium = pOther->getParent(); + while (!pMedium.isNull() && pMedium != this) + pMedium = pMedium->getParent(); + if (pMedium == this) + fThisParent = true; + else + { + Utf8Str tgtLoc; + { + AutoReadLock alock(pOther COMMA_LOCKVAL_SRC_POS); + tgtLoc = pOther->getLocationFull(); + } + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + throw setError(VBOX_E_INVALID_OBJECT_STATE, + tr("Media '%s' and '%s' are unrelated"), + m->strLocationFull.c_str(), tgtLoc.c_str()); + } + } + + /* + * Figure out the preferred merge direction. The current way is to + * get the current sizes of file based images and select the merge + * direction depending on the size. + * + * Can't use the VD API to get current size here as the media might + * be write locked by a running VM. Resort to RTFileQuerySize(). + */ + int vrc = VINF_SUCCESS; + uint64_t cbMediumThis = 0; + uint64_t cbMediumOther = 0; + + if (isMediumFormatFile() && pOther->isMediumFormatFile()) + { + vrc = RTFileQuerySize(this->getLocationFull().c_str(), &cbMediumThis); + if (RT_SUCCESS(vrc)) + { + vrc = RTFileQuerySize(pOther->getLocationFull().c_str(), + &cbMediumOther); + } + + if (RT_FAILURE(vrc)) + rc = E_FAIL; + else + { + /* + * Check which merge direction might be more optimal. + * This method is not bullet proof of course as there might + * be overlapping blocks in the images so the file size is + * not the best indicator but it is good enough for our purpose + * and everything else is too complicated, especially when the + * media are used by a running VM. + */ + bool fMergeIntoThis = cbMediumThis > cbMediumOther; + fMergeForward = fMergeIntoThis ^ fThisParent; + } + } + } + catch (HRESULT aRC) { rc = aRC; } + + return rc; +} + +/** * Prepares this (source) medium, target medium and all intermediate media * for the merge operation. * @@ -4470,8 +4659,9 @@ HRESULT Medium::unmarkLockedForDeletion() * later you must call #cancelMergeTo(). * @param fMergeForward Resulting merge direction (out). * @param pParentForTarget New parent for target medium after merge (out). - * @param aChildrenToReparent List of children of the source which will have - * to be reparented to the target after merge (out). + * @param aChildrenToReparent Medium lock list containing all children of the + * source which will have to be reparented to the target + * after merge (out). * @param aMediumLockList Medium locking information (out). * * @note Locks medium tree for reading. Locks this object, aTarget and all @@ -4483,7 +4673,7 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget, bool fLockMedia, bool &fMergeForward, ComObjPtr<Medium> &pParentForTarget, - MediaList &aChildrenToReparent, + MediumLockList * &aChildrenToReparent, MediumLockList * &aMediumLockList) { AssertReturn(pTarget != NULL, E_FAIL); @@ -4498,7 +4688,8 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget, HRESULT rc = S_OK; fMergeForward = false; pParentForTarget.setNull(); - aChildrenToReparent.clear(); + Assert(aChildrenToReparent == NULL); + aChildrenToReparent = NULL; Assert(aMediumLockList == NULL); aMediumLockList = NULL; @@ -4587,9 +4778,9 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget, if ( m->backRefs.size() != 0 && ( !aMachineId || m->backRefs.size() != 1 - || aMachineId->isEmpty() + || aMachineId->isZero() || *getFirstMachineBackrefId() != *aMachineId - || ( (!aSnapshotId || !aSnapshotId->isEmpty()) + || ( (!aSnapshotId || !aSnapshotId->isZero()) && *getFirstMachineBackrefSnapshotId() != *aSnapshotId))) throw setError(VBOX_E_OBJECT_IN_USE, tr("Medium '%s' is attached to %d virtual machines"), @@ -4681,19 +4872,21 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget, else { /* we will need to reparent children of the source */ + aChildrenToReparent = new MediumLockList(); for (MediaList::const_iterator it = getChildren().begin(); it != getChildren().end(); ++it) { pMedium = *it; - if (fLockMedia) - { - rc = pMedium->LockWrite(NULL); - if (FAILED(rc)) - throw rc; - } - - aChildrenToReparent.push_back(pMedium); + aChildrenToReparent->Append(pMedium, true /* fLockWrite */); + } + if (fLockMedia && aChildrenToReparent) + { + treeLock.release(); + rc = aChildrenToReparent->Lock(); + treeLock.acquire(); + if (FAILED(rc)) + throw rc; } } for (pLast = pLastIntermediate; @@ -4752,8 +4945,16 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget, if (FAILED(rc)) { - delete aMediumLockList; - aMediumLockList = NULL; + if (aMediumLockList) + { + delete aMediumLockList; + aMediumLockList = NULL; + } + if (aChildrenToReparent) + { + delete aChildrenToReparent; + aChildrenToReparent = NULL; + } } return rc; @@ -4838,9 +5039,9 @@ HRESULT Medium::prepareMergeTo(const ComObjPtr<Medium> &pTarget, HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget, bool fMergeForward, const ComObjPtr<Medium> &pParentForTarget, - const MediaList &aChildrenToReparent, + MediumLockList *aChildrenToReparent, MediumLockList *aMediumLockList, - ComObjPtr <Progress> *aProgress, + ComObjPtr<Progress> *aProgress, bool aWait) { AssertReturn(pTarget != NULL, E_FAIL); @@ -4855,7 +5056,7 @@ HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget, AssertComRCReturnRC(targetCaller.rc()); HRESULT rc = S_OK; - ComObjPtr <Progress> pProgress; + ComObjPtr<Progress> pProgress; Medium::Task *pTask = NULL; try @@ -4927,7 +5128,7 @@ HRESULT Medium::mergeTo(const ComObjPtr<Medium> &pTarget, * * @note Locks the media from the chain for writing. */ -void Medium::cancelMergeTo(const MediaList &aChildrenToReparent, +void Medium::cancelMergeTo(MediumLockList *aChildrenToReparent, MediumLockList *aMediumLockList) { AutoCaller autoCaller(this); @@ -4959,23 +5160,17 @@ void Medium::cancelMergeTo(const MediaList &aChildrenToReparent, /* the destructor will do the work */ delete aMediumLockList; - /* unlock the children which had to be reparented */ - for (MediaList::const_iterator it = aChildrenToReparent.begin(); - it != aChildrenToReparent.end(); - ++it) - { - const ComObjPtr<Medium> &pMedium = *it; - - AutoWriteLock alock(pMedium COMMA_LOCKVAL_SRC_POS); - pMedium->UnlockWrite(NULL); - } + /* unlock the children which had to be reparented, the destructor will do + * the work */ + if (aChildrenToReparent) + delete aChildrenToReparent; } /** * Fix the parent UUID of all children to point to this medium as their * parent. */ -HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent) +HRESULT Medium::fixParentUuidOfChildren(MediumLockList *pChildrenToReparent) { Assert(!isWriteLockOnCurrentThread()); Assert(!m->pVirtualBox->getMediaTreeLockHandle().isWriteLockOnCurrentThread()); @@ -5016,16 +5211,19 @@ HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent) throw vrc; } - for (MediaList::const_iterator it = childrenToReparent.begin(); - it != childrenToReparent.end(); + MediumLockList::Base::iterator childrenBegin = pChildrenToReparent->GetBegin(); + MediumLockList::Base::iterator childrenEnd = pChildrenToReparent->GetEnd(); + for (MediumLockList::Base::iterator it = childrenBegin; + it != childrenEnd; ++it) { + Medium *pMedium = it->GetMedium(); /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */ vrc = VDOpen(hdd, - (*it)->m->strFormat.c_str(), - (*it)->m->strLocationFull.c_str(), + pMedium->m->strFormat.c_str(), + pMedium->m->strLocationFull.c_str(), VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef, - (*it)->m->vdImageIfaces); + pMedium->m->vdImageIfaces); if (RT_FAILURE(vrc)) throw vrc; @@ -5036,8 +5234,6 @@ HRESULT Medium::fixParentUuidOfChildren(const MediaList &childrenToReparent) vrc = VDClose(hdd, false /* fDelete */); if (RT_FAILURE(vrc)) throw vrc; - - (*it)->UnlockWrite(NULL); } } catch (HRESULT aRC) { rc = aRC; } @@ -5433,7 +5629,7 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) /* are we dealing with a new medium constructed using the existing * location? */ - bool isImport = m->id.isEmpty(); + bool isImport = m->id.isZero(); unsigned uOpenFlags = VD_OPEN_FLAGS_INFO; /* Note that we don't use VD_OPEN_FLAGS_READONLY when opening new @@ -5453,10 +5649,11 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) /* Lock the medium, which makes the behavior much more consistent */ alock.release(); + ComPtr<IToken> pToken; if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE)) - rc = LockRead(NULL); + rc = LockRead(pToken.asOutParam()); else - rc = LockWrite(NULL); + rc = LockWrite(pToken.asOutParam()); if (FAILED(rc)) return rc; alock.acquire(); @@ -5524,7 +5721,7 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) throw S_OK; } - if (formatObj->getCapabilities() & MediumFormatCapabilities_Uuid) + if (formatObj->i_getCapabilities() & MediumFormatCapabilities_Uuid) { /* Modify the UUIDs if necessary. The associated fields are * not modified by other code, so no need to copy. */ @@ -5533,7 +5730,12 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) alock.acquire(); vrc = VDSetUuid(hdd, 0, m->uuidImage.raw()); alock.release(); - ComAssertRCThrow(vrc, E_FAIL); + if (RT_FAILURE(vrc)) + { + lastAccessError = Utf8StrFmt(tr("Could not update the UUID of medium '%s'%s"), + location.c_str(), vdError(vrc).c_str()); + throw S_OK; + } mediumId = m->uuidImage; } if (fSetParentId) @@ -5541,7 +5743,12 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) alock.acquire(); vrc = VDSetParentUuid(hdd, 0, m->uuidParentImage.raw()); alock.release(); - ComAssertRCThrow(vrc, E_FAIL); + if (RT_FAILURE(vrc)) + { + lastAccessError = Utf8StrFmt(tr("Could not update the parent UUID of medium '%s'%s"), + location.c_str(), vdError(vrc).c_str()); + throw S_OK; + } } /* zap the information, these are no long-term members */ alock.acquire(); @@ -5558,13 +5765,13 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) { mediumId = uuid; - if (mediumId.isEmpty() && (m->hddOpenMode == OpenReadOnly)) + if (mediumId.isZero() && (m->hddOpenMode == OpenReadOnly)) // only when importing a VDMK that has no UUID, create one in memory mediumId.create(); } else { - Assert(!mediumId.isEmpty()); + Assert(!mediumId.isZero()); if (mediumId != uuid) { @@ -5626,7 +5833,7 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) * Since such images don't support random writes they will not * be created for diff images. Only an overly smart user might * manually create this case. Too bad for him. */ - if ( isImport + if ( (isImport || fSetParentId) && !(uImageFlags & VD_VMDK_IMAGE_FLAGS_STREAM_OPTIMIZED)) { /* the parent must be known to us. Note that we freely @@ -5636,24 +5843,43 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) * threads yet (and init() will fail if this method reports * MediumState_Inaccessible) */ - Guid id = parentId; ComObjPtr<Medium> pParent; - rc = m->pVirtualBox->findHardDiskById(id, false /* aSetError */, &pParent); + if (RTUuidIsNull(&parentId)) + rc = VBOX_E_OBJECT_NOT_FOUND; + else + rc = m->pVirtualBox->findHardDiskById(Guid(parentId), false /* aSetError */, &pParent); if (FAILED(rc)) { - lastAccessError = Utf8StrFmt( - tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"), - &parentId, location.c_str(), - m->pVirtualBox->settingsFilePath().c_str()); - throw S_OK; + if (fSetImageId && !fSetParentId) + { + /* If the image UUID gets changed for an existing + * image then the parent UUID can be stale. In such + * cases clear the parent information. The parent + * information may/will be re-set later if the + * API client wants to adjust a complete medium + * hierarchy one by one. */ + rc = S_OK; + alock.acquire(); + RTUuidClear(&parentId); + vrc = VDSetParentUuid(hdd, 0, &parentId); + alock.release(); + ComAssertRCThrow(vrc, E_FAIL); + } + else + { + lastAccessError = Utf8StrFmt(tr("Parent medium with UUID {%RTuuid} of the medium '%s' is not found in the media registry ('%s')"), + &parentId, location.c_str(), + m->pVirtualBox->settingsFilePath().c_str()); + throw S_OK; + } } /* we set mParent & children() */ treeLock.acquire(); - Assert(m->pParent.isNull()); - m->pParent = pParent; - m->pParent->m->llChildren.push_back(this); + if (m->pParent) + deparent(); + setParent(pParent); treeLock.release(); } @@ -5765,13 +5991,9 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) else m->preLockState = MediumState_Inaccessible; - HRESULT rc2; - if (uOpenFlags & (VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SHAREABLE)) - rc2 = UnlockRead(NULL); - else - rc2 = UnlockWrite(NULL); - if (SUCCEEDED(rc) && FAILED(rc2)) - rc = rc2; + pToken->Abandon(); + pToken.setNull(); + if (FAILED(rc)) return rc; /* If this is a base image which incorrectly has a parent UUID set, @@ -5782,7 +6004,7 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) * the diff. Not acceptable. */ if (fRepairImageZeroParentUuid) { - rc = LockWrite(NULL); + rc = LockWrite(pToken.asOutParam()); if (FAILED(rc)) return rc; alock.release(); @@ -5820,9 +6042,8 @@ HRESULT Medium::queryInfo(bool fSetImageId, bool fSetParentId) rc = aRC; } - rc = UnlockWrite(NULL); - if (SUCCEEDED(rc) && FAILED(rc2)) - rc = rc2; + pToken->Abandon(); + pToken.setNull(); if (FAILED(rc)) return rc; } @@ -6013,7 +6234,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation, AssertReturn( (!m->strFormat.isEmpty() && !m->formatObj.isNull()) || ( autoCaller.state() == InInit && m->state != MediumState_NotCreated - && m->id.isEmpty() + && m->id.isZero() && m->strFormat.isEmpty() && m->formatObj.isNull()), E_FAIL); @@ -6023,7 +6244,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation, bool isImport = m->strFormat.isEmpty(); if ( isImport - || ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File) + || ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File) && !m->hostDrive)) { Guid id; @@ -6033,7 +6254,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation, if (m->state == MediumState_NotCreated) { /* must be a file (formatObj must be already known) */ - Assert(m->formatObj->getCapabilities() & MediumFormatCapabilities_File); + Assert(m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File); if (RTPathFilename(aLocation.c_str()) == NULL) { @@ -6041,11 +6262,11 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation, * slash), generate a new UUID + file name if the state allows * this */ - ComAssertMsgRet(!m->formatObj->getFileExtensions().empty(), + ComAssertMsgRet(!m->formatObj->i_getFileExtensions().empty(), ("Must be at least one extension if it is MediumFormatCapabilities_File\n"), E_FAIL); - Utf8Str strExt = m->formatObj->getFileExtensions().front(); + Utf8Str strExt = m->formatObj->i_getFileExtensions().front(); ComAssertMsgRet(!strExt.isEmpty(), ("Default extension must not be empty\n"), E_FAIL); @@ -6059,7 +6280,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation, // we must always have full paths now (if it refers to a file) if ( ( m->formatObj.isNull() - || m->formatObj->getCapabilities() & MediumFormatCapabilities_File) + || m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File) && !RTPathStartsWithRoot(locationFull.c_str())) return setError(VBOX_E_FILE_ERROR, tr("The given path '%s' is not fully qualified"), @@ -6145,7 +6366,7 @@ HRESULT Medium::setLocation(const Utf8Str &aLocation, m->strLocationFull = locationFull; /* is it still a file? */ - if ( (m->formatObj->getCapabilities() & MediumFormatCapabilities_File) + if ( (m->formatObj->i_getCapabilities() & MediumFormatCapabilities_File) && (m->state == MediumState_NotCreated) ) /* assign a new UUID (this UUID will be used when calling @@ -6193,8 +6414,8 @@ HRESULT Medium::setFormat(const Utf8Str &aFormat) Assert(m->mapProperties.empty()); - for (MediumFormat::PropertyList::const_iterator it = m->formatObj->getProperties().begin(); - it != m->formatObj->getProperties().end(); + for (MediumFormat::PropertyArray::const_iterator it = m->formatObj->i_getProperties().begin(); + it != m->formatObj->i_getProperties().end(); ++it) { m->mapProperties.insert(std::make_pair(it->strName, Utf8Str::Empty)); @@ -6572,7 +6793,8 @@ HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task) /* The object may request a specific UUID (through a special form of * the setLocation() argument). Otherwise we have to generate it */ Guid id = m->id; - fGenerateUuid = id.isEmpty(); + + fGenerateUuid = id.isZero(); if (fGenerateUuid) { id.create(); @@ -6582,7 +6804,7 @@ HRESULT Medium::taskCreateBaseHandler(Medium::CreateBaseTask &task) Utf8Str format(m->strFormat); Utf8Str location(m->strLocationFull); - uint64_t capabilities = m->formatObj->getCapabilities(); + uint64_t capabilities = m->formatObj->i_getCapabilities(); ComAssertThrow(capabilities & ( MediumFormatCapabilities_CreateFixed | MediumFormatCapabilities_CreateDynamic), E_FAIL); Assert(m->state == MediumState_Creating); @@ -6709,7 +6931,8 @@ HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task) /* The object may request a specific UUID (through a special form of * the setLocation() argument). Otherwise we have to generate it */ Guid targetId = pTarget->m->id; - fGenerateUuid = targetId.isEmpty(); + + fGenerateUuid = targetId.isZero(); if (fGenerateUuid) { targetId.create(); @@ -6721,7 +6944,7 @@ HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task) Utf8Str targetFormat(pTarget->m->strFormat); Utf8Str targetLocation(pTarget->m->strLocationFull); - uint64_t capabilities = pTarget->m->formatObj->getCapabilities(); + uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities(); ComAssertThrow(capabilities & MediumFormatCapabilities_CreateDynamic, E_FAIL); Assert(pTarget->m->state == MediumState_Creating); @@ -6762,7 +6985,7 @@ HRESULT Medium::taskCreateDiffHandler(Medium::CreateDiffTask &task) vrc = VDOpen(hdd, pMedium->m->strFormat.c_str(), pMedium->m->strLocationFull.c_str(), - VD_OPEN_FLAGS_READONLY | m->uOpenFlagsDef, + VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef, pMedium->m->vdImageIfaces); if (RT_FAILURE(vrc)) throw setError(VBOX_E_FILE_ERROR, @@ -6988,18 +7211,21 @@ HRESULT Medium::taskMergeHandler(Medium::MergeTask &task) /* we need to update UUIDs of all source's children * which cannot be part of the container at once so * add each one in there individually */ - if (task.mChildrenToReparent.size() > 0) + if (task.mpChildrenToReparent) { - for (MediaList::const_iterator it = task.mChildrenToReparent.begin(); - it != task.mChildrenToReparent.end(); + MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin(); + MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd(); + for (MediumLockList::Base::iterator it = childrenBegin; + it != childrenEnd; ++it) { + Medium *pMedium = it->GetMedium(); /* VD_OPEN_FLAGS_INFO since UUID is wrong yet */ vrc = VDOpen(hdd, - (*it)->m->strFormat.c_str(), - (*it)->m->strLocationFull.c_str(), + pMedium->m->strFormat.c_str(), + pMedium->m->strLocationFull.c_str(), VD_OPEN_FLAGS_INFO | m->uOpenFlagsDef, - (*it)->m->vdImageIfaces); + pMedium->m->vdImageIfaces); if (RT_FAILURE(vrc)) throw vrc; @@ -7011,8 +7237,6 @@ HRESULT Medium::taskMergeHandler(Medium::MergeTask &task) vrc = VDClose(hdd, false /* fDelete */); if (RT_FAILURE(vrc)) throw vrc; - - (*it)->UnlockWrite(NULL); } } } @@ -7075,16 +7299,18 @@ HRESULT Medium::taskMergeHandler(Medium::MergeTask &task) /* reparent source's children and disconnect the deleted * branch at the younger end */ - if (task.mChildrenToReparent.size() > 0) + if (task.mpChildrenToReparent) { /* obey {parent,child} lock order */ AutoWriteLock sourceLock(this COMMA_LOCKVAL_SRC_POS); - for (MediaList::const_iterator it = task.mChildrenToReparent.begin(); - it != task.mChildrenToReparent.end(); - it++) + MediumLockList::Base::iterator childrenBegin = task.mpChildrenToReparent->GetBegin(); + MediumLockList::Base::iterator childrenEnd = task.mpChildrenToReparent->GetEnd(); + for (MediumLockList::Base::iterator it = childrenBegin; + it != childrenEnd; + ++it) { - Medium *pMedium = *it; + Medium *pMedium = it->GetMedium(); AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS); pMedium->deparent(); // removes pMedium from source @@ -7170,10 +7396,7 @@ HRESULT Medium::taskMergeHandler(Medium::MergeTask &task) * in the mergeTo() docs. The latter also implies that we * don't own the merge chain, so release it in this case. */ if (task.isAsync()) - { - Assert(task.mChildrenToReparent.size() == 0); - cancelMergeTo(task.mChildrenToReparent, task.mpMediumLockList); - } + cancelMergeTo(task.mpChildrenToReparent, task.mpMediumLockList); } return mrc; @@ -7213,7 +7436,8 @@ HRESULT Medium::taskCloneHandler(Medium::CloneTask &task) /* The object may request a specific UUID (through a special form of * the setLocation() argument). Otherwise we have to generate it */ Guid targetId = pTarget->m->id; - fGenerateUuid = targetId.isEmpty(); + + fGenerateUuid = targetId.isZero(); if (fGenerateUuid) { targetId.create(); @@ -7258,7 +7482,7 @@ HRESULT Medium::taskCloneHandler(Medium::CloneTask &task) Utf8Str targetFormat(pTarget->m->strFormat); Utf8Str targetLocation(pTarget->m->strLocationFull); - uint64_t capabilities = pTarget->m->formatObj->getCapabilities(); + uint64_t capabilities = pTarget->m->formatObj->i_getCapabilities(); Assert( pTarget->m->state == MediumState_Creating || pTarget->m->state == MediumState_LockedWrite); @@ -7407,7 +7631,8 @@ HRESULT Medium::taskCloneHandler(Medium::CloneTask &task) ComObjPtr<Medium> pMedium; mrc = pParent->m->pVirtualBox->registerMedium(pTarget, &pMedium, DeviceType_HardDisk); - Assert(pTarget == pMedium); + Assert( FAILED(mrc) + || pTarget == pMedium); eik.fetch(); if (FAILED(mrc)) @@ -7421,7 +7646,8 @@ HRESULT Medium::taskCloneHandler(Medium::CloneTask &task) ComObjPtr<Medium> pMedium; mrc = m->pVirtualBox->registerMedium(pTarget, &pMedium, DeviceType_HardDisk); - Assert(pTarget == pMedium); + Assert( FAILED(mrc) + || pTarget == pMedium); eik.fetch(); } } @@ -7672,15 +7898,8 @@ HRESULT Medium::taskResetHandler(Medium::ResetTask &task) m->logicalSize = logicalSize; m->variant = variant; - if (task.isAsync()) - { - /* unlock ourselves when done */ - HRESULT rc2 = UnlockWrite(NULL); - AssertComRC(rc2); - } - - /* Note that in sync mode, it's the caller's responsibility to - * unlock the medium. */ + /* Everything is explicitly unlocked when the task exits, + * as the task destruction also destroys the media chain. */ return rc; } @@ -7736,7 +7955,7 @@ HRESULT Medium::taskCompactHandler(Medium::CompactTask &task) vrc = VDOpen(hdd, pMedium->m->strFormat.c_str(), pMedium->m->strLocationFull.c_str(), - m->uOpenFlagsDef | (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY, + m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY), pMedium->m->vdImageIfaces); if (RT_FAILURE(vrc)) throw setError(VBOX_E_FILE_ERROR, @@ -7792,13 +8011,14 @@ HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task) { HRESULT rc = S_OK; - /* Lock all in {parent,child} order. The lock is also used as a - * signal from the task initiator (which releases it only after - * RTThreadCreate()) that we can start the job. */ - AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS); + uint64_t size = 0, logicalSize = 0; try { + /* The lock is also used as a signal from the task initiator (which + * releases it only after RTThreadCreate()) that we can start the job */ + AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS); + PVBOXHDD hdd; int vrc = VDCreate(m->vdDiskIfaces, convertDeviceType(), &hdd); ComAssertRCThrow(vrc, E_FAIL); @@ -7833,7 +8053,7 @@ HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task) vrc = VDOpen(hdd, pMedium->m->strFormat.c_str(), pMedium->m->strLocationFull.c_str(), - m->uOpenFlagsDef | (it == mediumListLast) ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY, + m->uOpenFlagsDef | (it == mediumListLast ? VD_OPEN_FLAGS_NORMAL : VD_OPEN_FLAGS_READONLY), pMedium->m->vdImageIfaces); if (RT_FAILURE(vrc)) throw setError(VBOX_E_FILE_ERROR, @@ -7867,6 +8087,8 @@ HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task) location.c_str(), vdError(vrc).c_str()); } + size = VDGetFileSize(hdd, VD_LAST_IMAGE); + logicalSize = VDGetSize(hdd, VD_LAST_IMAGE); } catch (HRESULT aRC) { rc = aRC; } @@ -7874,6 +8096,13 @@ HRESULT Medium::taskResizeHandler(Medium::ResizeTask &task) } catch (HRESULT aRC) { rc = aRC; } + if (SUCCEEDED(rc)) + { + AutoWriteLock thisLock(this COMMA_LOCKVAL_SRC_POS); + m->size = size; + m->logicalSize = logicalSize; + } + /* Everything is explicitly unlocked when the task exits, * as the task destruction also destroys the media chain. */ @@ -7936,9 +8165,9 @@ HRESULT Medium::taskExportHandler(Medium::ExportTask &task) vdError(vrc).c_str()); } - Utf8Str targetFormat(task.mFormat->getId()); + Utf8Str targetFormat(task.mFormat->i_getId()); Utf8Str targetLocation(task.mFilename); - uint64_t capabilities = task.mFormat->getCapabilities(); + uint64_t capabilities = task.mFormat->i_getCapabilities(); Assert(m->state == MediumState_LockedRead); @@ -8031,7 +8260,8 @@ HRESULT Medium::taskImportHandler(Medium::ImportTask &task) /* The object may request a specific UUID (through a special form of * the setLocation() argument). Otherwise we have to generate it */ Guid targetId = m->id; - fGenerateUuid = targetId.isEmpty(); + + fGenerateUuid = targetId.isZero(); if (fGenerateUuid) { targetId.create(); @@ -8048,7 +8278,7 @@ HRESULT Medium::taskImportHandler(Medium::ImportTask &task) { /* Open source medium. */ vrc = VDOpen(hdd, - task.mFormat->getId().c_str(), + task.mFormat->i_getId().c_str(), task.mFilename.c_str(), VD_OPEN_FLAGS_READONLY | VD_OPEN_FLAGS_SEQUENTIAL | m->uOpenFlagsDef, task.mVDImageIfaces); @@ -8060,7 +8290,7 @@ HRESULT Medium::taskImportHandler(Medium::ImportTask &task) Utf8Str targetFormat(m->strFormat); Utf8Str targetLocation(m->strLocationFull); - uint64_t capabilities = task.mFormat->getCapabilities(); + uint64_t capabilities = task.mFormat->i_getCapabilities(); Assert( m->state == MediumState_Creating || m->state == MediumState_LockedWrite); diff --git a/src/VBox/Main/src-server/MediumLock.cpp b/src/VBox/Main/src-server/MediumLock.cpp index b8b601ca..567973d4 100644 --- a/src/VBox/Main/src-server/MediumLock.cpp +++ b/src/VBox/Main/src-server/MediumLock.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -74,7 +74,12 @@ bool MediumLock::GetLockRequest() const return mLockWrite; } -HRESULT MediumLock::Lock() +bool MediumLock::IsLocked() const +{ + return mIsLocked; +} + +HRESULT MediumLock::Lock(bool aIgnoreLockedMedia) { if (mIsLocked) return S_OK; @@ -101,9 +106,20 @@ HRESULT MediumLock::Lock() break; default: if (mLockWrite) - rc = mMedium->LockWrite(NULL); + { + if (aIgnoreLockedMedia && ( state == MediumState_LockedRead + || state == MediumState_LockedWrite)) + return S_OK; + else + rc = mMedium->LockWrite(mToken.asOutParam()); + } else - rc = mMedium->LockRead(NULL); + { + if (aIgnoreLockedMedia && state == MediumState_LockedWrite) + return S_OK; + else + rc = mMedium->LockRead(mToken.asOutParam()); + } } if (SUCCEEDED(rc)) { @@ -120,12 +136,10 @@ HRESULT MediumLock::Lock() HRESULT MediumLock::Unlock() { HRESULT rc = S_OK; - if (mIsLocked && !mLockSkipped) + if (mIsLocked && !mLockSkipped && mToken) { - if (mLockWrite) - rc = mMedium->UnlockWrite(NULL); - else - rc = mMedium->UnlockRead(NULL); + mToken->Abandon(); + mToken.setNull(); } mMediumCaller.attach(NULL); mLockSkipped = false; @@ -201,7 +215,7 @@ MediumLockList::Base::iterator MediumLockList::GetEnd() return mMediumLocks.end(); } -HRESULT MediumLockList::Lock() +HRESULT MediumLockList::Lock(bool fSkipOverLockedMedia /* = false */) { if (mIsLocked) return S_OK; @@ -210,7 +224,7 @@ HRESULT MediumLockList::Lock() it != mMediumLocks.end(); it++) { - rc = it->Lock(); + rc = it->Lock(fSkipOverLockedMedia); if (FAILED(rc)) { for (MediumLockList::Base::iterator it2 = mMediumLocks.begin(); @@ -288,7 +302,9 @@ HRESULT MediumLockListMap::Remove(const ComObjPtr<MediumAttachment> &aMediumAtta MediumLockListMap::Base::iterator it = mMediumLocks.find(aMediumAttachment); if (it == mMediumLocks.end()) return VBOX_E_INVALID_OBJECT_STATE; + MediumLockList *pMediumLockList = it->second; mMediumLocks.erase(it); + delete pMediumLockList; return S_OK; } diff --git a/src/VBox/Main/src-server/NATEngineImpl.cpp b/src/VBox/Main/src-server/NATEngineImpl.cpp index 7ce08fd8..e58e533a 100644 --- a/src/VBox/Main/src-server/NATEngineImpl.cpp +++ b/src/VBox/Main/src-server/NATEngineImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010-2012 Oracle Corporation + * Copyright (C) 2010-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -26,6 +26,7 @@ #include <VBox/err.h> #include <VBox/settings.h> +#include <VBox/com/array.h> // constructor / destructor @@ -338,6 +339,7 @@ NATEngine::RemoveRedirect(IN_BSTR aName) mNATRules.erase(it); mParent->setModified(Machine::IsModified_NetworkAdapters); m_fModified = true; + mData.commit(); alock.release(); mParent->onNATRedirectRuleChange(ulSlot, TRUE, aName, proto, Bstr(strHostIP).raw(), u16HostPort, Bstr(strGuestIP).raw(), u16GuestPort); return S_OK; diff --git a/src/VBox/Main/src-server/NATNetworkImpl.cpp b/src/VBox/Main/src-server/NATNetworkImpl.cpp new file mode 100644 index 00000000..65327250 --- /dev/null +++ b/src/VBox/Main/src-server/NATNetworkImpl.cpp @@ -0,0 +1,1115 @@ +/* $Id: NATNetworkImpl.cpp $ */ +/** @file + * INATNetwork implementation. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <string> +#include "NetworkServiceRunner.h" +#include "DHCPServerImpl.h" +#include "NATNetworkImpl.h" +#include "AutoCaller.h" +#include "Logging.h" + +#include <iprt/asm.h> +#include <iprt/cpp/utils.h> +#include <iprt/cidr.h> +#include <iprt/net.h> +#include <VBox/com/array.h> +#include <VBox/com/ptr.h> +#include <VBox/settings.h> + +#include "EventImpl.h" +#include "VBoxEvents.h" + +#include "VirtualBoxImpl.h" +#include <algorithm> +#include <list> + +#ifndef RT_OS_WINDOWS +# include <netinet/in.h> +#else +# define IN_LOOPBACKNET 127 +#endif + + +// constructor / destructor +///////////////////////////////////////////////////////////////////////////// + +struct NATNetwork::Data +{ + Data() + : fEnabled(TRUE) + , fIPv6Enabled(FALSE) + , fAdvertiseDefaultIPv6Route(FALSE) + , fNeedDhcpServer(TRUE) + , u32LoopbackIp6(0) + , offGateway(0) + , offDhcp(0) + { + IPv4Gateway.setNull(); + IPv4NetworkCidr.setNull(); + IPv6Prefix.setNull(); + IPv4DhcpServer.setNull(); + IPv4NetworkMask.setNull(); + IPv4DhcpServerLowerIp.setNull(); + IPv4DhcpServerUpperIp.setNull(); + } + virtual ~Data(){} + const ComObjPtr<EventSource> pEventSource; +#ifdef VBOX_WITH_NAT_SERVICE + NATNetworkServiceRunner NATRunner; + ComObjPtr<IDHCPServer> dhcpServer; +#endif + Bstr IPv4Gateway; + Bstr IPv4NetworkCidr; + Bstr IPv4NetworkMask; + Bstr IPv4DhcpServer; + Bstr IPv4DhcpServerLowerIp; + Bstr IPv4DhcpServerUpperIp; + BOOL fEnabled; + BOOL fIPv6Enabled; + Bstr IPv6Prefix; + BOOL fAdvertiseDefaultIPv6Route; + BOOL fNeedDhcpServer; + NATRuleMap mapName2PortForwardRule4; + NATRuleMap mapName2PortForwardRule6; + settings::NATLoopbackOffsetList llNATLoopbackOffsetList; + uint32_t u32LoopbackIp6; + uint32_t offGateway; + uint32_t offDhcp; +}; + + +NATNetwork::NATNetwork() + : mVirtualBox(NULL) +{ +} + + +NATNetwork::~NATNetwork() +{ +} + + +HRESULT NATNetwork::FinalConstruct() +{ + return BaseFinalConstruct(); +} + + +void NATNetwork::FinalRelease() +{ + uninit (); + + BaseFinalRelease(); +} + + +void NATNetwork::uninit() +{ + /* Enclose the state transition Ready->InUninit->NotReady */ + AutoUninitSpan autoUninitSpan(this); + if (autoUninitSpan.uninitDone()) + return; + delete m; + m = NULL; + unconst(mVirtualBox) = NULL; +} + + +HRESULT NATNetwork::init(VirtualBox *aVirtualBox, IN_BSTR aName) +{ + AssertReturn(aName != NULL, E_INVALIDARG); + + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + /* share VirtualBox weakly (parent remains NULL so far) */ + unconst(mVirtualBox) = aVirtualBox; + unconst(mName) = aName; + m = new Data(); + m->offGateway = 1; + m->IPv4NetworkCidr = "10.0.2.0/24"; + m->IPv6Prefix = "fe80::/64"; + + settings::NATHostLoopbackOffset off; + off.strLoopbackHostAddress = "127.0.0.1"; + off.u32Offset = (uint32_t)2; + m->llNATLoopbackOffsetList.push_back(off); + + recalculateIpv4AddressAssignments(); + + HRESULT hrc = unconst(m->pEventSource).createObject(); + if (FAILED(hrc)) throw hrc; + + hrc = m->pEventSource->init(); + if (FAILED(hrc)) throw hrc; + + /* Confirm a successful initialization */ + autoInitSpan.setSucceeded(); + + return S_OK; +} + + +HRESULT NATNetwork::init(VirtualBox *aVirtualBox, + const settings::NATNetwork &data) +{ + /* Enclose the state transition NotReady->InInit->Ready */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + /* share VirtualBox weakly (parent remains NULL so far) */ + unconst(mVirtualBox) = aVirtualBox; + + unconst(mName) = data.strNetworkName; + m = new Data(); + m->IPv4NetworkCidr = data.strNetwork; + m->fEnabled = data.fEnabled; + m->fAdvertiseDefaultIPv6Route = data.fAdvertiseDefaultIPv6Route; + m->fNeedDhcpServer = data.fNeedDhcpServer; + m->fIPv6Enabled = data.fIPv6; + + m->u32LoopbackIp6 = data.u32HostLoopback6Offset; + + m->llNATLoopbackOffsetList.clear(); + m->llNATLoopbackOffsetList.assign(data.llHostLoopbackOffsetList.begin(), + data.llHostLoopbackOffsetList.end()); + + recalculateIpv4AddressAssignments(); + + /* IPv4 port-forward rules */ + m->mapName2PortForwardRule4.clear(); + for (settings::NATRuleList::const_iterator it = data.llPortForwardRules4.begin(); + it != data.llPortForwardRules4.end(); ++it) + { + m->mapName2PortForwardRule4.insert(std::make_pair(it->strName.c_str(), *it)); + } + + /* IPv6 port-forward rules */ + m->mapName2PortForwardRule6.clear(); + for (settings::NATRuleList::const_iterator it = data.llPortForwardRules6.begin(); + it != data.llPortForwardRules6.end(); ++it) + { + m->mapName2PortForwardRule6.insert(std::make_pair(it->strName, *it)); + } + + HRESULT hrc = unconst(m->pEventSource).createObject(); + if (FAILED(hrc)) throw hrc; + + hrc = m->pEventSource->init(); + if (FAILED(hrc)) throw hrc; + + autoInitSpan.setSucceeded(); + + return S_OK; +} + + +HRESULT NATNetwork::saveSettings(settings::NATNetwork &data) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + data.strNetworkName = mName; + data.strNetwork = m->IPv4NetworkCidr; + data.fEnabled = RT_BOOL(m->fEnabled); + data.fAdvertiseDefaultIPv6Route = RT_BOOL(m->fAdvertiseDefaultIPv6Route); + data.fNeedDhcpServer = RT_BOOL(m->fNeedDhcpServer); + data.fIPv6 = RT_BOOL(m->fIPv6Enabled); + data.strIPv6Prefix = m->IPv6Prefix; + + /* saving ipv4 port-forward Rules*/ + data.llPortForwardRules4.clear(); + for (NATRuleMap::iterator it = m->mapName2PortForwardRule4.begin(); + it != m->mapName2PortForwardRule4.end(); ++it) + data.llPortForwardRules4.push_back(it->second); + + /* saving ipv6 port-forward Rules*/ + data.llPortForwardRules6.clear(); + for (NATRuleMap::iterator it = m->mapName2PortForwardRule6.begin(); + it != m->mapName2PortForwardRule6.end(); ++it) + data.llPortForwardRules6.push_back(it->second); + + data.u32HostLoopback6Offset = m->u32LoopbackIp6; + + data.llHostLoopbackOffsetList.clear(); + data.llHostLoopbackOffsetList.assign(m->llNATLoopbackOffsetList.begin(), + m->llNATLoopbackOffsetList.end()); + + mVirtualBox->onNATNetworkSetting(mName.raw(), + data.fEnabled ? TRUE : FALSE, + m->IPv4NetworkCidr.raw(), + m->IPv4Gateway.raw(), + data.fAdvertiseDefaultIPv6Route ? TRUE : FALSE, + data.fNeedDhcpServer ? TRUE : FALSE); + + /* Notify listerners listening on this network only */ + fireNATNetworkSettingEvent(m->pEventSource, + mName.raw(), + data.fEnabled ? TRUE : FALSE, + m->IPv4NetworkCidr.raw(), + m->IPv4Gateway.raw(), + data.fAdvertiseDefaultIPv6Route ? TRUE : FALSE, + data.fNeedDhcpServer ? TRUE : FALSE); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(EventSource)(IEventSource ** aEventSource) +{ + CheckComArgOutPointerValid(aEventSource); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + /* event source is const, no need to lock */ + m->pEventSource.queryInterfaceTo(aEventSource); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(NetworkName)(BSTR *aName) +{ + CheckComArgOutPointerValid(aName); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + mName.cloneTo(aName); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(NetworkName)(IN_BSTR aName) +{ + CheckComArgOutPointerValid(aName); + AutoCaller autoCaller(this); + + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aName == mName) + return S_OK; + + unconst(mName) = aName; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(Enabled)(BOOL *aEnabled) +{ + CheckComArgOutPointerValid(aEnabled); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aEnabled = m->fEnabled; + recalculateIpv4AddressAssignments(); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(Enabled)(BOOL aEnabled) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aEnabled == m->fEnabled) + return S_OK; + + m->fEnabled = aEnabled; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(Gateway)(BSTR *aIPv4Gateway) +{ + CheckComArgOutPointerValid(aIPv4Gateway); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + m->IPv4Gateway.cloneTo(aIPv4Gateway); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(Network)(BSTR *aIPv4NetworkCidr) +{ + CheckComArgOutPointerValid(aIPv4NetworkCidr); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + m->IPv4NetworkCidr.cloneTo(aIPv4NetworkCidr); + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(Network)(IN_BSTR aIPv4NetworkCidr) +{ + CheckComArgOutPointerValid(aIPv4NetworkCidr); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aIPv4NetworkCidr == m->IPv4NetworkCidr) + return S_OK; + + /* silently ignore network cidr update for now. + * todo: keep internally guest address of port forward rule + * as offset from network id. + */ + if (!m->mapName2PortForwardRule4.empty()) + return S_OK; + + unconst(m->IPv4NetworkCidr) = Bstr(aIPv4NetworkCidr); + recalculateIpv4AddressAssignments(); + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(IPv6Enabled)(BOOL *aIPv6Enabled) +{ + CheckComArgOutPointerValid(aIPv6Enabled); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aIPv6Enabled = m->fIPv6Enabled; + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(IPv6Enabled)(BOOL aIPv6Enabled) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aIPv6Enabled == m->fIPv6Enabled) + return S_OK; + + m->fIPv6Enabled = aIPv6Enabled; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(IPv6Prefix) (BSTR *aIPv6Prefix) +{ + CheckComArgOutPointerValid(aIPv6Prefix); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + m->IPv6Prefix.cloneTo(aIPv6Prefix); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(IPv6Prefix) (IN_BSTR aIPv6Prefix) +{ + CheckComArgOutPointerValid(aIPv6Prefix); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aIPv6Prefix == m->IPv6Prefix) + return S_OK; + + /* silently ignore network IPv6 prefix update. + * todo: see similar todo in NATNetwork::COMSETTER(Network)(IN_BSTR) + */ + if (!m->mapName2PortForwardRule6.empty()) + return S_OK; + + unconst(m->IPv6Prefix) = Bstr(aIPv6Prefix); + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(AdvertiseDefaultIPv6RouteEnabled)(BOOL *aAdvertiseDefaultIPv6Route) +{ + CheckComArgOutPointerValid(aAdvertiseDefaultIPv6Route); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aAdvertiseDefaultIPv6Route = m->fAdvertiseDefaultIPv6Route; + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(AdvertiseDefaultIPv6RouteEnabled)(BOOL aAdvertiseDefaultIPv6Route) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aAdvertiseDefaultIPv6Route == m->fAdvertiseDefaultIPv6Route) + return S_OK; + + m->fAdvertiseDefaultIPv6Route = aAdvertiseDefaultIPv6Route; + + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(NeedDhcpServer)(BOOL *aNeedDhcpServer) +{ + CheckComArgOutPointerValid(aNeedDhcpServer); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + *aNeedDhcpServer = m->fNeedDhcpServer; + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(NeedDhcpServer)(BOOL aNeedDhcpServer) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aNeedDhcpServer == m->fNeedDhcpServer) + return S_OK; + + m->fNeedDhcpServer = aNeedDhcpServer; + + recalculateIpv4AddressAssignments(); + + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMGETTER(LocalMappings)(ComSafeArrayOut(BSTR, aLocalMappings)) +{ + CheckComArgOutSafeArrayPointerValid(aLocalMappings); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + com::SafeArray<BSTR> sf(m->llNATLoopbackOffsetList.size()); + + size_t i = 0; + settings::NATLoopbackOffsetList::const_iterator it; + + for (it = m->llNATLoopbackOffsetList.begin(); + it != m->llNATLoopbackOffsetList.end(); ++it, ++i) + { + BstrFmt bstr("%s=%d", + (*it).strLoopbackHostAddress.c_str(), + (*it).u32Offset); + bstr.detachTo(&sf[i]); + } + sf.detachTo(ComSafeArrayOutArg(aLocalMappings)); + + return S_OK; +} + + +STDMETHODIMP NATNetwork::AddLocalMapping(IN_BSTR aHostId, LONG aOffset) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + //AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + RTNETADDRIPV4 addr, net, mask; + + int rc = RTNetStrToIPv4Addr(Utf8Str(aHostId).c_str(), &addr); + if (RT_FAILURE(rc)) + return E_INVALIDARG; + + /* check against 127/8 */ + if ((RT_N2H_U32(addr.u) >> IN_CLASSA_NSHIFT) != IN_LOOPBACKNET) + return E_INVALIDARG; + + /* check against networkid vs network mask */ + rc = RTCidrStrToIPv4(Utf8Str(m->IPv4NetworkCidr).c_str(), &net, &mask); + if (RT_FAILURE(rc)) + return E_INVALIDARG; + + if (((net.u + aOffset) & mask.u) != net.u) + return E_INVALIDARG; + + settings::NATLoopbackOffsetList::iterator it; + + it = std::find(m->llNATLoopbackOffsetList.begin(), + m->llNATLoopbackOffsetList.end(), + Utf8Str(aHostId).c_str()); + + if (it != m->llNATLoopbackOffsetList.end()) + { + if (aOffset == 0) /* erase */ + m->llNATLoopbackOffsetList.erase(it, it); + else /* modify */ + { + settings::NATLoopbackOffsetList::iterator it1; + it1 = std::find(m->llNATLoopbackOffsetList.begin(), + m->llNATLoopbackOffsetList.end(), + (uint32_t)aOffset); + if (it1 != m->llNATLoopbackOffsetList.end()) + return E_INVALIDARG; /* this offset is already registered. */ + + (*it).u32Offset = aOffset; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); + } + + /* injection */ + it = std::find(m->llNATLoopbackOffsetList.begin(), + m->llNATLoopbackOffsetList.end(), + (uint32_t)aOffset); + + if (it != m->llNATLoopbackOffsetList.end()) + return E_INVALIDARG; /* offset is already registered. */ + + settings::NATHostLoopbackOffset off; + off.strLoopbackHostAddress = aHostId; + off.u32Offset = (uint32_t)aOffset; + m->llNATLoopbackOffsetList.push_back(off); + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); +} + + +STDMETHODIMP NATNetwork::COMGETTER(LoopbackIp6)(LONG *aLoopbackIp6) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + *aLoopbackIp6 = m->u32LoopbackIp6; + return S_OK; +} + + +STDMETHODIMP NATNetwork::COMSETTER(LoopbackIp6)(LONG aLoopbackIp6) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (aLoopbackIp6 < 0) + return E_INVALIDARG; + + if (static_cast<uint32_t>(aLoopbackIp6) == m->u32LoopbackIp6) + return S_OK; + + m->u32LoopbackIp6 = aLoopbackIp6; + } + + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + return mVirtualBox->saveSettings(); +} + + +STDMETHODIMP NATNetwork::COMGETTER(PortForwardRules4)(ComSafeArrayOut(BSTR, aPortForwardRules4)) +{ + CheckComArgOutSafeArrayPointerValid(aPortForwardRules4); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + GetPortForwardRulesFromMap(ComSafeArrayInArg(aPortForwardRules4), + m->mapName2PortForwardRule4); + return S_OK; +} + +STDMETHODIMP NATNetwork::COMGETTER(PortForwardRules6)(ComSafeArrayOut(BSTR, + aPortForwardRules6)) +{ + CheckComArgOutSafeArrayPointerValid(aPortForwardRules6); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + GetPortForwardRulesFromMap(ComSafeArrayInArg(aPortForwardRules6), m->mapName2PortForwardRule6); + return S_OK; +} + + +STDMETHODIMP NATNetwork::AddPortForwardRule(BOOL aIsIpv6, + IN_BSTR aPortForwardRuleName, + NATProtocol_T aProto, + IN_BSTR aHostIp, + USHORT aHostPort, + IN_BSTR aGuestIp, + USHORT aGuestPort) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + Utf8Str name = aPortForwardRuleName; + Utf8Str proto; + settings::NATRule r; + NATRuleMap& mapRules = aIsIpv6 ? m->mapName2PortForwardRule6 : m->mapName2PortForwardRule4; + switch (aProto) + { + case NATProtocol_TCP: + proto = "tcp"; + break; + case NATProtocol_UDP: + proto = "udp"; + break; + default: + return E_INVALIDARG; + } + if (name.isEmpty()) + name = Utf8StrFmt("%s_[%s]%%%d_[%s]%%%d", proto.c_str(), + Utf8Str(aHostIp).c_str(), aHostPort, + Utf8Str(aGuestIp).c_str(), aGuestPort); + + NATRuleMap::iterator it; + + for (it = mapRules.begin(); it != mapRules.end(); ++it) + { + r = it->second; + if (it->first == name) + return setError(E_INVALIDARG, + tr("A NAT rule of this name already exists")); + if ( r.strHostIP == Utf8Str(aHostIp) + && r.u16HostPort == aHostPort + && r.proto == aProto) + return setError(E_INVALIDARG, + tr("A NAT rule for this host port and this host IP already exists")); + } + + r.strName = name.c_str(); + r.proto = aProto; + r.strHostIP = aHostIp; + r.u16HostPort = aHostPort; + r.strGuestIP = aGuestIp; + r.u16GuestPort = aGuestPort; + mapRules.insert(std::make_pair(name, r)); + } + + { + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + } + + mVirtualBox->onNATNetworkPortForward(mName.raw(), TRUE, aIsIpv6, + aPortForwardRuleName, aProto, + aHostIp, aHostPort, + aGuestIp, aGuestPort); + + /* Notify listerners listening on this network only */ + fireNATNetworkPortForwardEvent(m->pEventSource, mName.raw(), TRUE, + aIsIpv6, aPortForwardRuleName, aProto, + aHostIp, aHostPort, + aGuestIp, aGuestPort); + return S_OK; +} + + +STDMETHODIMP NATNetwork::RemovePortForwardRule(BOOL aIsIpv6, IN_BSTR aPortForwardRuleName) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) + return autoCaller.rc(); + + Utf8Str strHostIP; + Utf8Str strGuestIP; + uint16_t u16HostPort; + uint16_t u16GuestPort; + NATProtocol_T proto; + + + { + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + NATRuleMap& mapRules = aIsIpv6 ? m->mapName2PortForwardRule6 + : m->mapName2PortForwardRule4; + + NATRuleMap::iterator it = mapRules.find(aPortForwardRuleName); + + if (it == mapRules.end()) + return E_INVALIDARG; + + strHostIP = it->second.strHostIP; + strGuestIP = it->second.strGuestIP; + u16HostPort = it->second.u16HostPort; + u16GuestPort = it->second.u16GuestPort; + proto = it->second.proto; + + mapRules.erase(it); + } + + { + AutoWriteLock vboxLock(mVirtualBox COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mVirtualBox->saveSettings(); + ComAssertComRCRetRC(rc); + } + + mVirtualBox->onNATNetworkPortForward(mName.raw(), FALSE, aIsIpv6, + aPortForwardRuleName, proto, + Bstr(strHostIP).raw(), u16HostPort, + Bstr(strGuestIP).raw(), u16GuestPort); + + /* Notify listerners listening on this network only */ + fireNATNetworkPortForwardEvent(m->pEventSource, mName.raw(), FALSE, + aIsIpv6, aPortForwardRuleName, proto, + Bstr(strHostIP).raw(), u16HostPort, + Bstr(strGuestIP).raw(), u16GuestPort); + return S_OK; +} + + +STDMETHODIMP NATNetwork::Start(IN_BSTR aTrunkType) +{ +#ifdef VBOX_WITH_NAT_SERVICE + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + if (!m->fEnabled) return S_OK; + + m->NATRunner.setOption(NetworkServiceRunner::kNsrKeyNetwork, Utf8Str(mName).c_str()); + m->NATRunner.setOption(NetworkServiceRunner::kNsrKeyTrunkType, Utf8Str(aTrunkType).c_str()); + m->NATRunner.setOption(NetworkServiceRunner::kNsrIpAddress, Utf8Str(m->IPv4Gateway).c_str()); + m->NATRunner.setOption(NetworkServiceRunner::kNsrIpNetmask, Utf8Str(m->IPv4NetworkMask).c_str()); + + /* No portforwarding rules from command-line, all will be fetched via API */ + + if (m->fNeedDhcpServer) + { + /* + * Just to as idea... via API (on creation user pass the cidr of network and) + * and we calculate it's addreses (mutable?). + */ + + /* + * Configuration and running DHCP server: + * 1. find server first createDHCPServer + * 2. if return status is E_INVALARG => server already exists just find and start. + * 3. if return status neither E_INVALRG nor S_OK => return E_FAIL + * 4. if return status S_OK proceed to DHCP server configuration + * 5. call setConfiguration() and pass all required parameters + * 6. start dhcp server. + */ + HRESULT hrc = mVirtualBox->FindDHCPServerByNetworkName(mName.raw(), + m->dhcpServer.asOutParam()); + switch (hrc) + { + case E_INVALIDARG: + /* server haven't beeen found let create it then */ + hrc = mVirtualBox->CreateDHCPServer(mName.raw(), + m->dhcpServer.asOutParam()); + if (FAILED(hrc)) + return E_FAIL; + /* breakthrough */ + + { + LogFunc(("gateway: %s, dhcpserver:%s, dhcplowerip:%s, dhcpupperip:%s\n", + Utf8Str(m->IPv4Gateway.raw()).c_str(), + Utf8Str(m->IPv4DhcpServer.raw()).c_str(), + Utf8Str(m->IPv4DhcpServerLowerIp.raw()).c_str(), + Utf8Str(m->IPv4DhcpServerUpperIp.raw()).c_str())); + + hrc = m->dhcpServer->COMSETTER(Enabled)(true); + + BSTR dhcpip = NULL; + BSTR netmask = NULL; + BSTR lowerip = NULL; + BSTR upperip = NULL; + + m->IPv4DhcpServer.cloneTo(&dhcpip); + m->IPv4NetworkMask.cloneTo(&netmask); + m->IPv4DhcpServerLowerIp.cloneTo(&lowerip); + m->IPv4DhcpServerUpperIp.cloneTo(&upperip); + hrc = m->dhcpServer->SetConfiguration(dhcpip, + netmask, + lowerip, + upperip); + } + case S_OK: + break; + + default: + return E_FAIL; + } + + /* XXX: AddGlobalOption(DhcpOpt_Router,) - enables attachement of DhcpServer to Main. */ + m->dhcpServer->AddGlobalOption(DhcpOpt_Router, m->IPv4Gateway.raw()); + + hrc = m->dhcpServer->Start(mName.raw(), Bstr("").raw(), aTrunkType); + if (FAILED(hrc)) + { + m->dhcpServer.setNull(); + return E_FAIL; + } + } + + if (RT_SUCCESS(m->NATRunner.start())) + { + mVirtualBox->onNATNetworkStartStop(mName.raw(), TRUE); + return S_OK; + } + /** @todo missing setError()! */ + return E_FAIL; +#else + NOREF(aTrunkType); + ReturnComNotImplemented(); +#endif +} + + +STDMETHODIMP NATNetwork::Stop() +{ +#ifdef VBOX_WITH_NAT_SERVICE + if (!m->dhcpServer.isNull()) + m->dhcpServer->Stop(); + + if (RT_SUCCESS(m->NATRunner.stop())) + { + mVirtualBox->onNATNetworkStartStop(mName.raw(), FALSE); + return S_OK; + } + /** @todo missing setError()! */ + return E_FAIL; +#else + ReturnComNotImplemented(); +#endif +} + + +void NATNetwork::GetPortForwardRulesFromMap(ComSafeArrayOut(BSTR, aPortForwardRules), NATRuleMap& aRules) +{ + com::SafeArray<BSTR> sf(aRules.size()); + size_t i = 0; + NATRuleMap::const_iterator it; + for (it = aRules.begin(); + it != aRules.end(); ++it, ++i) + { + settings::NATRule r = it->second; + BstrFmt bstr("%s:%s:[%s]:%d:[%s]:%d", + r.strName.c_str(), + (r.proto == NATProtocol_TCP? "tcp" : "udp"), + r.strHostIP.c_str(), + r.u16HostPort, + r.strGuestIP.c_str(), + r.u16GuestPort); + bstr.detachTo(&sf[i]); + } + sf.detachTo(ComSafeArrayOutArg(aPortForwardRules)); +} + + +int NATNetwork::findFirstAvailableOffset(ADDRESSLOOKUPTYPE addrType, uint32_t *poff) +{ + RTNETADDRIPV4 network, netmask; + + int rc = RTCidrStrToIPv4(Utf8Str(m->IPv4NetworkCidr.raw()).c_str(), + &network, + &netmask); + AssertRCReturn(rc, rc); + + uint32_t off; + settings::NATLoopbackOffsetList::iterator it; + for (off = 1; off < ~netmask.u; ++off) + { + + bool skip = false; + for (it = m->llNATLoopbackOffsetList.begin(); + it != m->llNATLoopbackOffsetList.end(); + ++it) + { + if ((*it).u32Offset == off) + { + skip = true; + break; + } + + } + + if (skip) + continue; + + if (off == m->offGateway) + { + if (addrType == ADDR_GATEWAY) + break; + else + continue; + } + + if (off == m->offDhcp) + { + if (addrType == ADDR_DHCP) + break; + else + continue; + } + + if (!skip) + break; + } + + if (poff) + *poff = off; + + return VINF_SUCCESS; +} + + +int NATNetwork::recalculateIpv4AddressAssignments() +{ + RTNETADDRIPV4 network, netmask; + int rc = RTCidrStrToIPv4(Utf8Str(m->IPv4NetworkCidr.raw()).c_str(), + &network, + &netmask); + AssertRCReturn(rc, rc); + + findFirstAvailableOffset(ADDR_GATEWAY, &m->offGateway); + if (m->fNeedDhcpServer) + findFirstAvailableOffset(ADDR_DHCP, &m->offDhcp); + + /* I don't remember the reason CIDR calculated on the host. */ + RTNETADDRIPV4 gateway = network; + gateway.u += m->offGateway; + gateway.u = RT_H2N_U32(gateway.u); + char szTmpIp[16]; + RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", gateway); + m->IPv4Gateway = szTmpIp; + + if (m->fNeedDhcpServer) + { + RTNETADDRIPV4 dhcpserver = network; + dhcpserver.u += m->offDhcp; + + /* XXX: adding more services should change the math here */ + RTNETADDRIPV4 dhcplowerip = network; + uint32_t offDhcpLowerIp; + findFirstAvailableOffset(ADDR_DHCPLOWERIP, &offDhcpLowerIp); + dhcplowerip.u = RT_H2N_U32(dhcplowerip.u + offDhcpLowerIp); + + RTNETADDRIPV4 dhcpupperip; + dhcpupperip.u = RT_H2N_U32((network.u | ~netmask.u) - 1); + + dhcpserver.u = RT_H2N_U32(dhcpserver.u); + network.u = RT_H2N_U32(network.u); + + RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpserver); + m->IPv4DhcpServer = szTmpIp; + RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcplowerip); + m->IPv4DhcpServerLowerIp = szTmpIp; + RTStrPrintf(szTmpIp, sizeof(szTmpIp), "%RTnaipv4", dhcpupperip); + m->IPv4DhcpServerUpperIp = szTmpIp; + + LogFunc(("network:%RTnaipv4, dhcpserver:%RTnaipv4, dhcplowerip:%RTnaipv4, dhcpupperip:%RTnaipv4\n", + network, dhcpserver, dhcplowerip, dhcpupperip)); + } + + /* we need IPv4NetworkMask for NAT's gw service start */ + netmask.u = RT_H2N_U32(netmask.u); + RTStrPrintf(szTmpIp, 16, "%RTnaipv4", netmask); + m->IPv4NetworkMask = szTmpIp; + + LogFlowFunc(("getaway:%RTnaipv4, netmask:%RTnaipv4\n", gateway, netmask)); + return VINF_SUCCESS; +} diff --git a/src/VBox/Main/src-server/DHCPServerRunner.cpp b/src/VBox/Main/src-server/NATNetworkServiceRunner.cpp index aab7c8a9..5b51db5b 100644 --- a/src/VBox/Main/src-server/DHCPServerRunner.cpp +++ b/src/VBox/Main/src-server/NATNetworkServiceRunner.cpp @@ -1,10 +1,10 @@ -/* $Id: DHCPServerRunner.cpp $ */ +/* $Id: NATNetworkServiceRunner.cpp $ */ /** @file - * VirtualBox Main - interface for VBox DHCP server + * VirtualBox Main - interface for VBox NAT Network service */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -14,43 +14,36 @@ * VirtualBox OSE distribution. VirtualBox OSE is distributed in the * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. */ -#include "DHCPServerRunner.h" +#include "NATNetworkServiceRunner.h" #include <iprt/process.h> #include <iprt/param.h> #include <iprt/env.h> struct ARGDEF { - DHCPCFG Type; + NATSCCFG Type; const char * Name; }; #ifdef RT_OS_WINDOWS -# define DHCP_EXECUTABLE_NAME "VBoxNetDHCP.exe" +# define NATSR_EXECUTABLE_NAME "VBoxNetNAT.exe" #else -# define DHCP_EXECUTABLE_NAME "VBoxNetDHCP" +# define NATSR_EXECUTABLE_NAME "VBoxNetNAT" #endif static const ARGDEF g_aArgDefs[] = { - {DHCPCFG_NAME, "--name"}, - {DHCPCFG_NETNAME, "--network"}, - {DHCPCFG_TRUNKTYPE, "--trunk-type"}, - {DHCPCFG_TRUNKNAME, "--trunk-name"}, - {DHCPCFG_MACADDRESS, "--mac-address"}, - {DHCPCFG_IPADDRESS, "--ip-address"}, - {DHCPCFG_LEASEDB, "--lease-db"}, - {DHCPCFG_VERBOSE, "--verbose"}, - {DHCPCFG_BEGINCONFIG, "--begin-config"}, - {DHCPCFG_GATEWAY, "--gateway"}, - {DHCPCFG_LOWERIP, "--lower-ip"}, - {DHCPCFG_UPPERIP, "--upper-ip"}, - {DHCPCFG_NETMASK, "--netmask"}, - {DHCPCFG_HELP, "--help"}, - {DHCPCFG_VERSION, "--version"} + {NATSCCFG_NAME, "-n"}, + {NATSCCFG_TRUNKTYPE, "--trunk-type"}, + {NATSCCFG_MACADDRESS, "--mac-address"}, + {NATSCCFG_IPADDRESS, "--ip-address"}, + {NATSCCFG_NETMASK, "--netmask"}, + {NATSCCFG_PORTFORWARD4, "--pf4"}, + {NATSCCFG_PORTFORWARD6, "--pf6"} + }; -static const ARGDEF * getArgDef(DHCPCFG type) +static const ARGDEF * getArgDef(NATSCCFG type) { for (unsigned i = 0; i < RT_ELEMENTS(g_aArgDefs); i++) if (g_aArgDefs[i].Type == type) @@ -59,26 +52,26 @@ static const ARGDEF * getArgDef(DHCPCFG type) return NULL; } -DHCPServerRunner::DHCPServerRunner() +NATNetworkServiceRunner::NATNetworkServiceRunner() { mProcess = NIL_RTPROCESS; - for (unsigned i = 0; i < DHCPCFG_NOTOPT_MAXVAL; i++) + for (unsigned i = 0; i < NATSCCFG_NOTOPT_MAXVAL; i++) { mOptionEnabled[i] = false; } } -void DHCPServerRunner::detachFromServer() +void NATNetworkServiceRunner::detachFromServer() { mProcess = NIL_RTPROCESS; } -int DHCPServerRunner::start() +int NATNetworkServiceRunner::start() { if (isRunning()) return VINF_ALREADY_INITIALIZED; - const char * args[DHCPCFG_NOTOPT_MAXVAL * 2]; + const char * args[NATSCCFG_NOTOPT_MAXVAL * 2]; /* get the path to the executable */ char exePathBuf[RTPATH_MAX]; @@ -90,20 +83,20 @@ int DHCPServerRunner::start() if (suffix) { suffix++; - strcpy(suffix, DHCP_EXECUTABLE_NAME); + strcpy(suffix, NATSR_EXECUTABLE_NAME); } else - exePath = DHCP_EXECUTABLE_NAME; + exePath = NATSR_EXECUTABLE_NAME; int index = 0; args[index++] = exePath; - for (unsigned i = 0; i < DHCPCFG_NOTOPT_MAXVAL; i++) + for (unsigned i = 0; i < NATSCCFG_NOTOPT_MAXVAL; i++) { if (mOptionEnabled[i]) { - const ARGDEF *pArgDef = getArgDef((DHCPCFG)i); + const ARGDEF *pArgDef = getArgDef((NATSCCFG)i); if (!pArgDef) continue; args[index++] = pArgDef->Name; // e.g. "--network" @@ -117,15 +110,20 @@ int DHCPServerRunner::start() } args[index++] = NULL; + RTENV env; + int rc = RTEnvCreate(&env); + AssertRCReturn(rc,rc); + + RTEnvPutEx(env, "VBOX_LOG=e.l.f"); - int rc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &mProcess); + rc = RTProcCreate(exePath, args, RTENV_DEFAULT, 0, &mProcess); if (RT_FAILURE(rc)) mProcess = NIL_RTPROCESS; - + RTEnvDestroy(env); return rc; } -int DHCPServerRunner::stop() +int NATNetworkServiceRunner::stop() { if (!isRunning()) return VINF_OBJECT_DESTROYED; @@ -135,7 +133,7 @@ int DHCPServerRunner::stop() return rc; } -bool DHCPServerRunner::isRunning() +bool NATNetworkServiceRunner::isRunning() { if (mProcess == NIL_RTPROCESS) return false; diff --git a/src/VBox/Main/src-server/NetworkAdapterImpl.cpp b/src/VBox/Main/src-server/NetworkAdapterImpl.cpp index c7e1fa17..e3908bb3 100644 --- a/src/VBox/Main/src-server/NetworkAdapterImpl.cpp +++ b/src/VBox/Main/src-server/NetworkAdapterImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -23,6 +23,7 @@ #include "GuestOSTypeImpl.h" #include "HostImpl.h" #include "SystemPropertiesImpl.h" +#include "VirtualBoxImpl.h" #include <iprt/string.h> #include <iprt/cpp/utils.h> @@ -439,8 +440,7 @@ STDMETHODIMP NetworkAdapter::COMSETTER(MACAddress)(IN_BSTR aMACAddress) return rc; } -STDMETHODIMP NetworkAdapter::COMGETTER(AttachmentType)( - NetworkAttachmentType_T *aAttachmentType) +STDMETHODIMP NetworkAdapter::COMGETTER(AttachmentType)(NetworkAttachmentType_T *aAttachmentType) { CheckComArgOutPointerValid(aAttachmentType); @@ -454,8 +454,7 @@ STDMETHODIMP NetworkAdapter::COMGETTER(AttachmentType)( return S_OK; } -STDMETHODIMP NetworkAdapter::COMSETTER(AttachmentType)( - NetworkAttachmentType_T aAttachmentType) +STDMETHODIMP NetworkAdapter::COMSETTER(AttachmentType)(NetworkAttachmentType_T aAttachmentType) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); @@ -477,6 +476,14 @@ STDMETHODIMP NetworkAdapter::COMSETTER(AttachmentType)( mData->mInternalNetwork = "intnet"; } + /* there must a NAT network name */ + if (mData->mNATNetwork.isEmpty()) + { + Log(("NAT network name not defined, setting to default \"NatNetwork\"\n")); + mData->mNATNetwork = "NatNetwork"; + } + + NetworkAttachmentType_T oldAttachmentType = mData->mAttachmentType; mData->mAttachmentType = aAttachmentType; m_fModified = true; @@ -487,6 +494,12 @@ STDMETHODIMP NetworkAdapter::COMSETTER(AttachmentType)( mParent->setModified(Machine::IsModified_NetworkAdapters); mlock.release(); + if (oldAttachmentType == NetworkAttachmentType_NATNetwork) + checkAndSwitchFromNatNetworking(mData->mNATNetwork.raw()); + + if (aAttachmentType == NetworkAttachmentType_NATNetwork) + switchToNatNetworking(mData->mNATNetwork.raw()); + /* Adapt the CFGM logic and notify the guest => changeAdapter=TRUE. */ mParent->onNetworkAdapterChange(this, TRUE); } @@ -525,6 +538,15 @@ STDMETHODIMP NetworkAdapter::COMSETTER(BridgedInterface)(IN_BSTR aBridgedInterfa if (mData->mBridgedInterface != aBridgedInterface) { + /* if an empty/null string is to be set, bridged interface must be + * turned off */ + if ( (aBridgedInterface == NULL || *aBridgedInterface == '\0') + && mData->mAttachmentType == NetworkAttachmentType_Bridged) + { + return setError(E_FAIL, + tr("Empty or null bridged interface name is not valid")); + } + mData.backup(); mData->mBridgedInterface = aBridgedInterface; @@ -576,6 +598,15 @@ STDMETHODIMP NetworkAdapter::COMSETTER(HostOnlyInterface)(IN_BSTR aHostOnlyInter if (mData->mHostOnlyInterface != aHostOnlyInterface) { + /* if an empty/null string is to be set, host only interface must be + * turned off */ + if ( (aHostOnlyInterface == NULL || *aHostOnlyInterface == '\0') + && mData->mAttachmentType == NetworkAttachmentType_HostOnly) + { + return setError(E_FAIL, + tr("Empty or null host only interface name is not valid")); + } + mData.backup(); mData->mHostOnlyInterface = aHostOnlyInterface; @@ -683,7 +714,19 @@ STDMETHODIMP NetworkAdapter::COMSETTER(NATNetwork)(IN_BSTR aNATNetwork) if (mData->mNATNetwork != aNATNetwork) { + + /* if an empty/null string is to be set, host only interface must be + * turned off */ + if ( (aNATNetwork == NULL || *aNATNetwork == '\0') + && mData->mAttachmentType == NetworkAttachmentType_NATNetwork) + { + return setError(E_FAIL, + tr("Empty or null NAT network name is not valid")); + } + mData.backup(); + + Bstr oldNatNetworkName = mData->mNATNetwork; mData->mNATNetwork = aNATNetwork; m_fModified = true; @@ -694,9 +737,14 @@ STDMETHODIMP NetworkAdapter::COMSETTER(NATNetwork)(IN_BSTR aNATNetwork) mParent->setModified(Machine::IsModified_NetworkAdapters); mlock.release(); - /* Changing the NAT network isn't allowed during runtime, therefore - * no immediate replug in CFGM logic => changeAdapter=FALSE */ - mParent->onNetworkAdapterChange(this, FALSE); + checkAndSwitchFromNatNetworking(oldNatNetworkName.raw()); + + switchToNatNetworking(aNATNetwork); + + /* When changing the host adapter, adapt the CFGM logic to make this + * change immediately effect and to notify the guest that the network + * might have changed, therefore changeAdapter=TRUE. */ + mParent->onNetworkAdapterChange(this, TRUE); } return S_OK; @@ -1208,6 +1256,7 @@ HRESULT NetworkAdapter::loadSettings(BandwidthControl *bwctl, mData->mHostOnlyInterface = data.strHostOnlyName; mData->mGenericDriver = data.strGenericDriver; mData->mGenericProperties = data.genericProperties; + mData->mNATNetwork = data.strNATNetworkName; // leave the lock before setting attachment type alock.release(); @@ -1268,6 +1317,8 @@ HRESULT NetworkAdapter::saveSettings(settings::NetworkAdapter &data) data.strGenericDriver = mData->mGenericDriver; data.genericProperties = mData->mGenericProperties; + data.strNATNetworkName = mData->mNATNetwork; + // after saving settings, we are no longer different from the XML on disk m_fModified = false; @@ -1511,4 +1562,48 @@ void NetworkAdapter::updateBandwidthGroup(BandwidthGroup *aBwGroup) LogFlowThisFuncLeave(); } + + +HRESULT NetworkAdapter::checkAndSwitchFromNatNetworking(IN_BSTR networkName) +{ + MachineState_T state; + + HRESULT hrc = mParent->COMGETTER(State)(&state); + if (FAILED(hrc)) + return hrc; + + if (state == MachineState_Running) + { + Bstr bstrName; + hrc = mParent->COMGETTER(Name)(bstrName.asOutParam()); + LogRel(("VM '%ls' stops using NAT network '%ls'\n", bstrName.raw(), networkName)); + int natCount = mParent->getVirtualBox()->natNetworkRefDec(networkName); + if (natCount == -1) + return E_INVALIDARG; /* no such network */ + } + + return S_OK; +} + + +HRESULT NetworkAdapter::switchToNatNetworking(IN_BSTR aNatNetworkName) +{ + MachineState_T state; + + HRESULT hrc = mParent->COMGETTER(State)(&state); + if (FAILED(hrc)) + return hrc; + + if (state == MachineState_Running) + { + Bstr bstrName; + hrc = mParent->COMGETTER(Name)(bstrName.asOutParam()); + LogRel(("VM '%ls' starts using NAT network '%ls'\n", bstrName.raw(), aNatNetworkName)); + int natCount = mParent->getVirtualBox()->natNetworkRefInc(aNatNetworkName); + if (natCount == -1) + return E_INVALIDARG; /* not found */ + } + + return S_OK; +} /* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/NetworkServiceRunner.cpp b/src/VBox/Main/src-server/NetworkServiceRunner.cpp new file mode 100644 index 00000000..14610e5a --- /dev/null +++ b/src/VBox/Main/src-server/NetworkServiceRunner.cpp @@ -0,0 +1,138 @@ +/* $Id: NetworkServiceRunner.cpp $ */ +/** @file + * VirtualBox Main - interface for VBox DHCP server + */ + +/* + * Copyright (C) 2009-2012 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <map> +#include <string> +#include "NetworkServiceRunner.h" +#include <iprt/process.h> +#include <iprt/param.h> +#include <iprt/env.h> + + +const std::string NetworkServiceRunner::kNsrKeyName = "--name"; +const std::string NetworkServiceRunner::kNsrKeyNetwork = "--network"; +const std::string NetworkServiceRunner::kNsrKeyTrunkType = "--trunk-type"; +const std::string NetworkServiceRunner::kNsrTrunkName = "--trunk-name"; +const std::string NetworkServiceRunner::kNsrMacAddress = "--mac-address"; +const std::string NetworkServiceRunner::kNsrIpAddress = "--ip-address"; +const std::string NetworkServiceRunner::kNsrIpNetmask = "--netmask"; +const std::string NetworkServiceRunner::kNsrKeyNeedMain = "--need-main"; + +struct NetworkServiceRunner::Data +{ + Data(const char* aProcName):mProcName(aProcName), mProcess(NIL_RTPROCESS){} + const char *mProcName; + RTPROCESS mProcess; + std::map<std::string, std::string> mOptions; +}; + +NetworkServiceRunner::NetworkServiceRunner(const char *aProcName) +{ + m = new NetworkServiceRunner::Data(aProcName); + +} + + +NetworkServiceRunner::~NetworkServiceRunner() +{ + stop(); + delete m; + m = NULL; +} + + +int NetworkServiceRunner::setOption(const std::string& key, const std::string& val) +{ + m->mOptions.insert(std::map<std::string, std::string>::value_type(key, val)); + return VINF_SUCCESS; +} + + +void NetworkServiceRunner::detachFromServer() +{ + m->mProcess = NIL_RTPROCESS; +} + + +int NetworkServiceRunner::start() +{ + if (isRunning()) + return VINF_ALREADY_INITIALIZED; + + const char * args[10*2]; + + AssertReturn(m->mOptions.size() < 10, VERR_INTERNAL_ERROR); + + /* get the path to the executable */ + char exePathBuf[RTPATH_MAX]; + const char *exePath = RTProcGetExecutablePath(exePathBuf, RTPATH_MAX); + char *substrSl = strrchr(exePathBuf, '/'); + char *substrBs = strrchr(exePathBuf, '\\'); + char *suffix = substrSl ? substrSl : substrBs; + + if (suffix) + { + suffix++; + strcpy(suffix, m->mProcName); + } + + int index = 0; + + args[index++] = exePath; + + std::map<std::string, std::string>::const_iterator it; + for(it = m->mOptions.begin(); it != m->mOptions.end(); ++it) + { + args[index++] = it->first.c_str(); + args[index++] = it->second.c_str(); + } + + args[index++] = NULL; + + int rc = RTProcCreate(suffix ? exePath : m->mProcName, args, RTENV_DEFAULT, 0, &m->mProcess); + if (RT_FAILURE(rc)) + m->mProcess = NIL_RTPROCESS; + + return rc; +} + + +int NetworkServiceRunner::stop() +{ + if (!isRunning()) + return VINF_OBJECT_DESTROYED; + + int rc = RTProcTerminate(m->mProcess); + RTProcWait(m->mProcess, RTPROCWAIT_FLAGS_BLOCK, NULL); + m->mProcess = NIL_RTPROCESS; + return rc; +} + +bool NetworkServiceRunner::isRunning() +{ + if (m->mProcess == NIL_RTPROCESS) + return false; + + RTPROCSTATUS status; + int rc = RTProcWait(m->mProcess, RTPROCWAIT_FLAGS_NOBLOCK, &status); + + if (rc == VERR_PROCESS_RUNNING) + return true; + + m->mProcess = NIL_RTPROCESS; + return false; +} diff --git a/src/VBox/Main/src-server/ParallelPortImpl.cpp b/src/VBox/Main/src-server/ParallelPortImpl.cpp index 56743a08..e9ce1d0d 100644 --- a/src/VBox/Main/src-server/ParallelPortImpl.cpp +++ b/src/VBox/Main/src-server/ParallelPortImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/Performance.cpp b/src/VBox/Main/src-server/Performance.cpp index 465bfa1c..41ace847 100644 --- a/src/VBox/Main/src-server/Performance.cpp +++ b/src/VBox/Main/src-server/Performance.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -24,8 +24,12 @@ #ifndef VBOX_COLLECTOR_TEST_CASE #include "VirtualBoxImpl.h" #include "MachineImpl.h" +#include "MediumImpl.h" +#include "AutoCaller.h" #endif #include "Performance.h" +#include "HostNetworkInterfaceImpl.h" +#include "netif.h" #include <VBox/com/array.h> #include <VBox/com/ptr.h> @@ -45,47 +49,57 @@ using namespace pm; int CollectorHAL::getHostCpuLoad(ULONG * /* user */, ULONG * /* kernel */, ULONG * /* idle */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorHAL::getProcessCpuLoad(RTPROCESS /* process */, ULONG * /* user */, ULONG * /* kernel */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorHAL::getRawHostCpuLoad(uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* idle */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorHAL::getRawHostNetworkLoad(const char * /* name */, uint64_t * /* rx */, uint64_t * /* tx */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorHAL::getRawHostDiskLoad(const char * /* name */, uint64_t * /* disk_ms */, uint64_t * /* total_ms */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorHAL::getRawProcessCpuLoad(RTPROCESS /* process */, uint64_t * /* user */, uint64_t * /* kernel */, uint64_t * /* total */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorHAL::getHostMemoryUsage(ULONG * /* total */, ULONG * /* used */, ULONG * /* available */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorHAL::getHostFilesystemUsage(const char * /* name */, ULONG * /* total */, ULONG * /* used */, ULONG * /* available */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; +} + +int CollectorHAL::getHostDiskSize(const char * /* name */, uint64_t * /* size */) +{ + return VERR_NOT_IMPLEMENTED; } int CollectorHAL::getProcessMemoryUsage(RTPROCESS /* process */, ULONG * /* used */) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; +} + +int CollectorHAL::getDiskListByFs(const char * /* name */, DiskList& /* listUsage */, DiskList& /* listLoad */) +{ + return VERR_NOT_IMPLEMENTED; } /* Generic implementations */ @@ -169,7 +183,7 @@ CollectorGuestRequest* CollectorGuestQueue::pop() return NULL; } -int CGRQEnable::execute() +HRESULT CGRQEnable::execute() { Assert(mCGuest); return mCGuest->enableInternal(mMask); @@ -184,7 +198,7 @@ void CGRQEnable::debugPrint(void *aObject, const char *aFunction, const char *aT aObject, aFunction, mMask, aText)); } -int CGRQDisable::execute() +HRESULT CGRQDisable::execute() { Assert(mCGuest); return mCGuest->disableInternal(mMask); @@ -199,7 +213,7 @@ void CGRQDisable::debugPrint(void *aObject, const char *aFunction, const char *a aObject, aFunction, mMask, aText)); } -int CGRQAbort::execute() +HRESULT CGRQAbort::execute() { return E_ABORT; } @@ -217,7 +231,7 @@ CollectorGuest::CollectorGuest(Machine *machine, RTPROCESS process) : mUnregistered(false), mEnabled(false), mValid(false), mMachine(machine), mProcess(process), mCpuUser(0), mCpuKernel(0), mCpuIdle(0), mMemTotal(0), mMemFree(0), mMemBalloon(0), mMemShared(0), mMemCache(0), mPageTotal(0), - mAllocVMM(0), mFreeVMM(0), mBalloonedVMM(0), mSharedVMM(0) + mAllocVMM(0), mFreeVMM(0), mBalloonedVMM(0), mSharedVMM(0), mVmNetRx(0), mVmNetTx(0) { Assert(mMachine); /* cannot use ComObjPtr<Machine> in Performance.h, do it manually */ @@ -267,7 +281,7 @@ int CollectorGuest::disable(ULONG mask) return enqueueRequest(new CGRQDisable(mask)); } -int CollectorGuest::enableInternal(ULONG mask) +HRESULT CollectorGuest::enableInternal(ULONG mask) { HRESULT ret = S_OK; @@ -306,7 +320,7 @@ int CollectorGuest::enableInternal(ULONG mask) this, __PRETTY_FUNCTION__, SUCCEEDED(ret)?"success":"failed")); } } - if ((mask & GUESTSTATS_VMMRAM) == GUESTSTATS_VMMRAM) + if ((mask & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM) enableVMMStats(true); mEnabled |= mask; @@ -318,7 +332,7 @@ int CollectorGuest::disableInternal(ULONG mask) if (!(mEnabled & mask)) return E_UNEXPECTED; - if ((mask & GUESTSTATS_VMMRAM) == GUESTSTATS_VMMRAM) + if ((mask & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM) enableVMMStats(false); mEnabled &= ~mask; if (!mEnabled) @@ -328,7 +342,7 @@ int CollectorGuest::disableInternal(ULONG mask) NOREF(ret); LogAleksey(("{%p} " LOG_FN_FMT ": Set guest statistics update interval to 0 sec (%s)\n", this, __PRETTY_FUNCTION__, SUCCEEDED(ret)?"success":"failed")); - invalidate(GUESTSTATS_ALL); + invalidate(VMSTATS_ALL); } return S_OK; @@ -353,15 +367,16 @@ void CollectorGuest::updateStats(ULONG aValidStats, ULONG aCpuUser, ULONG aMemBalloon, ULONG aMemShared, ULONG aMemCache, ULONG aPageTotal, ULONG aAllocVMM, ULONG aFreeVMM, - ULONG aBalloonedVMM, ULONG aSharedVMM) + ULONG aBalloonedVMM, ULONG aSharedVMM, + ULONG aVmNetRx, ULONG aVmNetTx) { - if ((aValidStats & GUESTSTATS_CPULOAD) == GUESTSTATS_CPULOAD) + if ((aValidStats & VMSTATS_GUEST_CPULOAD) == VMSTATS_GUEST_CPULOAD) { mCpuUser = aCpuUser; mCpuKernel = aCpuKernel, mCpuIdle = aCpuIdle; } - if ((aValidStats & GUESTSTATS_RAMUSAGE) == GUESTSTATS_RAMUSAGE) + if ((aValidStats & VMSTATS_GUEST_RAMUSAGE) == VMSTATS_GUEST_RAMUSAGE) { mMemTotal = aMemTotal; mMemFree = aMemFree; @@ -370,13 +385,18 @@ void CollectorGuest::updateStats(ULONG aValidStats, ULONG aCpuUser, mMemCache = aMemCache; mPageTotal = aPageTotal; } - if ((aValidStats & GUESTSTATS_VMMRAM) == GUESTSTATS_VMMRAM) + if ((aValidStats & VMSTATS_VMM_RAM) == VMSTATS_VMM_RAM) { mAllocVMM = aAllocVMM; mFreeVMM = aFreeVMM; mBalloonedVMM = aBalloonedVMM; mSharedVMM = aSharedVMM; } + if ((aValidStats & VMSTATS_NET_RATE) == VMSTATS_NET_RATE) + { + mVmNetRx = aVmNetRx; + mVmNetTx = aVmNetTx; + } mValid = aValidStats; } @@ -446,7 +466,7 @@ void CollectorGuestManager::unregisterGuest(CollectorGuest* pGuest) { /* Found the guest already collecting stats, elect it */ mVMMStatsProvider = *it; - rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(GUESTSTATS_VMMRAM)); + rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(VMSTATS_VMM_RAM)); if (FAILED(rc)) { /* This is not a good candidate -- try to find another */ @@ -466,8 +486,8 @@ void CollectorGuestManager::unregisterGuest(CollectorGuest* pGuest) continue; mVMMStatsProvider = *it; - //mVMMStatsProvider->enable(GUESTSTATS_VMMRAM); - rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(GUESTSTATS_VMMRAM)); + //mVMMStatsProvider->enable(VMSTATS_VMM_RAM); + rc = mVMMStatsProvider->enqueueRequest(new CGRQEnable(VMSTATS_VMM_RAM)); if (SUCCEEDED(rc)) break; /* This was not a good candidate -- try to find another */ @@ -508,7 +528,7 @@ int CollectorGuestManager::enqueueRequest(CollectorGuestRequest *aRequest) */ if (aRequest->getGuest() && aRequest->getGuest() == mGuestBeingCalled) { - /* + /* * Before we can declare a guest blocked we need to wait for a while * and then check again as it may never had a chance to process * the previous request. Half a second is an eternity for processes @@ -619,6 +639,12 @@ void HostCpuLoad::collect() } } +void HostCpuLoadRaw::init(ULONG period, ULONG length) +{ + HostCpuLoad::init(period, length); + mHAL->getRawHostCpuLoad(&mUserPrev, &mKernelPrev, &mIdlePrev); +} + void HostCpuLoadRaw::preCollect(CollectorHints& hints, uint64_t /* iTick */) { hints.collectHostCpuLoad(); @@ -659,13 +685,50 @@ void HostCpuLoadRaw::collect() } } +#ifndef VBOX_COLLECTOR_TEST_CASE +static bool getLinkSpeed(const char *szShortName, uint32_t *pSpeed) +{ + NETIFSTATUS enmState = NETIF_S_UNKNOWN; + int rc = NetIfGetState(szShortName, &enmState); + if (RT_FAILURE(rc)) + return false; + if (enmState != NETIF_S_UP) + *pSpeed = 0; + else + { + rc = NetIfGetLinkSpeed(szShortName, pSpeed); + if (RT_FAILURE(rc)) + return false; + } + return true; +} + +void HostNetworkSpeed::init(ULONG period, ULONG length) +{ + mPeriod = period; + mLength = length; + mLinkSpeed->init(length); + /* + * Retrieve the link speed now as it may be wrong if the metric was + * registered at boot (see @bugref{6613}). + */ + getLinkSpeed(mShortName.c_str(), &mSpeed); +} + void HostNetworkLoadRaw::init(ULONG period, ULONG length) { mPeriod = period; mLength = length; mRx->init(mLength); mTx->init(mLength); - int rc = mHAL->getRawHostNetworkLoad(mShortName.c_str(), &mRxPrev, &mTxPrev); + /* + * Retrieve the link speed now as it may be wrong if the metric was + * registered at boot (see @bugref{6613}). + */ + uint32_t uSpeedMbit = 65535; + if (getLinkSpeed(mShortName.c_str(), &uSpeedMbit)) + mSpeed = (uint64_t)uSpeedMbit * (1000000/8); /* Convert to bytes/sec */ + /*int rc =*/ mHAL->getRawHostNetworkLoad(mShortName.c_str(), &mRxPrev, &mTxPrev); //AssertRC(rc); } @@ -686,25 +749,23 @@ void HostNetworkLoadRaw::preCollect(CollectorHints& /* hints */, uint64_t /* iTi void HostNetworkLoadRaw::collect() { - uint64_t rx, tx; + uint64_t rx = mRxPrev; + uint64_t tx = mTxPrev; + if (RT_UNLIKELY(mSpeed * getPeriod() == 0)) + { + LogFlowThisFunc(("Check cable for %s! speed=%llu period=%d.\n", mShortName.c_str(), mSpeed, getPeriod())); + /* We do not collect host network metrics for unplugged interfaces! */ + return; + } mRc = mHAL->getRawHostNetworkLoad(mShortName.c_str(), &rx, &tx); if (RT_SUCCESS(mRc)) { uint64_t rxDiff = rx - mRxPrev; uint64_t txDiff = tx - mTxPrev; - if (RT_UNLIKELY(mSpeed * getPeriod() == 0)) - { - LogFlowThisFunc(("Check cable for %s! speed=%llu period=%d.\n", mShortName.c_str(), mSpeed, getPeriod())); - mRx->put(0); - mTx->put(0); - } - else - { - mRx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * rxDiff / (mSpeed * getPeriod()))); - mTx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * txDiff / (mSpeed * getPeriod()))); - } + mRx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * rxDiff / (mSpeed * getPeriod()))); + mTx->put((ULONG)(PM_NETWORK_LOAD_MULTIPLIER * txDiff / (mSpeed * getPeriod()))); mRxPrev = rx; mTxPrev = tx; @@ -713,6 +774,7 @@ void HostNetworkLoadRaw::collect() LogFlowThisFunc(("Failed to collect data: %Rrc (%d)." " Will update the list of interfaces...\n", mRc,mRc)); } +#endif /* !VBOX_COLLECTOR_TEST_CASE */ void HostDiskLoadRaw::init(ULONG period, ULONG length) { @@ -815,7 +877,6 @@ void HostRamUsage::collect() mTotal->put(total); mUsed->put(used); mAvailable->put(available); - } } @@ -841,10 +902,28 @@ void HostFilesystemUsage::collect() mTotal->put(total); mUsed->put(used); mAvailable->put(available); - } } +void HostDiskUsage::init(ULONG period, ULONG length) +{ + mPeriod = period; + mLength = length; + mTotal->init(mLength); +} + +void HostDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */) +{ +} + +void HostDiskUsage::collect() +{ + uint64_t total; + int rc = mHAL->getHostDiskSize(mDiskName.c_str(), &total); + if (RT_SUCCESS(rc)) + mTotal->put((ULONG)(total / _1M)); +} + #ifndef VBOX_COLLECTOR_TEST_CASE void HostRamVmm::init(ULONG period, ULONG length) { @@ -861,7 +940,7 @@ int HostRamVmm::enable() int rc = S_OK; CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider(); if (provider) - rc = provider->enable(GUESTSTATS_VMMRAM); + rc = provider->enable(VMSTATS_VMM_RAM); BaseMetric::enable(); return rc; } @@ -872,7 +951,7 @@ int HostRamVmm::disable() BaseMetric::disable(); CollectorGuest *provider = mCollectorGuestManager->getVMMStatsProvider(); if (provider) - rc = provider->disable(GUESTSTATS_VMMRAM); + rc = provider->disable(VMSTATS_VMM_RAM); return rc; } @@ -888,15 +967,15 @@ void HostRamVmm::collect() { LogAleksey(("{%p} " LOG_FN_FMT ": provider=%p enabled=%s valid=%s...\n", this, __PRETTY_FUNCTION__, provider, provider->isEnabled()?"y":"n", - provider->isValid(GUESTSTATS_VMMRAM)?"y":"n")); - if (provider->isValid(GUESTSTATS_VMMRAM)) + provider->isValid(VMSTATS_VMM_RAM)?"y":"n")); + if (provider->isValid(VMSTATS_VMM_RAM)) { /* Provider is ready, get updated stats */ mAllocCurrent = provider->getAllocVMM(); mFreeCurrent = provider->getFreeVMM(); mBalloonedCurrent = provider->getBalloonedVMM(); mSharedCurrent = provider->getSharedVMM(); - provider->invalidate(GUESTSTATS_VMMRAM); + provider->invalidate(VMSTATS_VMM_RAM); } /* * Note that if there are no new values from the provider we will use @@ -994,6 +1073,76 @@ void MachineRamUsage::collect() #ifndef VBOX_COLLECTOR_TEST_CASE +void MachineDiskUsage::init(ULONG period, ULONG length) +{ + mPeriod = period; + mLength = length; + mUsed->init(mLength); +} + +void MachineDiskUsage::preCollect(CollectorHints& /* hints */, uint64_t /* iTick */) +{ +} + +void MachineDiskUsage::collect() +{ + ULONG used = 0; + + for (MediaList::iterator it = mDisks.begin(); it != mDisks.end(); ++it) + { + ComObjPtr<Medium> pMedium = *it; + + /* just in case */ + AssertStmt(!pMedium.isNull(), continue); + + AutoCaller localAutoCaller(pMedium); + if (FAILED(localAutoCaller.rc())) continue; + + AutoReadLock local_alock(pMedium COMMA_LOCKVAL_SRC_POS); + + used += (ULONG)(pMedium->getSize() / _1M); + } + + mUsed->put(used); +} + +void MachineNetRate::init(ULONG period, ULONG length) +{ + mPeriod = period; + mLength = length; + + mRx->init(mLength); + mTx->init(mLength); +} + +void MachineNetRate::collect() +{ + if (mCGuest->isValid(VMSTATS_NET_RATE)) + { + mRx->put(mCGuest->getVmNetRx()); + mTx->put(mCGuest->getVmNetTx()); + mCGuest->invalidate(VMSTATS_NET_RATE); + } +} + +int MachineNetRate::enable() +{ + int rc = mCGuest->enable(VMSTATS_NET_RATE); + BaseMetric::enable(); + return rc; +} + +int MachineNetRate::disable() +{ + BaseMetric::disable(); + return mCGuest->disable(VMSTATS_NET_RATE); +} + +void MachineNetRate::preCollect(CollectorHints& hints, uint64_t /* iTick */) +{ + hints.collectGuestStats(mCGuest->getProcess()); +} + void GuestCpuLoad::init(ULONG period, ULONG length) { mPeriod = period; @@ -1011,18 +1160,18 @@ void GuestCpuLoad::preCollect(CollectorHints& hints, uint64_t /* iTick */) void GuestCpuLoad::collect() { - if (mCGuest->isValid(GUESTSTATS_CPULOAD)) + if (mCGuest->isValid(VMSTATS_GUEST_CPULOAD)) { mUser->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuUser()) / 100); mKernel->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuKernel()) / 100); mIdle->put((ULONG)(PM_CPU_LOAD_MULTIPLIER * mCGuest->getCpuIdle()) / 100); - mCGuest->invalidate(GUESTSTATS_CPULOAD); + mCGuest->invalidate(VMSTATS_GUEST_CPULOAD); } } int GuestCpuLoad::enable() { - int rc = mCGuest->enable(GUESTSTATS_CPULOAD); + int rc = mCGuest->enable(VMSTATS_GUEST_CPULOAD); BaseMetric::enable(); return rc; } @@ -1030,7 +1179,7 @@ int GuestCpuLoad::enable() int GuestCpuLoad::disable() { BaseMetric::disable(); - return mCGuest->disable(GUESTSTATS_CPULOAD); + return mCGuest->disable(VMSTATS_GUEST_CPULOAD); } void GuestRamUsage::init(ULONG period, ULONG length) @@ -1048,7 +1197,7 @@ void GuestRamUsage::init(ULONG period, ULONG length) void GuestRamUsage::collect() { - if (mCGuest->isValid(GUESTSTATS_RAMUSAGE)) + if (mCGuest->isValid(VMSTATS_GUEST_RAMUSAGE)) { mTotal->put(mCGuest->getMemTotal()); mFree->put(mCGuest->getMemFree()); @@ -1056,13 +1205,13 @@ void GuestRamUsage::collect() mShared->put(mCGuest->getMemShared()); mCache->put(mCGuest->getMemCache()); mPagedTotal->put(mCGuest->getPageTotal()); - mCGuest->invalidate(GUESTSTATS_RAMUSAGE); + mCGuest->invalidate(VMSTATS_GUEST_RAMUSAGE); } } int GuestRamUsage::enable() { - int rc = mCGuest->enable(GUESTSTATS_RAMUSAGE); + int rc = mCGuest->enable(VMSTATS_GUEST_RAMUSAGE); BaseMetric::enable(); return rc; } @@ -1070,7 +1219,7 @@ int GuestRamUsage::enable() int GuestRamUsage::disable() { BaseMetric::disable(); - return mCGuest->disable(GUESTSTATS_RAMUSAGE); + return mCGuest->disable(VMSTATS_GUEST_RAMUSAGE); } void GuestRamUsage::preCollect(CollectorHints& hints, uint64_t /* iTick */) diff --git a/src/VBox/Main/src-server/PerformanceImpl.cpp b/src/VBox/Main/src-server/PerformanceImpl.cpp index b233f0a7..87ad1c4c 100644 --- a/src/VBox/Main/src-server/PerformanceImpl.cpp +++ b/src/VBox/Main/src-server/PerformanceImpl.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2008-2010 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/ProgressProxyImpl.cpp b/src/VBox/Main/src-server/ProgressProxyImpl.cpp index a6628760..60750d6f 100644 --- a/src/VBox/Main/src-server/ProgressProxyImpl.cpp +++ b/src/VBox/Main/src-server/ProgressProxyImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2010 Oracle Corporation + * Copyright (C) 2010-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/SerialPortImpl.cpp b/src/VBox/Main/src-server/SerialPortImpl.cpp index 81234c49..5bbf4ba3 100644 --- a/src/VBox/Main/src-server/SerialPortImpl.cpp +++ b/src/VBox/Main/src-server/SerialPortImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/SnapshotImpl.cpp b/src/VBox/Main/src-server/SnapshotImpl.cpp index 4740b122..411cdd56 100644 --- a/src/VBox/Main/src-server/SnapshotImpl.cpp +++ b/src/VBox/Main/src-server/SnapshotImpl.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -29,6 +29,7 @@ // to remove them and put that code in shared code in MachineImplcpp #include "SharedFolderImpl.h" #include "USBControllerImpl.h" +#include "USBDeviceFiltersImpl.h" #include "VirtualBoxImpl.h" #include "AutoCaller.h" @@ -41,6 +42,7 @@ #include <VBox/settings.h> + //////////////////////////////////////////////////////////////////////////////// // // Snapshot private data definition @@ -113,7 +115,7 @@ HRESULT Snapshot::init(VirtualBox *aVirtualBox, { LogFlowThisFunc(("uuid=%s aParent->uuid=%s\n", aId.toString().c_str(), (aParent) ? aParent->m->uuid.toString().c_str() : "")); - ComAssertRet(!aId.isEmpty() && !aName.isEmpty() && aMachine, E_INVALIDARG); + ComAssertRet(!aId.isZero() && aId.isValid() && !aName.isEmpty() && aMachine, E_INVALIDARG); /* Enclose the state transition NotReady->InInit->Ready */ AutoInitSpan autoInitSpan(this); @@ -327,7 +329,8 @@ STDMETHODIMP Snapshot::COMSETTER(Name)(IN_BSTR aName) // prohibit setting a UUID only as the machine name, or else it can // never be found by findMachine() Guid test(aName); - if (test.isNotEmpty()) + + if (!test.isZero() && test.isValid()) return setError(E_INVALIDARG, tr("A machine cannot have a UUID as its name")); AutoCaller autoCaller(this); @@ -493,6 +496,31 @@ const Utf8Str& Snapshot::getStateFilePath() const } /** + * Returns the depth in the snapshot tree for this snapshot. + * + * @note takes the snapshot tree lock + */ + +uint32_t Snapshot::getDepth() +{ + AutoCaller autoCaller(this); + AssertComRC(autoCaller.rc()); + + // snapshots tree is protected by machine lock + AutoReadLock alock(m->pMachine COMMA_LOCKVAL_SRC_POS); + + uint32_t cDepth = 0; + ComObjPtr<Snapshot> pSnap(this); + while (!pSnap.isNull()) + { + pSnap = pSnap->m->pParent; + cDepth++; + } + + return cDepth; +} + +/** * Returns the number of direct child snapshots, without grandchildren. * Does not recurse. * @return @@ -788,11 +816,19 @@ HRESULT Snapshot::saveSnapshotImpl(settings::Snapshot &data, bool aAttrsOnly) it != m->llChildren.end(); ++it) { - settings::Snapshot snap; - rc = (*it)->saveSnapshotImpl(snap, aAttrsOnly); - if (FAILED(rc)) return rc; + // Use the heap to reduce the stack footprint. Each recursion needs + // over 1K, and there can be VMs with deeply nested snapshots. The + // stack can be quite small, especially with XPCOM. - data.llChildSnapshots.push_back(snap); + settings::Snapshot *snap = new settings::Snapshot(); + rc = (*it)->saveSnapshotImpl(*snap, aAttrsOnly); + if (FAILED(rc)) + { + delete snap; + return rc; + } + data.llChildSnapshots.push_back(*snap); + delete snap; } } @@ -954,7 +990,8 @@ HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine, LogFlowThisFuncEnter(); LogFlowThisFunc(("mName={%s}\n", aSessionMachine->mUserData->s.strName.c_str())); - AssertReturn(aSessionMachine && !Guid(aSnapshotId).isEmpty(), E_INVALIDARG); + Guid l_guid(aSnapshotId); + AssertReturn(aSessionMachine && (!l_guid.isZero() && l_guid.isValid()), E_INVALIDARG); /* Enclose the state transition NotReady->InInit->Ready */ AutoInitSpan autoInitSpan(this); @@ -1040,8 +1077,22 @@ HRESULT SnapshotMachine::init(SessionMachine *aSessionMachine, unconst(mAudioAdapter).createObject(); mAudioAdapter->initCopy(this, pMachine->mAudioAdapter); - unconst(mUSBController).createObject(); - mUSBController->initCopy(this, pMachine->mUSBController); + /* create copies of all USB controllers (mUSBControllerData + * after attaching a copy contains just references to original objects) */ + mUSBControllers.allocate(); + for (USBControllerList::const_iterator + it = aSessionMachine->mUSBControllers->begin(); + it != aSessionMachine->mUSBControllers->end(); + ++it) + { + ComObjPtr<USBController> ctrl; + ctrl.createObject(); + ctrl->initCopy(this, *it); + mUSBControllers->push_back(ctrl); + } + + unconst(mUSBDeviceFilters).createObject(); + mUSBDeviceFilters->initCopy(this, pMachine->mUSBDeviceFilters); mNetworkAdapters.resize(pMachine->mNetworkAdapters.size()); for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) @@ -1095,7 +1146,8 @@ HRESULT SnapshotMachine::initFromSettings(Machine *aMachine, LogFlowThisFuncEnter(); LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str())); - AssertReturn(aMachine && !Guid(aSnapshotId).isEmpty(), E_INVALIDARG); + Guid l_guid(aSnapshotId); + AssertReturn(aMachine && (!l_guid.isZero() && l_guid.isValid()), E_INVALIDARG); /* Enclose the state transition NotReady->InInit->Ready */ AutoInitSpan autoInitSpan(this); @@ -1122,6 +1174,7 @@ HRESULT SnapshotMachine::initFromSettings(Machine *aMachine, mHWData.allocate(); mMediaData.allocate(); mStorageControllers.allocate(); + mUSBControllers.allocate(); /* SSData is always unique for SnapshotMachine */ mSSData.allocate(); @@ -1138,8 +1191,8 @@ HRESULT SnapshotMachine::initFromSettings(Machine *aMachine, unconst(mAudioAdapter).createObject(); mAudioAdapter->init(this); - unconst(mUSBController).createObject(); - mUSBController->init(this); + unconst(mUSBDeviceFilters).createObject(); + mUSBDeviceFilters->init(this); mNetworkAdapters.resize(Global::getMaxNetworkAdapters(mHWData->mChipsetType)); for (ULONG slot = 0; slot < mNetworkAdapters.size(); slot++) @@ -1381,7 +1434,7 @@ struct SessionMachine::DeleteSnapshotTask * * When the console is done, it calls SessionMachine::EndTakingSnapshot(). * - * @note Locks mParent + this object for writing. + * @note Locks mParent + this object for writing. * * @param aInitiator in: The console on which Console::TakeSnapshot was called. * @param aName in: The name for the new snapshot. @@ -1416,6 +1469,14 @@ STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator, AssertReturn(mConsoleTaskData.mLastState == MachineState_Null, E_FAIL); AssertReturn(mConsoleTaskData.mSnapshot.isNull(), E_FAIL); + if ( mData->mCurrentSnapshot + && mData->mCurrentSnapshot->getDepth() >= SETTINGS_SNAPSHOT_DEPTH_MAX) + { + return setError(VBOX_E_INVALID_OBJECT_STATE, + tr("Cannot take another snapshot for machine '%s', because it exceeds the maximum snapshot depth limit. Please delete some earlier snapshot which you no longer need"), + mUserData->s.strName.c_str()); + } + if ( !fTakingSnapshotOnline && mData->mMachineState != MachineState_Saved ) @@ -1435,8 +1496,16 @@ STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator, Utf8Str strStateFilePath; /* stateFilePath is null when the machine is not online nor saved */ if (fTakingSnapshotOnline) - // creating a new online snapshot: then we need a fresh saved state file - composeSavedStateFilename(strStateFilePath); + { + Bstr value; + HRESULT rc = GetExtraData(Bstr("VBoxInternal2/ForceTakeSnapshotWithoutState").raw(), + value.asOutParam()); + if (FAILED(rc) || value != "1") + { + // creating a new online snapshot: we need a fresh saved state file + composeSavedStateFilename(strStateFilePath); + } + } else if (mData->mMachineState == MachineState_Saved) // taking an online snapshot from machine in "saved" state: then use existing state file strStateFilePath = mSSData->strStateFilePath; @@ -1499,9 +1568,9 @@ STDMETHODIMP SessionMachine::BeginTakingSnapshot(IConsole *aInitiator, if (FAILED(rc)) throw rc; - // if we got this far without an error, then save the media registries - // that got modified for the diff images - mParent->saveModifiedRegistries(); + // MUST NOT save the settings or the media registry here, because + // this causes trouble with rolling back settings if the user cancels + // taking the snapshot after the diff images have been created. } catch (HRESULT hrc) { @@ -1608,11 +1677,14 @@ STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess) /* inform callbacks */ mParent->onSnapshotTaken(mData->mUuid, mConsoleTaskData.mSnapshot->getId()); + machineLock.release(); } else { /* delete all differencing hard disks created (this will also attach * their parents back by rolling back mMediaData) */ + machineLock.release(); + rollbackMedia(); mData->mFirstSnapshot = pOldFirstSnap; // might have been changed above @@ -1624,15 +1696,19 @@ STDMETHODIMP SessionMachine::EndTakingSnapshot(BOOL aSuccess) // snapshot means that a new saved state file was created, which we must // clean up now RTFileDelete(mConsoleTaskData.mSnapshot->getStateFilePath().c_str()); + machineLock.acquire(); + mConsoleTaskData.mSnapshot->uninit(); + machineLock.release(); + } /* clear out the snapshot data */ mConsoleTaskData.mLastState = MachineState_Null; mConsoleTaskData.mSnapshot.setNull(); - machineLock.release(); + /* machineLock has been released already */ mParent->saveModifiedRegistries(); @@ -2027,7 +2103,7 @@ void SessionMachine::restoreSnapshotHandler(RestoreSnapshotTask &aTask) //////////////////////////////////////////////////////////////////////////////// /** - * Implementation for IInternalMachineControl::deleteSnapshot(). + * Implementation for IInternalMachineControl::DeleteSnapshot(). * * Gets called from Console::DeleteSnapshot(), and that's basically the * only thing Console does initially. Deleting a snapshot happens entirely on @@ -2053,7 +2129,9 @@ STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator, Guid startId(aStartId); Guid endId(aEndId); - AssertReturn(aInitiator && !startId.isEmpty() && !endId.isEmpty(), E_INVALIDARG); + + AssertReturn(aInitiator && !startId.isZero() && !endId.isZero() && startId.isValid() && endId.isValid(), E_INVALIDARG); + AssertReturn(aMachineState && aProgress, E_POINTER); /** @todo implement the "and all children" and "range" variants */ @@ -2086,10 +2164,15 @@ STDMETHODIMP SessionMachine::DeleteSnapshot(IConsole *aInitiator, size_t childrenCount = pSnapshot->getChildrenCount(); if (childrenCount > 1) return setError(VBOX_E_INVALID_OBJECT_STATE, - tr("Snapshot '%s' of the machine '%s' cannot be deleted. because it has %d child snapshots, which is more than the one snapshot allowed for deletion"), + tr("Snapshot '%s' of the machine '%s' cannot be deleted, because it has %d child snapshots, which is more than the one snapshot allowed for deletion"), pSnapshot->getName().c_str(), mUserData->s.strName.c_str(), childrenCount); + if (pSnapshot == mData->mCurrentSnapshot && childrenCount >= 1) + return setError(VBOX_E_INVALID_OBJECT_STATE, + tr("Snapshot '%s' of the machine '%s' cannot be deleted, because it is the current snapshot and has one child snapshot"), + pSnapshot->getName().c_str(), + mUserData->s.strName.c_str()); /* If the snapshot being deleted is the current one, ensure current * settings are committed and saved. @@ -2215,18 +2298,20 @@ struct MediumDeleteRec const ComObjPtr<MediumAttachment> &aOnlineMediumAttachment, bool fMergeForward, const ComObjPtr<Medium> &aParentForTarget, - const MediaList &aChildrenToReparent, + MediumLockList *aChildrenToReparent, bool fNeedsOnlineMerge, - MediumLockList *aMediumLockList) + MediumLockList *aMediumLockList, + const ComPtr<IToken> &aHDLockToken) : mpHD(aHd), mpSource(aSource), mpTarget(aTarget), mpOnlineMediumAttachment(aOnlineMediumAttachment), mfMergeForward(fMergeForward), mpParentForTarget(aParentForTarget), - mChildrenToReparent(aChildrenToReparent), + mpChildrenToReparent(aChildrenToReparent), mfNeedsOnlineMerge(fNeedsOnlineMerge), - mpMediumLockList(aMediumLockList) + mpMediumLockList(aMediumLockList), + mpHDLockToken(aHDLockToken) {} MediumDeleteRec(const ComObjPtr<Medium> &aHd, @@ -2235,9 +2320,10 @@ struct MediumDeleteRec const ComObjPtr<MediumAttachment> &aOnlineMediumAttachment, bool fMergeForward, const ComObjPtr<Medium> &aParentForTarget, - const MediaList &aChildrenToReparent, + MediumLockList *aChildrenToReparent, bool fNeedsOnlineMerge, MediumLockList *aMediumLockList, + const ComPtr<IToken> &aHDLockToken, const Guid &aMachineId, const Guid &aSnapshotId) : mpHD(aHd), @@ -2246,9 +2332,10 @@ struct MediumDeleteRec mpOnlineMediumAttachment(aOnlineMediumAttachment), mfMergeForward(fMergeForward), mpParentForTarget(aParentForTarget), - mChildrenToReparent(aChildrenToReparent), + mpChildrenToReparent(aChildrenToReparent), mfNeedsOnlineMerge(fNeedsOnlineMerge), mpMediumLockList(aMediumLockList), + mpHDLockToken(aHDLockToken), mMachineId(aMachineId), mSnapshotId(aSnapshotId) {} @@ -2259,9 +2346,11 @@ struct MediumDeleteRec ComObjPtr<MediumAttachment> mpOnlineMediumAttachment; bool mfMergeForward; ComObjPtr<Medium> mpParentForTarget; - MediaList mChildrenToReparent; + MediumLockList *mpChildrenToReparent; bool mfNeedsOnlineMerge; MediumLockList *mpMediumLockList; + /** optional lock token, used only in case mpHD is not merged/deleted */ + ComPtr<IToken> mpHDLockToken; /* these are for reattaching the hard disk in case of a failure: */ Guid mMachineId; Guid mSnapshotId; @@ -2371,11 +2460,12 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) ComObjPtr<Medium> pSource; bool fMergeForward = false; ComObjPtr<Medium> pParentForTarget; - MediaList childrenToReparent; + MediumLockList *pChildrenToReparent = NULL; bool fNeedsOnlineMerge = false; bool fOnlineMergePossible = aTask.m_fDeleteOnline; MediumLockList *pMediumLockList = NULL; MediumLockList *pVMMALockList = NULL; + ComPtr<IToken> pHDLockToken; ComObjPtr<MediumAttachment> pOnlineMediumAttachment; if (fOnlineMergePossible) { @@ -2408,9 +2498,10 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) fOnlineMergePossible, pVMMALockList, pSource, pTarget, fMergeForward, pParentForTarget, - childrenToReparent, + pChildrenToReparent, fNeedsOnlineMerge, - pMediumLockList); + pMediumLockList, + pHDLockToken); treeLock.acquire(); if (FAILED(rc)) throw rc; @@ -2433,7 +2524,6 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) // then do a backward merge, i.e. merge its only child onto the // base disk. Here we need then to update the attachment that // refers to the child and have it point to the parent instead - Assert(pHD->getParent().isNull()); Assert(pHD->getChildren().size() == 1); ComObjPtr<Medium> pReplaceHD = pHD->getChildren().front(); @@ -2454,7 +2544,7 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) if (pSnapshotId) replaceSnapshotId = *pSnapshotId; - if (!replaceMachineId.isEmpty()) + if (replaceMachineId.isValid() && !replaceMachineId.isZero()) { // Adjust the backreferences, otherwise merging will assert. // Note that the medium attachment object stays associated @@ -2467,9 +2557,10 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) pOnlineMediumAttachment, fMergeForward, pParentForTarget, - childrenToReparent, + pChildrenToReparent, fNeedsOnlineMerge, pMediumLockList, + pHDLockToken, replaceMachineId, replaceSnapshotId)); } @@ -2478,9 +2569,116 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) pOnlineMediumAttachment, fMergeForward, pParentForTarget, - childrenToReparent, + pChildrenToReparent, fNeedsOnlineMerge, - pMediumLockList)); + pMediumLockList, + pHDLockToken)); + } + + { + /*check available place on the storage*/ + RTFOFF pcbTotal = 0; + RTFOFF pcbFree = 0; + uint32_t pcbBlock = 0; + uint32_t pcbSector = 0; + std::multimap<uint32_t,uint64_t> neededStorageFreeSpace; + std::map<uint32_t,const char*> serialMapToStoragePath; + + MediumDeleteRecList::const_iterator it_md = toDelete.begin(); + + while (it_md != toDelete.end()) + { + uint64_t diskSize = 0; + uint32_t pu32Serial = 0; + ComObjPtr<Medium> pSource_local = it_md->mpSource; + ComObjPtr<Medium> pTarget_local = it_md->mpTarget; + ComPtr<IMediumFormat> pTargetFormat; + + { + if ( pSource_local.isNull() + || pSource_local == pTarget_local) + { + ++it_md; + continue; + } + } + + rc = pTarget_local->COMGETTER(MediumFormat)(pTargetFormat.asOutParam()); + if (FAILED(rc)) + throw rc; + + if(pTarget_local->isMediumFormatFile()) + { + int vrc = RTFsQuerySerial(pTarget_local->getLocationFull().c_str(), &pu32Serial); + if (RT_FAILURE(vrc)) + { + rc = setError(E_FAIL, + tr(" Unable to merge storage '%s'. Can't get storage UID "), + pTarget_local->getLocationFull().c_str()); + throw rc; + } + + pSource_local->COMGETTER(Size)((LONG64*)&diskSize); + + /* store needed free space in multimap */ + neededStorageFreeSpace.insert(std::make_pair(pu32Serial,diskSize)); + /* linking storage UID with snapshot path, it is a helper container (just for easy finding needed path) */ + serialMapToStoragePath.insert(std::make_pair(pu32Serial,pTarget_local->getLocationFull().c_str())); + } + + ++it_md; + } + + while (!neededStorageFreeSpace.empty()) + { + std::pair<std::multimap<uint32_t,uint64_t>::iterator,std::multimap<uint32_t,uint64_t>::iterator> ret; + uint64_t commonSourceStoragesSize = 0; + + /* find all records in multimap with identical storage UID*/ + ret = neededStorageFreeSpace.equal_range(neededStorageFreeSpace.begin()->first); + std::multimap<uint32_t,uint64_t>::const_iterator it_ns = ret.first; + + for (; it_ns != ret.second ; ++it_ns) + { + commonSourceStoragesSize += it_ns->second; + } + + /* find appropriate path by storage UID*/ + std::map<uint32_t,const char*>::const_iterator it_sm = serialMapToStoragePath.find(ret.first->first); + /* get info about a storage */ + if (it_sm == serialMapToStoragePath.end()) + { + LogFlowThisFunc((" Path to the storage wasn't found...\n ")); + + rc = setError(E_INVALIDARG, + tr(" Unable to merge storage '%s'. Path to the storage wasn't found. "), + it_sm->second); + throw rc; + } + + int vrc = RTFsQuerySizes(it_sm->second, &pcbTotal, &pcbFree,&pcbBlock, &pcbSector); + if (RT_FAILURE(vrc)) + { + rc = setError(E_FAIL, + tr(" Unable to merge storage '%s'. Can't get the storage size. "), + it_sm->second); + throw rc; + } + + if (commonSourceStoragesSize > (uint64_t)pcbFree) + { + LogFlowThisFunc((" Not enough free space to merge...\n ")); + + rc = setError(E_OUTOFMEMORY, + tr(" Unable to merge storage '%s' - not enough free storage space. "), + it_sm->second); + throw rc; + } + + neededStorageFreeSpace.erase(ret.first, ret.second); + } + + serialMapToStoragePath.clear(); } // we can release the locks now since the machine state is MachineState_DeletingSnapshot @@ -2572,16 +2770,22 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) bool fNeedsSave = false; if (it->mfNeedsOnlineMerge) { + // Put the medium merge information (MediumDeleteRec) where + // SessionMachine::FinishOnlineMergeMedium can get at it. + // This callback will arrive while onlineMergeMedium is + // still executing, and there can't be two tasks. + mConsoleTaskData.mDeleteSnapshotInfo = (void *)&(*it); // online medium merge, in the direction decided earlier rc = onlineMergeMedium(it->mpOnlineMediumAttachment, it->mpSource, it->mpTarget, it->mfMergeForward, it->mpParentForTarget, - it->mChildrenToReparent, + it->mpChildrenToReparent, it->mpMediumLockList, aTask.pProgress, &fNeedsSave); + mConsoleTaskData.mDeleteSnapshotInfo = NULL; } else { @@ -2589,7 +2793,7 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) rc = it->mpSource->mergeTo(it->mpTarget, it->mfMergeForward, it->mpParentForTarget, - it->mChildrenToReparent, + it->mpChildrenToReparent, it->mpMediumLockList, &aTask.pProgress, true /* aWait */); @@ -2708,7 +2912,9 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) mParent->markRegistryModified(getId()); } } - catch (HRESULT aRC) { rc = aRC; } + catch (HRESULT aRC) { + rc = aRC; + } if (FAILED(rc)) { @@ -2726,10 +2932,10 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) ++it) { cancelDeleteSnapshotMedium(it->mpHD, it->mpSource, - it->mChildrenToReparent, + it->mpChildrenToReparent, it->mfNeedsOnlineMerge, - it->mpMediumLockList, it->mMachineId, - it->mSnapshotId); + it->mpMediumLockList, it->mpHDLockToken, + it->mMachineId, it->mSnapshotId); } } @@ -2780,14 +2986,16 @@ void SessionMachine::deleteSnapshotHandler(DeleteSnapshotTask &aTask) * @param aTarget Target hard disk for merge (out). * @param aMergeForward Merge direction decision (out). * @param aParentForTarget New parent if target needs to be reparented (out). - * @param aChildrenToReparent Children which have to be reparented to the - * target (out). + * @param aChildrenToReparent MediumLockList with children which have to be + * reparented to the target (out). * @param fNeedsOnlineMerge Whether this merge needs to be done online (out). * If this is set to @a true then the @a aVMMALockList * parameter has been modified and is returned as * @a aMediumLockList. * @param aMediumLockList Where to store the created medium lock list (may * return NULL if no real merge is necessary). + * @param aHDLockToken Where to store the write lock token for aHD, in case + * it is not merged or deleted (out). * * @note Caller must hold media tree lock for writing. This locks this object * and every medium object on the merge chain for writing. @@ -2801,9 +3009,10 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD ComObjPtr<Medium> &aTarget, bool &aMergeForward, ComObjPtr<Medium> &aParentForTarget, - MediaList &aChildrenToReparent, + MediumLockList * &aChildrenToReparent, bool &fNeedsOnlineMerge, - MediumLockList * &aMediumLockList) + MediumLockList * &aMediumLockList, + ComPtr<IToken> &aHDLockToken) { Assert(!mParent->getMediaTreeLockHandle().isWriteLockOnCurrentThread()); Assert(!fOnlineMergePossible || VALID_PTR(aVMMALockList)); @@ -2816,6 +3025,7 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD && type != MediumType_Shareable && type != MediumType_Readonly, E_FAIL); + aChildrenToReparent = NULL; aMediumLockList = NULL; fNeedsOnlineMerge = false; @@ -2832,7 +3042,7 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD /* lock only, to prevent any usage until the snapshot deletion * is completed */ alock.release(); - return aHD->LockWrite(NULL); + return aHD->LockWrite(aHDLockToken.asOutParam()); } /* the differencing hard disk w/o children will be deleted, protect it @@ -2863,7 +3073,7 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD * deletion, so lock only, to prevent any usage */ childLock.release(); alock.release(); - return aHD->LockWrite(NULL); + return aHD->LockWrite(aHDLockToken.asOutParam()); } aSource = pChild; @@ -2871,9 +3081,30 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD } else { - /* forward merge */ - aSource = aHD; - aTarget = pChild; + /* Determine best merge direction. */ + bool fMergeForward = true; + + childLock.release(); + alock.release(); + HRESULT rc = aHD->queryPreferredMergeDirection(pChild, fMergeForward); + alock.acquire(); + childLock.acquire(); + + if (FAILED(rc) && rc != E_FAIL) + return rc; + + if (fMergeForward) + { + aSource = aHD; + aTarget = pChild; + LogFlowFunc(("Forward merging selected\n")); + } + else + { + aSource = pChild; + aTarget = aHD; + LogFlowFunc(("Backward merging selected\n")); + } } HRESULT rc; @@ -2939,39 +3170,37 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD if (fOnlineMergePossible) { /* we will lock the children of the source for reparenting */ - for (MediaList::const_iterator it = aChildrenToReparent.begin(); - it != aChildrenToReparent.end(); - ++it) + if (aChildrenToReparent && !aChildrenToReparent->IsEmpty()) { - ComObjPtr<Medium> pMedium = *it; - AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); - if (pMedium->getState() == MediumState_Created) - { - mediumLock.release(); - childLock.release(); - alock.release(); - rc = pMedium->LockWrite(NULL); - alock.acquire(); - childLock.acquire(); - mediumLock.acquire(); - if (FAILED(rc)) - throw rc; - } - else + /* Cannot just call aChildrenToReparent->Lock(), as one of + * the children is the one under which the current state of + * the VM is located, and this means it is already locked + * (for reading). Note that no special unlocking is needed, + * because cancelMergeTo will unlock everything locked in + * its context (using the unlock on destruction), and both + * cancelDeleteSnapshotMedium (in case something fails) and + * FinishOnlineMergeMedium re-define the read/write lock + * state of everything which the VM need, search for the + * UpdateLock method calls. */ + childLock.release(); + alock.release(); + rc = aChildrenToReparent->Lock(true /* fSkipOverLockedMedia */); + alock.acquire(); + childLock.acquire(); + MediumLockList::Base::iterator childrenToReparentBegin = aChildrenToReparent->GetBegin(); + MediumLockList::Base::iterator childrenToReparentEnd = aChildrenToReparent->GetEnd(); + for (MediumLockList::Base::iterator it = childrenToReparentBegin; + it != childrenToReparentEnd; + ++it) { - mediumLock.release(); - childLock.release(); - alock.release(); - rc = aVMMALockList->Update(pMedium, true); - alock.acquire(); - childLock.acquire(); - mediumLock.acquire(); - if (FAILED(rc)) + ComObjPtr<Medium> pMedium = it->GetMedium(); + AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); + if (!it->IsLocked()) { mediumLock.release(); childLock.release(); alock.release(); - rc = pMedium->LockWrite(NULL); + rc = aVMMALockList->Update(pMedium, true); alock.acquire(); childLock.acquire(); mediumLock.acquire(); @@ -3058,6 +3287,7 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD * @param aChildrenToReparent Children to unlock. * @param fNeedsOnlineMerge Whether this merge needs to be done online. * @param aMediumLockList Medium locks to cancel. + * @param aHDLockToken Optional write lock token for aHD. * @param aMachineId Machine id to attach the medium to. * @param aSnapshotId Snapshot id to attach the medium to. * @@ -3065,9 +3295,10 @@ HRESULT SessionMachine::prepareDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD */ void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD, const ComObjPtr<Medium> &aSource, - const MediaList &aChildrenToReparent, + MediumLockList *aChildrenToReparent, bool fNeedsOnlineMerge, MediumLockList *aMediumLockList, + const ComPtr<IToken> &aHDLockToken, const Guid &aMachineId, const Guid &aSnapshotId) { @@ -3079,8 +3310,12 @@ void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD, if (aHD->getParent().isNull()) { - HRESULT rc = aHD->UnlockWrite(NULL); - AssertComRC(rc); + Assert(!aHDLockToken.isNull()); + if (!aHDLockToken.isNull()) + { + HRESULT rc = aHDLockToken->Abandon(); + AssertComRC(rc); + } } else { @@ -3128,7 +3363,7 @@ void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD, } } - if (!aMachineId.isEmpty()) + if (aMachineId.isValid() && !aMachineId.isZero()) { // reattach the source media to the snapshot HRESULT rc = aSource->addBackReference(aMachineId, aSnapshotId); @@ -3147,8 +3382,8 @@ void SessionMachine::cancelDeleteSnapshotMedium(const ComObjPtr<Medium> &aHD, * @param aTarget Target hard disk for merge. * @param aMergeForward Merge direction. * @param aParentForTarget New parent if target needs to be reparented. - * @param aChildrenToReparent Children which have to be reparented to the - * target. + * @param aChildrenToReparent Medium lock list with children which have to be + * reparented to the target. * @param aMediumLockList Where to store the created medium lock list (may * return NULL if no real merge is necessary). * @param aProgress Progress indicator. @@ -3159,7 +3394,7 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe const ComObjPtr<Medium> &aTarget, bool fMergeForward, const ComObjPtr<Medium> &aParentForTarget, - const MediaList &aChildrenToReparent, + MediumLockList *aChildrenToReparent, MediumLockList *aMediumLockList, ComObjPtr<Progress> &aProgress, bool *pfNeedsMachineSaveSettings) @@ -3168,6 +3403,9 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe AssertReturn(aTarget != NULL, E_FAIL); AssertReturn(aSource != aTarget, E_FAIL); AssertReturn(aMediumLockList != NULL, E_FAIL); + NOREF(fMergeForward); + NOREF(aParentForTarget); + NOREF(aChildrenToReparent); HRESULT rc = S_OK; @@ -3208,12 +3446,6 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe ComAssertThrow( uSourceIdx != (unsigned)-1 && uTargetIdx != (unsigned)-1, E_FAIL); - // For forward merges, tell the VM what images need to have their - // parent UUID updated. This cannot be done in VBoxSVC, as opening - // the required parent images is not safe while the VM is running. - // For backward merges this will be simply an array of size 0. - com::SafeIfaceArray<IMedium> childrenToReparent(aChildrenToReparent); - ComPtr<IInternalSessionControl> directControl; { AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); @@ -3229,9 +3461,6 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe // updating the medium attachment, chain linking and state. rc = directControl->OnlineMergeMedium(aMediumAttachment, uSourceIdx, uTargetIdx, - aSource, aTarget, - fMergeForward, aParentForTarget, - ComSafeArrayAsInParam(childrenToReparent), aProgress); if (FAILED(rc)) throw rc; @@ -3247,7 +3476,7 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe } /** - * Implementation for IInternalMachineControl::finishOnlineMergeMedium(). + * Implementation for IInternalMachineControl::FinishOnlineMergeMedium(). * * Gets called after the successful completion of an online merge from * Console::onlineMergeMedium(), which gets invoked indirectly above in @@ -3256,17 +3485,11 @@ HRESULT SessionMachine::onlineMergeMedium(const ComObjPtr<MediumAttachment> &aMe * This updates the medium information and medium state so that the VM * can continue with the updated state of the medium chain. */ -STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumAttachment, - IMedium *aSource, - IMedium *aTarget, - BOOL aMergeForward, - IMedium *aParentForTarget, - ComSafeArrayIn(IMedium *, aChildrenToReparent)) +STDMETHODIMP SessionMachine::FinishOnlineMergeMedium() { HRESULT rc = S_OK; - ComObjPtr<Medium> pSource(static_cast<Medium *>(aSource)); - ComObjPtr<Medium> pTarget(static_cast<Medium *>(aTarget)); - ComObjPtr<Medium> pParentForTarget(static_cast<Medium *>(aParentForTarget)); + MediumDeleteRec *pDeleteRec = (MediumDeleteRec *)mConsoleTaskData.mDeleteSnapshotInfo; + AssertReturn(pDeleteRec, E_FAIL); bool fSourceHasChildren = false; // all hard disks but the target were successfully deleted by @@ -3279,36 +3502,35 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA // we delete the last reference to the no longer existing medium object. ComObjPtr<Medium> targetChild; - if (aMergeForward) + if (pDeleteRec->mfMergeForward) { // first, unregister the target since it may become a base // hard disk which needs re-registration - rc = mParent->unregisterMedium(pTarget); + rc = mParent->unregisterMedium(pDeleteRec->mpTarget); AssertComRC(rc); // then, reparent it and disconnect the deleted branch at // both ends (chain->parent() is source's parent) - pTarget->deparent(); - pTarget->setParent(pParentForTarget); - if (pParentForTarget) - pSource->deparent(); + pDeleteRec->mpTarget->deparent(); + pDeleteRec->mpTarget->setParent(pDeleteRec->mpParentForTarget); + if (pDeleteRec->mpParentForTarget) + pDeleteRec->mpSource->deparent(); // then, register again - rc = mParent->registerMedium(pTarget, &pTarget, DeviceType_HardDisk); + rc = mParent->registerMedium(pDeleteRec->mpTarget, &pDeleteRec->mpTarget, DeviceType_HardDisk); AssertComRC(rc); } else { - Assert(pTarget->getChildren().size() == 1); - targetChild = pTarget->getChildren().front(); + Assert(pDeleteRec->mpTarget->getChildren().size() == 1); + targetChild = pDeleteRec->mpTarget->getChildren().front(); // disconnect the deleted branch at the elder end targetChild->deparent(); // Update parent UUIDs of the source's children, reparent them and // disconnect the deleted branch at the younger end - com::SafeIfaceArray<IMedium> childrenToReparent(ComSafeArrayInArg(aChildrenToReparent)); - if (childrenToReparent.size() > 0) + if (pDeleteRec->mpChildrenToReparent && !pDeleteRec->mpChildrenToReparent->IsEmpty()) { fSourceHasChildren = true; // Fix the parent UUID of the images which needs to be moved to @@ -3316,35 +3538,35 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA // but only for reading since the VM is paused. If anything fails // we must continue. The worst possible result is that the images // need manual fixing via VBoxManage to adjust the parent UUID. - MediaList toReparent; - for (size_t i = 0; i < childrenToReparent.size(); i++) - { - Medium *pMedium = static_cast<Medium *>(childrenToReparent[i]); - toReparent.push_back(pMedium); - } treeLock.release(); - pTarget->fixParentUuidOfChildren(toReparent); + pDeleteRec->mpTarget->fixParentUuidOfChildren(pDeleteRec->mpChildrenToReparent); + // The childen are still write locked, unlock them now and don't + // rely on the destructor doing it very late. + pDeleteRec->mpChildrenToReparent->Unlock(); treeLock.acquire(); // obey {parent,child} lock order - AutoWriteLock sourceLock(pSource COMMA_LOCKVAL_SRC_POS); + AutoWriteLock sourceLock(pDeleteRec->mpSource COMMA_LOCKVAL_SRC_POS); - for (size_t i = 0; i < childrenToReparent.size(); i++) + MediumLockList::Base::iterator childrenBegin = pDeleteRec->mpChildrenToReparent->GetBegin(); + MediumLockList::Base::iterator childrenEnd = pDeleteRec->mpChildrenToReparent->GetEnd(); + for (MediumLockList::Base::iterator it = childrenBegin; + it != childrenEnd; + ++it) { - Medium *pMedium = static_cast<Medium *>(childrenToReparent[i]); + Medium *pMedium = it->GetMedium(); AutoWriteLock childLock(pMedium COMMA_LOCKVAL_SRC_POS); pMedium->deparent(); // removes pMedium from source - pMedium->setParent(pTarget); + pMedium->setParent(pDeleteRec->mpTarget); } } } /* unregister and uninitialize all hard disks removed by the merge */ MediumLockList *pMediumLockList = NULL; - MediumAttachment *pMediumAttachment = static_cast<MediumAttachment *>(aMediumAttachment); - rc = mData->mSession.mLockedMedia.Get(pMediumAttachment, pMediumLockList); - const ComObjPtr<Medium> &pLast = aMergeForward ? pTarget : pSource; + rc = mData->mSession.mLockedMedia.Get(pDeleteRec->mpOnlineMediumAttachment, pMediumLockList); + const ComObjPtr<Medium> &pLast = pDeleteRec->mfMergeForward ? pDeleteRec->mpTarget : pDeleteRec->mpSource; AssertReturn(SUCCEEDED(rc) && pMediumLockList, E_FAIL); MediumLockList::Base::iterator lockListBegin = pMediumLockList->GetBegin(); @@ -3360,7 +3582,7 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA const ComObjPtr<Medium> pMedium = mediumLock.GetMedium(); /* The target and all images not merged (readonly) are skipped */ - if ( pMedium == pTarget + if ( pMedium == pDeleteRec->mpTarget || pMedium->getState() == MediumState_LockedRead) { ++it; @@ -3382,10 +3604,10 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA * caller may still hold an AutoCaller instance for it * and therefore we cannot uninit() it (it's therefore * the caller's responsibility) */ - if (pMedium == aSource) + if (pMedium == pDeleteRec->mpSource) { - Assert(pSource->getChildren().size() == 0); - Assert(pSource->getFirstMachineBackrefId() == NULL); + Assert(pDeleteRec->mpSource->getChildren().size() == 0); + Assert(pDeleteRec->mpSource->getFirstMachineBackrefId() == NULL); } /* Delete the medium lock list entry, which also releases the @@ -3421,11 +3643,12 @@ STDMETHODIMP SessionMachine::FinishOnlineMergeMedium(IMediumAttachment *aMediumA * source has no children) then update the medium associated with the * attachment, as the previously associated one (source) is now deleted. * Without the immediate update the VM could not continue running. */ - if (!aMergeForward && !fSourceHasChildren) + if (!pDeleteRec->mfMergeForward && !fSourceHasChildren) { - AutoWriteLock attLock(pMediumAttachment COMMA_LOCKVAL_SRC_POS); - pMediumAttachment->updateMedium(pTarget); + AutoWriteLock attLock(pDeleteRec->mpOnlineMediumAttachment COMMA_LOCKVAL_SRC_POS); + pDeleteRec->mpOnlineMediumAttachment->updateMedium(pDeleteRec->mpTarget); } return S_OK; } + diff --git a/src/VBox/Main/src-server/StorageControllerImpl.cpp b/src/VBox/Main/src-server/StorageControllerImpl.cpp index 100c5b22..b5686933 100644 --- a/src/VBox/Main/src-server/StorageControllerImpl.cpp +++ b/src/VBox/Main/src-server/StorageControllerImpl.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2008-2010 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/SystemPropertiesImpl.cpp b/src/VBox/Main/src-server/SystemPropertiesImpl.cpp index 380f7574..3e0ff614 100644 --- a/src/VBox/Main/src-server/SystemPropertiesImpl.cpp +++ b/src/VBox/Main/src-server/SystemPropertiesImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -90,6 +90,7 @@ HRESULT SystemProperties::init(VirtualBox *aParent) unconst(mParent) = aParent; setDefaultMachineFolder(Utf8Str::Empty); + setLoggingLevel(Utf8Str::Empty); setDefaultHardDiskFormat(Utf8Str::Empty); setVRDEAuthLibrary(Utf8Str::Empty); @@ -97,6 +98,18 @@ HRESULT SystemProperties::init(VirtualBox *aParent) m->ulLogHistoryCount = 3; + + /* On Windows and OS X, HW virtualization use isn't exclusive by + * default so that VT-x or AMD-V can be shared with other + * hypervisors without requiring user intervention. + * NB: See also SystemProperties constructor in settings.h + */ +#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS) + m->fExclusiveHwVirt = false; +#else + m->fExclusiveHwVirt = true; +#endif + HRESULT rc = S_OK; /* Fetch info of all available hd backends. */ @@ -313,6 +326,36 @@ STDMETHODIMP SystemProperties::COMGETTER(MaxBootPosition)(ULONG *aMaxBootPositio } +STDMETHODIMP SystemProperties::COMGETTER(ExclusiveHwVirt)(BOOL *aExclusiveHwVirt) +{ + CheckComArgOutPointerValid(aExclusiveHwVirt); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + *aExclusiveHwVirt = m->fExclusiveHwVirt; + + return S_OK; +} + +STDMETHODIMP SystemProperties::COMSETTER(ExclusiveHwVirt)(BOOL aExclusiveHwVirt) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + m->fExclusiveHwVirt = !!aExclusiveHwVirt; + alock.release(); + + // VirtualBox::saveSettings() needs vbox write lock + AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS); + HRESULT rc = mParent->saveSettings(); + + return rc; +} + STDMETHODIMP SystemProperties::GetMaxNetworkAdapters(ChipsetType_T aChipset, ULONG *count) { CheckComArgOutPointerValid(count); @@ -346,6 +389,7 @@ STDMETHODIMP SystemProperties::GetMaxNetworkAdaptersOfType(ChipsetType_T aChipse { case NetworkAttachmentType_NAT: case NetworkAttachmentType_Internal: + case NetworkAttachmentType_NATNetwork: /* chipset default is OK */ break; case NetworkAttachmentType_Bridged: @@ -531,6 +575,8 @@ STDMETHODIMP SystemProperties::GetDeviceTypesForStorageBus(StorageBus_T aBus, { case StorageBus_IDE: case StorageBus_SATA: + case StorageBus_SCSI: + case StorageBus_SAS: { com::SafeArray<DeviceType_T> saDeviceTypes(2); saDeviceTypes[0] = DeviceType_DVD; @@ -538,14 +584,6 @@ STDMETHODIMP SystemProperties::GetDeviceTypesForStorageBus(StorageBus_T aBus, saDeviceTypes.detachTo(ComSafeArrayOutArg(aDeviceTypes)); break; } - case StorageBus_SCSI: - case StorageBus_SAS: - { - com::SafeArray<DeviceType_T> saDeviceTypes(1); - saDeviceTypes[0] = DeviceType_HardDisk; - saDeviceTypes.detachTo(ComSafeArrayOutArg(aDeviceTypes)); - break; - } case StorageBus_Floppy: { com::SafeArray<DeviceType_T> saDeviceTypes(1); @@ -588,6 +626,36 @@ STDMETHODIMP SystemProperties::GetDefaultIoCacheSettingForStorageController(Stor return S_OK; } +STDMETHODIMP SystemProperties::GetMaxInstancesOfUSBControllerType(ChipsetType_T aChipset, + USBControllerType_T aType, + ULONG *aMaxInstances) +{ + NOREF(aChipset); + CheckComArgOutPointerValid(aMaxInstances); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + ULONG cCtrs = 0; + + /* no need to lock, this is const */ + switch (aType) + { + case USBControllerType_OHCI: + case USBControllerType_EHCI: + { + cCtrs = 1; + break; + } + default: + AssertMsgFailed(("Invalid bus type %d\n", aType)); + } + + *aMaxInstances = cCtrs; + + return S_OK; +} + STDMETHODIMP SystemProperties::COMGETTER(DefaultMachineFolder)(BSTR *aDefaultMachineFolder) { CheckComArgOutPointerValid(aDefaultMachineFolder); @@ -621,6 +689,44 @@ STDMETHODIMP SystemProperties::COMSETTER(DefaultMachineFolder)(IN_BSTR aDefaultM return rc; } +STDMETHODIMP SystemProperties::COMGETTER(LoggingLevel)(BSTR *aLoggingLevel) +{ + CheckComArgOutPointerValid(aLoggingLevel); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + Utf8Str useLoggingLevel(m->strLoggingLevel); + if (useLoggingLevel.isEmpty()) + useLoggingLevel = VBOXSVC_LOG_DEFAULT; + + useLoggingLevel.cloneTo(aLoggingLevel); + return S_OK; +} + + +STDMETHODIMP SystemProperties::COMSETTER(LoggingLevel)(IN_BSTR aLoggingLevel) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + HRESULT rc = setLoggingLevel(aLoggingLevel); + alock.release(); + + if (SUCCEEDED(rc)) + { + AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS); + rc = mParent->saveSettings(); + } + else + LogRel(("Cannot set passed logging level=%ls, or the default one - Error=%Rhrc \n", aLoggingLevel, rc)); + + return rc; +} + STDMETHODIMP SystemProperties::COMGETTER(MediumFormats)(ComSafeArrayOut(IMediumFormat *, aMediumFormats)) { CheckComArgOutSafeArrayPointerValid(aMediumFormats); @@ -997,6 +1103,40 @@ STDMETHODIMP SystemProperties::COMSETTER(DefaultAdditionsISO)(IN_BSTR aDefaultAd return rc; } +STDMETHODIMP SystemProperties::COMGETTER(DefaultFrontend)(BSTR *aDefaultFrontend) +{ + CheckComArgOutPointerValid(aDefaultFrontend); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + m->strDefaultFrontend.cloneTo(aDefaultFrontend); + + return S_OK; +} + +STDMETHODIMP SystemProperties::COMSETTER(DefaultFrontend)(IN_BSTR aDefaultFrontend) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + if (m->strDefaultFrontend == Utf8Str(aDefaultFrontend)) + return S_OK; + HRESULT rc = setDefaultFrontend(aDefaultFrontend); + alock.release(); + + if (SUCCEEDED(rc)) + { + // VirtualBox::saveSettings() needs vbox write lock + AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS); + rc = mParent->saveSettings(); + } + + return rc; +} + // public methods only for internal purposes ///////////////////////////////////////////////////////////////////////////// @@ -1012,6 +1152,9 @@ HRESULT SystemProperties::loadSettings(const settings::SystemProperties &data) rc = setDefaultMachineFolder(data.strDefaultMachineFolder); if (FAILED(rc)) return rc; + rc = setLoggingLevel(data.strLoggingLevel); + if (FAILED(rc)) return rc; + rc = setDefaultHardDiskFormat(data.strDefaultHardDiskFormat); if (FAILED(rc)) return rc; @@ -1025,6 +1168,7 @@ HRESULT SystemProperties::loadSettings(const settings::SystemProperties &data) if (FAILED(rc)) return rc; m->ulLogHistoryCount = data.ulLogHistoryCount; + m->fExclusiveHwVirt = data.fExclusiveHwVirt; rc = setAutostartDatabasePath(data.strAutostartDatabasePath); if (FAILED(rc)) return rc; @@ -1036,6 +1180,9 @@ HRESULT SystemProperties::loadSettings(const settings::SystemProperties &data) (void)setDefaultAdditionsISO(data.strDefaultAdditionsISO); } + rc = setDefaultFrontend(data.strDefaultFrontend); + if (FAILED(rc)) return rc; + return S_OK; } @@ -1074,7 +1221,7 @@ ComObjPtr<MediumFormat> SystemProperties::mediumFormat(const Utf8Str &aFormat) { /* MediumFormat is all const, no need to lock */ - if ((*it)->getId().compare(aFormat, Utf8Str::CaseInsensitive) == 0) + if ((*it)->i_getId().compare(aFormat, Utf8Str::CaseInsensitive) == 0) { format = *it; break; @@ -1107,8 +1254,8 @@ ComObjPtr<MediumFormat> SystemProperties::mediumFormatFromExtension(const Utf8St ++it) { /* MediumFormat is all const, no need to lock */ - MediumFormat::StrList aFileList = (*it)->getFileExtensions(); - for (MediumFormat::StrList::const_iterator it1 = aFileList.begin(); + MediumFormat::StrArray aFileList = (*it)->i_getFileExtensions(); + for (MediumFormat::StrArray::const_iterator it1 = aFileList.begin(); it1 != aFileList.end(); ++it1) { @@ -1177,6 +1324,31 @@ HRESULT SystemProperties::setDefaultMachineFolder(const Utf8Str &strPath) return S_OK; } +HRESULT SystemProperties::setLoggingLevel(const Utf8Str &aLoggingLevel) +{ + Utf8Str useLoggingLevel(aLoggingLevel); + int rc = RTLogGroupSettings(RTLogRelDefaultInstance(), useLoggingLevel.c_str()); + // If failed and not the default logging level - try to use the default logging level. + if (RT_FAILURE(rc)) + { + // If failed write message to the release log. + LogRel(("Cannot set passed logging level=%s Error=%Rrc \n", useLoggingLevel.c_str(), rc)); + // If attempted logging level not the default one then try the default one. + if (!useLoggingLevel.equals(VBOXSVC_LOG_DEFAULT)) + { + rc = RTLogGroupSettings(RTLogRelDefaultInstance(), VBOXSVC_LOG_DEFAULT); + // If failed report this to the release log. + if (RT_FAILURE(rc)) + LogRel(("Cannot set default logging level Error=%Rrc \n", rc)); + } + // On any failure - set default level as the one to be stored. + useLoggingLevel = VBOXSVC_LOG_DEFAULT; + } + // Set to passed value or if default used/attempted (even if error condition) use empty string. + m->strLoggingLevel = (useLoggingLevel.equals(VBOXSVC_LOG_DEFAULT) ? "" : useLoggingLevel); + return RT_SUCCESS(rc) ? S_OK : E_FAIL; +} + HRESULT SystemProperties::setDefaultHardDiskFormat(const Utf8Str &aFormat) { if (!aFormat.isEmpty()) @@ -1288,3 +1460,10 @@ HRESULT SystemProperties::setDefaultAdditionsISO(const Utf8Str &aPath) return S_OK; } + +HRESULT SystemProperties::setDefaultFrontend(const Utf8Str &aDefaultFrontend) +{ + m->strDefaultFrontend = aDefaultFrontend; + + return S_OK; +} diff --git a/src/VBox/Main/src-server/TokenImpl.cpp b/src/VBox/Main/src-server/TokenImpl.cpp new file mode 100644 index 00000000..29802e7b --- /dev/null +++ b/src/VBox/Main/src-server/TokenImpl.cpp @@ -0,0 +1,217 @@ +/* $Id: TokenImpl.cpp $ */ +/** @file + * + * Token COM class implementation: MachineToken and MediumLockToken + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "TokenImpl.h" +#include "MachineImpl.h" +#include "MediumImpl.h" +#include "AutoCaller.h" +#include "Logging.h" + +// constructor / destructor +///////////////////////////////////////////////////////////////////////////// + +DEFINE_EMPTY_CTOR_DTOR(MachineToken) + +HRESULT MachineToken::FinalConstruct() +{ + return BaseFinalConstruct(); +} + +void MachineToken::FinalRelease() +{ + uninit(false); + + BaseFinalRelease(); +} + +// public initializer/uninitializer for internal purposes only +///////////////////////////////////////////////////////////////////////////// + +/** + * Initializes the token object. + * + * @param pSessionMachine Pointer to a SessionMachine object. + */ +HRESULT MachineToken::init(const ComObjPtr<SessionMachine> &pSessionMachine) +{ + LogFlowThisFunc(("pSessionMachine=%p\n", &pSessionMachine)); + + ComAssertRet(!pSessionMachine.isNull(), E_INVALIDARG); + + /* Enclose the state transition NotReady->InInit->Ready */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + m.pSessionMachine = pSessionMachine; + + /* Confirm a successful initialization */ + autoInitSpan.setSucceeded(); + + return S_OK; +} + +/** + * Uninitializes the instance and sets the ready flag to FALSE. + * Called either from FinalRelease() or by the parent when it gets destroyed. + */ +void MachineToken::uninit(bool fAbandon) +{ + LogFlowThisFunc(("\n")); + + /* Enclose the state transition Ready->InUninit->NotReady */ + AutoUninitSpan autoUninitSpan(this); + if (autoUninitSpan.uninitDone()) + return; + + /* Destroy the SessionMachine object, check is paranoia */ + if (!m.pSessionMachine.isNull()) + { + m.pSessionMachine->uninit(fAbandon ? SessionMachine::Uninit::Normal : SessionMachine::Uninit::Abnormal); + m.pSessionMachine.setNull(); + } +} + +// IToken methods +///////////////////////////////////////////////////////////////////////////// + +HRESULT MachineToken::abandon(AutoCaller &aAutoCaller) +{ + /* have to release the AutoCaller before calling uninit(), self-deadlock */ + aAutoCaller.release(); + + /* uninit does everything we need */ + uninit(true); + return S_OK; +} + +HRESULT MachineToken::dummy() +{ + /* Remember, the wrapper contains the AutoCaller, which means that after + * uninit() this code won't be reached any more. */ + + /* this is a NOOP, no need to lock */ + + return S_OK; +} + +// public methods only for internal purposes +///////////////////////////////////////////////////////////////////////////// + + +// constructor / destructor +///////////////////////////////////////////////////////////////////////////// + +DEFINE_EMPTY_CTOR_DTOR(MediumLockToken) + +HRESULT MediumLockToken::FinalConstruct() +{ + return BaseFinalConstruct(); +} + +void MediumLockToken::FinalRelease() +{ + uninit(); + + BaseFinalRelease(); +} + +// public initializer/uninitializer for internal purposes only +///////////////////////////////////////////////////////////////////////////// + +/** + * Initializes the token object. + * + * @param pMedium Pointer to a Medium object. + * @param fWrite True if this is a write lock, false otherwise. + */ +HRESULT MediumLockToken::init(const ComObjPtr<Medium> &pMedium, bool fWrite) +{ + LogFlowThisFunc(("pMedium=%p\n", &pMedium)); + + ComAssertRet(!pMedium.isNull(), E_INVALIDARG); + + /* Enclose the state transition NotReady->InInit->Ready */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + m.pMedium = pMedium; + m.fWrite = fWrite; + + /* Confirm a successful initialization */ + autoInitSpan.setSucceeded(); + + return S_OK; +} + +/** + * Uninitializes the instance and sets the ready flag to FALSE. + * Called either from FinalRelease() or by the parent when it gets destroyed. + */ +void MediumLockToken::uninit() +{ + LogFlowThisFunc(("\n")); + + /* Enclose the state transition Ready->InUninit->NotReady */ + AutoUninitSpan autoUninitSpan(this); + if (autoUninitSpan.uninitDone()) + return; + + /* Release the appropriate lock, check is paranoia */ + if (!m.pMedium.isNull()) + { + if (m.fWrite) + { + HRESULT rc = m.pMedium->unlockWrite(NULL); + AssertComRC(rc); + } + else + { + HRESULT rc = m.pMedium->unlockRead(NULL); + AssertComRC(rc); + } + m.pMedium.setNull(); + } +} + +// IToken methods +///////////////////////////////////////////////////////////////////////////// + +HRESULT MediumLockToken::abandon(AutoCaller &aAutoCaller) +{ + /* have to release the AutoCaller before calling uninit(), self-deadlock */ + aAutoCaller.release(); + + /* uninit does everything we need */ + uninit(); + return S_OK; +} + +HRESULT MediumLockToken::dummy() +{ + /* Remember, the wrapper contains the AutoCaller, which means that after + * uninit() this code won't be reached any more. */ + + /* this is a NOOP, no need to lock */ + + return S_OK; +} + +// public methods only for internal purposes +///////////////////////////////////////////////////////////////////////////// + +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/USBControllerImpl.cpp b/src/VBox/Main/src-server/USBControllerImpl.cpp index 8f4d1c48..18c4f43e 100644 --- a/src/VBox/Main/src-server/USBControllerImpl.cpp +++ b/src/VBox/Main/src-server/USBControllerImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2005-2012 Oracle Corporation + * Copyright (C) 2005-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,18 +21,13 @@ #include "MachineImpl.h" #include "VirtualBoxImpl.h" #include "HostImpl.h" -#ifdef VBOX_WITH_USB -# include "USBDeviceImpl.h" -# include "HostUSBDeviceImpl.h" -# include "USBProxyService.h" -# include "USBDeviceFilterImpl.h" -#endif #include <iprt/string.h> #include <iprt/cpp/utils.h> #include <VBox/err.h> #include <VBox/settings.h> +#include <VBox/com/array.h> #include <algorithm> @@ -43,41 +38,31 @@ // defines ///////////////////////////////////////////////////////////////////////////// -typedef std::list< ComObjPtr<USBDeviceFilter> > DeviceFilterList; - struct BackupableUSBData { BackupableUSBData() - : fEnabled(false), - fEnabledEHCI(false) + : enmType(USBControllerType_Null) { } - BOOL fEnabled; - BOOL fEnabledEHCI; + Utf8Str strName; + USBControllerType_T enmType; }; struct USBController::Data { Data(Machine *pMachine) - : pParent(pMachine), - pHost(pMachine->getVirtualBox()->host()) + : pParent(pMachine) { } ~Data() {}; Machine * const pParent; - Host * const pHost; // peer machine's USB controller const ComObjPtr<USBController> pPeer; Backupable<BackupableUSBData> bd; -#ifdef VBOX_WITH_USB - // the following fields need special backup/rollback/commit handling, - // so they cannot be a part of BackupableData - Backupable<DeviceFilterList> llDeviceFilters; -#endif }; @@ -106,12 +91,18 @@ void USBController::FinalRelease() * * @returns COM result indicator. * @param aParent Pointer to our parent object. + * @param aName The name of the USB controller. + * @param enmType The USB controller type. */ -HRESULT USBController::init(Machine *aParent) +HRESULT USBController::init(Machine *aParent, const Utf8Str &aName, USBControllerType_T enmType) { - LogFlowThisFunc(("aParent=%p\n", aParent)); + LogFlowThisFunc(("aParent=%p aName=\"%s\"\n", aParent, aName.c_str())); - ComAssertRet(aParent, E_INVALIDARG); + ComAssertRet(aParent && !aName.isEmpty(), E_INVALIDARG); + if ( (enmType <= USBControllerType_Null) + || (enmType > USBControllerType_EHCI)) + return setError(E_INVALIDARG, + tr("Invalid USB controller type")); /* Enclose the state transition NotReady->InInit->Ready */ AutoInitSpan autoInitSpan(this); @@ -122,9 +113,8 @@ HRESULT USBController::init(Machine *aParent) /* mPeer is left null */ m->bd.allocate(); -#ifdef VBOX_WITH_USB - m->llDeviceFilters.allocate(); -#endif + m->bd->strName = aName; + m->bd->enmType = enmType; /* Confirm a successful initialization */ autoInitSpan.setSucceeded(); @@ -140,13 +130,22 @@ HRESULT USBController::init(Machine *aParent) * @returns COM result indicator. * @param aParent Pointer to our parent object. * @param aPeer The object to share. + * @param aReshare + * When false, the original object will remain a data owner. + * Otherwise, data ownership will be transferred from the original + * object to this one. + * + * @note This object must be destroyed before the original object + * it shares data with is destroyed. * - * @note This object must be destroyed before the original object - * it shares data with is destroyed. + * @note Locks @a aThat object for writing if @a aReshare is @c true, or for + * reading if @a aReshare is false. */ -HRESULT USBController::init(Machine *aParent, USBController *aPeer) +HRESULT USBController::init(Machine *aParent, USBController *aPeer, + bool fReshare /* = false */) { - LogFlowThisFunc(("aParent=%p, aPeer=%p\n", aParent, aPeer)); + LogFlowThisFunc(("aParent=%p, aPeer=%p, fReshare=%RTbool\n", + aParent, aPeer, fReshare)); ComAssertRet(aParent && aPeer, E_INVALIDARG); @@ -156,24 +155,24 @@ HRESULT USBController::init(Machine *aParent, USBController *aPeer) m = new Data(aParent); - unconst(m->pPeer) = aPeer; + /* sanity */ + AutoCaller peerCaller(aPeer); + AssertComRCReturnRC(peerCaller.rc()); - AutoWriteLock thatlock(aPeer COMMA_LOCKVAL_SRC_POS); - m->bd.share(aPeer->m->bd); + if (fReshare) + { + AutoWriteLock peerLock(aPeer COMMA_LOCKVAL_SRC_POS); -#ifdef VBOX_WITH_USB - /* create copies of all filters */ - m->llDeviceFilters.allocate(); - DeviceFilterList::const_iterator it = aPeer->m->llDeviceFilters->begin(); - while (it != aPeer->m->llDeviceFilters->end()) + unconst(aPeer->m->pPeer) = this; + m->bd.attach (aPeer->m->bd); + } + else { - ComObjPtr<USBDeviceFilter> filter; - filter.createObject(); - filter->init(this, *it); - m->llDeviceFilters->push_back(filter); - ++it; + unconst(m->pPeer) = aPeer; + + AutoReadLock peerLock(aPeer COMMA_LOCKVAL_SRC_POS); + m->bd.share (aPeer->m->bd); } -#endif /* VBOX_WITH_USB */ /* Confirm a successful initialization */ autoInitSpan.setSucceeded(); @@ -204,20 +203,6 @@ HRESULT USBController::initCopy(Machine *aParent, USBController *aPeer) AutoWriteLock thatlock(aPeer COMMA_LOCKVAL_SRC_POS); m->bd.attachCopy(aPeer->m->bd); -#ifdef VBOX_WITH_USB - /* create private copies of all filters */ - m->llDeviceFilters.allocate(); - DeviceFilterList::const_iterator it = aPeer->m->llDeviceFilters->begin(); - while (it != aPeer->m->llDeviceFilters->end()) - { - ComObjPtr<USBDeviceFilter> filter; - filter.createObject(); - filter->initCopy(this, *it); - m->llDeviceFilters->push_back(filter); - ++it; - } -#endif /* VBOX_WITH_USB */ - /* Confirm a successful initialization */ autoInitSpan.setSucceeded(); @@ -238,16 +223,6 @@ void USBController::uninit() if (autoUninitSpan.uninitDone()) return; -#ifdef VBOX_WITH_USB - // uninit all device filters on the list (it's a standard std::list not an ObjectsList - // so we must uninit() manually) - for (DeviceFilterList::iterator it = m->llDeviceFilters->begin(); - it != m->llDeviceFilters->end(); - ++it) - (*it)->uninit(); - - m->llDeviceFilters.free(); -#endif m->bd.free(); unconst(m->pPeer) = NULL; @@ -260,112 +235,29 @@ void USBController::uninit() // IUSBController properties ///////////////////////////////////////////////////////////////////////////// - -STDMETHODIMP USBController::COMGETTER(Enabled)(BOOL *aEnabled) -{ - CheckComArgOutPointerValid(aEnabled); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - - *aEnabled = m->bd->fEnabled; - - return S_OK; -} - - -STDMETHODIMP USBController::COMSETTER(Enabled)(BOOL aEnabled) -{ - LogFlowThisFunc(("aEnabled=%RTbool\n", aEnabled)); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(m->pParent); - if (FAILED(adep.rc())) return adep.rc(); - - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - - if (m->bd->fEnabled != aEnabled) - { - m->bd.backup(); - m->bd->fEnabled = aEnabled; - - // leave the lock for safety - alock.release(); - - AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS); - m->pParent->setModified(Machine::IsModified_USB); - mlock.release(); - - m->pParent->onUSBControllerChange(); - } - - return S_OK; -} - -STDMETHODIMP USBController::COMGETTER(EnabledEHCI)(BOOL *aEnabled) -{ - CheckComArgOutPointerValid(aEnabled); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - - *aEnabled = m->bd->fEnabledEHCI; - - return S_OK; -} - -STDMETHODIMP USBController::COMSETTER(EnabledEHCI)(BOOL aEnabled) +STDMETHODIMP USBController::COMGETTER(Name) (BSTR *aName) { - LogFlowThisFunc(("aEnabled=%RTbool\n", aEnabled)); + CheckComArgOutPointerValid(aName); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(m->pParent); - if (FAILED(adep.rc())) return adep.rc(); - - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - - if (m->bd->fEnabledEHCI != aEnabled) - { - m->bd.backup(); - m->bd->fEnabledEHCI = aEnabled; - - // leave the lock for safety - alock.release(); - - AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS); - m->pParent->setModified(Machine::IsModified_USB); - mlock.release(); - - m->pParent->onUSBControllerChange(); - } + /* strName is constant during life time, no need to lock */ + m->bd->strName.cloneTo(aName); return S_OK; } -STDMETHODIMP USBController::COMGETTER(ProxyAvailable)(BOOL *aEnabled) +STDMETHODIMP USBController::COMGETTER(Type)(USBControllerType_T *aType) { - CheckComArgOutPointerValid(aEnabled); + CheckComArgOutPointerValid(aType); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); -#ifdef VBOX_WITH_USB - *aEnabled = true; -#else - *aEnabled = false; -#endif + *aType = m->bd->enmType; return S_OK; } @@ -377,366 +269,27 @@ STDMETHODIMP USBController::COMGETTER(USBStandard)(USHORT *aUSBStandard) AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* not accessing data -- no need to lock */ - - /** @todo This is no longer correct */ - *aUSBStandard = 0x0101; - - return S_OK; -} - -#ifndef VBOX_WITH_USB -/** - * Fake class for build without USB. - * We need an empty collection & enum for deviceFilters, that's all. - */ -class ATL_NO_VTABLE USBDeviceFilter : - public VirtualBoxBase, - VBOX_SCRIPTABLE_IMPL(IUSBDeviceFilter) -{ -public: - DECLARE_NOT_AGGREGATABLE(USBDeviceFilter) - DECLARE_PROTECT_FINAL_CONSTRUCT() - BEGIN_COM_MAP(USBDeviceFilter) - VBOX_DEFAULT_INTERFACE_ENTRIES(IUSBDeviceFilter) - END_COM_MAP() - - DECLARE_EMPTY_CTOR_DTOR(USBDeviceFilter) - - // IUSBDeviceFilter properties - STDMETHOD(COMGETTER(Name))(BSTR *aName); - STDMETHOD(COMSETTER(Name))(IN_BSTR aName); - STDMETHOD(COMGETTER(Active))(BOOL *aActive); - STDMETHOD(COMSETTER(Active))(BOOL aActive); - STDMETHOD(COMGETTER(VendorId))(BSTR *aVendorId); - STDMETHOD(COMSETTER(VendorId))(IN_BSTR aVendorId); - STDMETHOD(COMGETTER(ProductId))(BSTR *aProductId); - STDMETHOD(COMSETTER(ProductId))(IN_BSTR aProductId); - STDMETHOD(COMGETTER(Revision))(BSTR *aRevision); - STDMETHOD(COMSETTER(Revision))(IN_BSTR aRevision); - STDMETHOD(COMGETTER(Manufacturer))(BSTR *aManufacturer); - STDMETHOD(COMSETTER(Manufacturer))(IN_BSTR aManufacturer); - STDMETHOD(COMGETTER(Product))(BSTR *aProduct); - STDMETHOD(COMSETTER(Product))(IN_BSTR aProduct); - STDMETHOD(COMGETTER(SerialNumber))(BSTR *aSerialNumber); - STDMETHOD(COMSETTER(SerialNumber))(IN_BSTR aSerialNumber); - STDMETHOD(COMGETTER(Port))(BSTR *aPort); - STDMETHOD(COMSETTER(Port))(IN_BSTR aPort); - STDMETHOD(COMGETTER(Remote))(BSTR *aRemote); - STDMETHOD(COMSETTER(Remote))(IN_BSTR aRemote); - STDMETHOD(COMGETTER(MaskedInterfaces))(ULONG *aMaskedIfs); - STDMETHOD(COMSETTER(MaskedInterfaces))(ULONG aMaskedIfs); -}; -#endif /* !VBOX_WITH_USB */ - - -STDMETHODIMP USBController::COMGETTER(DeviceFilters)(ComSafeArrayOut(IUSBDeviceFilter *, aDevicesFilters)) -{ -#ifdef VBOX_WITH_USB - CheckComArgOutSafeArrayPointerValid(aDevicesFilters); - - AutoCaller autoCaller(this); - if(FAILED(autoCaller.rc())) return autoCaller.rc(); - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - SafeIfaceArray<IUSBDeviceFilter> collection(*m->llDeviceFilters.data()); - collection.detachTo(ComSafeArrayOutArg(aDevicesFilters)); - - return S_OK; -#else - NOREF(aDevicesFilters); -# ifndef RT_OS_WINDOWS - NOREF(aDevicesFiltersSize); -# endif - ReturnComNotImplemented(); -#endif -} - -// IUSBController methods -///////////////////////////////////////////////////////////////////////////// - -STDMETHODIMP USBController::CreateDeviceFilter(IN_BSTR aName, - IUSBDeviceFilter **aFilter) -{ -#ifdef VBOX_WITH_USB - CheckComArgOutPointerValid(aFilter); - - CheckComArgStrNotEmptyOrNull(aName); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(m->pParent); - if (FAILED(adep.rc())) return adep.rc(); - - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - - ComObjPtr<USBDeviceFilter> filter; - filter.createObject(); - HRESULT rc = filter->init(this, aName); - ComAssertComRCRetRC(rc); - rc = filter.queryInterfaceTo(aFilter); - AssertComRCReturnRC(rc); - - return S_OK; -#else - NOREF(aName); - NOREF(aFilter); - ReturnComNotImplemented(); -#endif -} - -STDMETHODIMP USBController::InsertDeviceFilter(ULONG aPosition, - IUSBDeviceFilter *aFilter) -{ -#ifdef VBOX_WITH_USB - - CheckComArgNotNull(aFilter); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(m->pParent); - if (FAILED(adep.rc())) return adep.rc(); - - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - - ComObjPtr<USBDeviceFilter> filter = static_cast<USBDeviceFilter*>(aFilter); - // @todo r=dj make sure the input object is actually from us -// if (!filter) -// return setError(E_INVALIDARG, -// tr("The given USB device filter is not created within " -// "this VirtualBox instance")); - - if (filter->mInList) - return setError(VBOX_E_INVALID_OBJECT_STATE, - tr("The given USB device filter is already in the list")); - - /* backup the list before modification */ - m->llDeviceFilters.backup(); - - /* iterate to the position... */ - DeviceFilterList::iterator it; - if (aPosition < m->llDeviceFilters->size()) + switch (m->bd->enmType) { - it = m->llDeviceFilters->begin(); - std::advance(it, aPosition); - } - else - it = m->llDeviceFilters->end(); - /* ...and insert */ - m->llDeviceFilters->insert(it, filter); - filter->mInList = true; - - /* notify the proxy (only when it makes sense) */ - if (filter->getData().mActive && Global::IsOnline(adep.machineState()) - && filter->getData().mRemote.isMatch(false)) - { - USBProxyService *service = m->pHost->usbProxyService(); - ComAssertRet(service, E_FAIL); - - ComAssertRet(filter->getId() == NULL, E_FAIL); - filter->getId() = service->insertFilter(&filter->getData().mUSBFilter); + case USBControllerType_OHCI: + *aUSBStandard = 0x0101; + break; + case USBControllerType_EHCI: + *aUSBStandard = 0x0200; + break; + default: + AssertMsgFailedReturn(("Invalid controller type %d\n", m->bd->enmType), + E_FAIL); } - alock.release(); - AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS); - m->pParent->setModified(Machine::IsModified_USB); - mlock.release(); - return S_OK; - -#else /* VBOX_WITH_USB */ - - NOREF(aPosition); - NOREF(aFilter); - ReturnComNotImplemented(); - -#endif /* VBOX_WITH_USB */ -} - -STDMETHODIMP USBController::RemoveDeviceFilter(ULONG aPosition, - IUSBDeviceFilter **aFilter) -{ -#ifdef VBOX_WITH_USB - - CheckComArgOutPointerValid(aFilter); - - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(m->pParent); - if (FAILED(adep.rc())) return adep.rc(); - - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - - if (!m->llDeviceFilters->size()) - return setError(E_INVALIDARG, - tr("The USB device filter list is empty")); - - if (aPosition >= m->llDeviceFilters->size()) - return setError(E_INVALIDARG, - tr("Invalid position: %lu (must be in range [0, %lu])"), - aPosition, m->llDeviceFilters->size() - 1); - - /* backup the list before modification */ - m->llDeviceFilters.backup(); - - ComObjPtr<USBDeviceFilter> filter; - { - /* iterate to the position... */ - DeviceFilterList::iterator it = m->llDeviceFilters->begin(); - std::advance(it, aPosition); - /* ...get an element from there... */ - filter = *it; - /* ...and remove */ - filter->mInList = false; - m->llDeviceFilters->erase(it); - } - - /* cancel sharing (make an independent copy of data) */ - filter->unshare(); - - filter.queryInterfaceTo(aFilter); - - /* notify the proxy (only when it makes sense) */ - if (filter->getData().mActive && Global::IsOnline(adep.machineState()) - && filter->getData().mRemote.isMatch(false)) - { - USBProxyService *service = m->pHost->usbProxyService(); - ComAssertRet(service, E_FAIL); - - ComAssertRet(filter->getId() != NULL, E_FAIL); - service->removeFilter(filter->getId()); - filter->getId() = NULL; - } - - alock.release(); - AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS); - m->pParent->setModified(Machine::IsModified_USB); - mlock.release(); - - return S_OK; - -#else /* VBOX_WITH_USB */ - - NOREF(aPosition); - NOREF(aFilter); - ReturnComNotImplemented(); - -#endif /* VBOX_WITH_USB */ } // public methods only for internal purposes ///////////////////////////////////////////////////////////////////////////// -/** - * Loads settings from the given machine node. - * May be called once right after this object creation. - * - * @param aMachineNode <Machine> node. - * - * @note Does not lock "this" as Machine::loadHardware, which calls this, does not lock either. - */ -HRESULT USBController::loadSettings(const settings::USBController &data) -{ - AutoCaller autoCaller(this); - AssertComRCReturnRC(autoCaller.rc()); - - /* Note: we assume that the default values for attributes of optional - * nodes are assigned in the Data::Data() constructor and don't do it - * here. It implies that this method may only be called after constructing - * a new BIOSSettings object while all its data fields are in the default - * values. Exceptions are fields whose creation time defaults don't match - * values that should be applied when these fields are not explicitly set - * in the settings file (for backwards compatibility reasons). This takes - * place when a setting of a newly created object must default to A while - * the same setting of an object loaded from the old settings file must - * default to B. */ - - m->bd->fEnabled = data.fEnabled; - m->bd->fEnabledEHCI = data.fEnabledEHCI; - -#ifdef VBOX_WITH_USB - for (settings::USBDeviceFiltersList::const_iterator it = data.llDeviceFilters.begin(); - it != data.llDeviceFilters.end(); - ++it) - { - const settings::USBDeviceFilter &f = *it; - ComObjPtr<USBDeviceFilter> pFilter; - pFilter.createObject(); - HRESULT rc = pFilter->init(this, // parent - f); - if (FAILED(rc)) return rc; - - m->llDeviceFilters->push_back(pFilter); - pFilter->mInList = true; - } -#endif /* VBOX_WITH_USB */ - - return S_OK; -} - -/** - * Saves settings to the given machine node. - * - * @param aMachineNode <Machine> node. - * - * @note Locks this object for reading. - */ -HRESULT USBController::saveSettings(settings::USBController &data) -{ - AutoCaller autoCaller(this); - if (FAILED(autoCaller.rc())) return autoCaller.rc(); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - - data.fEnabled = !!m->bd->fEnabled; - data.fEnabledEHCI = !!m->bd->fEnabledEHCI; - -#ifdef VBOX_WITH_USB - data.llDeviceFilters.clear(); - - for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); - it != m->llDeviceFilters->end(); - ++it) - { - AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS); - const USBDeviceFilter::Data &filterData = (*it)->getData(); - - Bstr str; - - settings::USBDeviceFilter f; - f.strName = filterData.mName; - f.fActive = !!filterData.mActive; - (*it)->COMGETTER(VendorId)(str.asOutParam()); - f.strVendorId = str; - (*it)->COMGETTER(ProductId)(str.asOutParam()); - f.strProductId = str; - (*it)->COMGETTER(Revision)(str.asOutParam()); - f.strRevision = str; - (*it)->COMGETTER(Manufacturer)(str.asOutParam()); - f.strManufacturer = str; - (*it)->COMGETTER(Product)(str.asOutParam()); - f.strProduct = str; - (*it)->COMGETTER(SerialNumber)(str.asOutParam()); - f.strSerialNumber = str; - (*it)->COMGETTER(Port)(str.asOutParam()); - f.strPort = str; - f.strRemote = filterData.mRemote.string(); - f.ulMaskedInterfaces = filterData.mMaskedIfs; - - data.llDeviceFilters.push_back(f); - } -#endif /* VBOX_WITH_USB */ - - return S_OK; -} - /** @note Locks objects for writing! */ void USBController::rollback() { @@ -750,82 +303,6 @@ void USBController::rollback() AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); m->bd.rollback(); - -#ifdef VBOX_WITH_USB - - if (m->llDeviceFilters.isBackedUp()) - { - USBProxyService *service = m->pHost->usbProxyService(); - Assert(service); - - /* uninitialize all new filters (absent in the backed up list) */ - DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); - DeviceFilterList *backedList = m->llDeviceFilters.backedUpData(); - while (it != m->llDeviceFilters->end()) - { - if (std::find(backedList->begin(), backedList->end(), *it) == - backedList->end()) - { - /* notify the proxy (only when it makes sense) */ - if ((*it)->getData().mActive && - Global::IsOnline(adep.machineState()) - && (*it)->getData().mRemote.isMatch(false)) - { - USBDeviceFilter *filter = *it; - Assert(filter->getId() != NULL); - service->removeFilter(filter->getId()); - filter->getId() = NULL; - } - - (*it)->uninit(); - } - ++it; - } - - if (Global::IsOnline(adep.machineState())) - { - /* find all removed old filters (absent in the new list) - * and insert them back to the USB proxy */ - it = backedList->begin(); - while (it != backedList->end()) - { - if (std::find(m->llDeviceFilters->begin(), m->llDeviceFilters->end(), *it) == - m->llDeviceFilters->end()) - { - /* notify the proxy (only when necessary) */ - if ((*it)->getData().mActive - && (*it)->getData().mRemote.isMatch(false)) - { - USBDeviceFilter *flt = *it; /* resolve ambiguity */ - Assert(flt->getId() == NULL); - flt->getId() = service->insertFilter(&flt->getData().mUSBFilter); - } - } - ++it; - } - } - - /* restore the list */ - m->llDeviceFilters.rollback(); - } - - /* here we don't depend on the machine state any more */ - adep.release(); - - /* rollback any changes to filters after restoring the list */ - DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); - while (it != m->llDeviceFilters->end()) - { - if ((*it)->isModified()) - { - (*it)->rollback(); - /* call this to notify the USB proxy about changes */ - onDeviceFilterChange(*it); - } - ++it; - } - -#endif /* VBOX_WITH_USB */ } /** @@ -856,82 +333,6 @@ void USBController::commit() m->pPeer->m->bd.attach(m->bd); } } - -#ifdef VBOX_WITH_USB - bool commitFilters = false; - - if (m->llDeviceFilters.isBackedUp()) - { - m->llDeviceFilters.commit(); - - /* apply changes to peer */ - if (m->pPeer) - { - AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS); - - /* commit all changes to new filters (this will reshare data with - * peers for those who have peers) */ - DeviceFilterList *newList = new DeviceFilterList(); - DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); - while (it != m->llDeviceFilters->end()) - { - (*it)->commit(); - - /* look if this filter has a peer filter */ - ComObjPtr<USBDeviceFilter> peer = (*it)->peer(); - if (!peer) - { - /* no peer means the filter is a newly created one; - * create a peer owning data this filter share it with */ - peer.createObject(); - peer->init(m->pPeer, *it, true /* aReshare */); - } - else - { - /* remove peer from the old list */ - m->pPeer->m->llDeviceFilters->remove(peer); - } - /* and add it to the new list */ - newList->push_back(peer); - - ++it; - } - - /* uninit old peer's filters that are left */ - it = m->pPeer->m->llDeviceFilters->begin(); - while (it != m->pPeer->m->llDeviceFilters->end()) - { - (*it)->uninit(); - ++it; - } - - /* attach new list of filters to our peer */ - m->pPeer->m->llDeviceFilters.attach(newList); - } - else - { - /* we have no peer (our parent is the newly created machine); - * just commit changes to filters */ - commitFilters = true; - } - } - else - { - /* the list of filters itself is not changed, - * just commit changes to filters themselves */ - commitFilters = true; - } - - if (commitFilters) - { - DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); - while (it != m->llDeviceFilters->end()) - { - (*it)->commit(); - ++it; - } - } -#endif /* VBOX_WITH_USB */ } /** @@ -963,295 +364,56 @@ void USBController::copyFrom(USBController *aThat) /* this will back up current data */ m->bd.assignCopy(aThat->m->bd); - -#ifdef VBOX_WITH_USB - - /* Note that we won't inform the USB proxy about new filters since the VM is - * not running when we are here and therefore no need to do so */ - - /* create private copies of all filters */ - m->llDeviceFilters.backup(); - m->llDeviceFilters->clear(); - for (DeviceFilterList::const_iterator it = aThat->m->llDeviceFilters->begin(); - it != aThat->m->llDeviceFilters->end(); - ++it) - { - ComObjPtr<USBDeviceFilter> filter; - filter.createObject(); - filter->initCopy(this, *it); - m->llDeviceFilters->push_back(filter); - } - -#endif /* VBOX_WITH_USB */ } -#ifdef VBOX_WITH_USB - /** - * Called by setter methods of all USB device filters. + * Cancels sharing (if any) by making an independent copy of data. + * This operation also resets this object's peer to NULL. * - * @note Locks nothing. + * @note Locks this object for writing, together with the peer object + * represented by @a aThat (locked for reading). */ -HRESULT USBController::onDeviceFilterChange(USBDeviceFilter *aFilter, - BOOL aActiveChanged /* = FALSE */) +void USBController::unshare() { + /* sanity */ AutoCaller autoCaller(this); - AssertComRCReturnRC(autoCaller.rc()); + AssertComRCReturnVoid (autoCaller.rc()); - /* we need the machine state */ - AutoAnyStateDependency adep(m->pParent); - AssertComRCReturnRC(adep.rc()); - - /* nothing to do if the machine isn't running */ - if (!Global::IsOnline(adep.machineState())) - return S_OK; + /* sanity too */ + AutoCaller peerCaller (m->pPeer); + AssertComRCReturnVoid (peerCaller.rc()); - /* we don't modify our data fields -- no need to lock */ + /* peer is not modified, lock it for reading (m->pPeer is "master" so locked + * first) */ + AutoReadLock rl(m->pPeer COMMA_LOCKVAL_SRC_POS); + AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS); - if ( aFilter->mInList - && m->pParent->isRegistered()) + if (m->bd.isShared()) { - USBProxyService *service = m->pHost->usbProxyService(); - ComAssertRet(service, E_FAIL); - - if (aActiveChanged) - { - if (aFilter->getData().mRemote.isMatch(false)) - { - /* insert/remove the filter from the proxy */ - if (aFilter->getData().mActive) - { - ComAssertRet(aFilter->getId() == NULL, E_FAIL); - aFilter->getId() = service->insertFilter(&aFilter->getData().mUSBFilter); - } - else - { - ComAssertRet(aFilter->getId() != NULL, E_FAIL); - service->removeFilter(aFilter->getId()); - aFilter->getId() = NULL; - } - } - } - else - { - if (aFilter->getData().mActive) - { - /* update the filter in the proxy */ - ComAssertRet(aFilter->getId() != NULL, E_FAIL); - service->removeFilter(aFilter->getId()); - if (aFilter->getData().mRemote.isMatch(false)) - { - aFilter->getId() = service->insertFilter(&aFilter->getData().mUSBFilter); - } - } - } - } - - return S_OK; -} - -/** - * Returns true if the given USB device matches to at least one of - * this controller's USB device filters. - * - * A HostUSBDevice specific version. - * - * @note Locks this object for reading. - */ -bool USBController::hasMatchingFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs) -{ - AutoCaller autoCaller(this); - AssertComRCReturn(autoCaller.rc(), false); + if (!m->bd.isBackedUp()) + m->bd.backup(); - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - - /* Disabled USB controllers cannot actually work with USB devices */ - if (!m->bd->fEnabled) - return false; - - /* apply self filters */ - for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); - it != m->llDeviceFilters->end(); - ++it) - { - AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS); - if (aDevice->isMatch((*it)->getData())) - { - *aMaskedIfs = (*it)->getData().mMaskedIfs; - return true; - } + m->bd.commit(); } - return false; + unconst(m->pPeer) = NULL; } -/** - * Returns true if the given USB device matches to at least one of - * this controller's USB device filters. - * - * A generic version that accepts any IUSBDevice on input. - * - * @note - * This method MUST correlate with HostUSBDevice::isMatch() - * in the sense of the device matching logic. - * - * @note Locks this object for reading. - */ -bool USBController::hasMatchingFilter(IUSBDevice *aUSBDevice, ULONG *aMaskedIfs) +const Utf8Str& USBController::getName() const { - LogFlowThisFuncEnter(); - - AutoCaller autoCaller(this); - AssertComRCReturn(autoCaller.rc(), false); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - - /* Disabled USB controllers cannot actually work with USB devices */ - if (!m->bd->fEnabled) - return false; - - HRESULT rc = S_OK; - - /* query fields */ - USBFILTER dev; - USBFilterInit(&dev, USBFILTERTYPE_CAPTURE); - - USHORT vendorId = 0; - rc = aUSBDevice->COMGETTER(VendorId)(&vendorId); - ComAssertComRCRet(rc, false); - ComAssertRet(vendorId, false); - int vrc = USBFilterSetNumExact(&dev, USBFILTERIDX_VENDOR_ID, vendorId, true); AssertRC(vrc); - - USHORT productId = 0; - rc = aUSBDevice->COMGETTER(ProductId)(&productId); - ComAssertComRCRet(rc, false); - vrc = USBFilterSetNumExact(&dev, USBFILTERIDX_PRODUCT_ID, productId, true); AssertRC(vrc); - - USHORT revision; - rc = aUSBDevice->COMGETTER(Revision)(&revision); - ComAssertComRCRet(rc, false); - vrc = USBFilterSetNumExact(&dev, USBFILTERIDX_DEVICE, revision, true); AssertRC(vrc); - - Bstr manufacturer; - rc = aUSBDevice->COMGETTER(Manufacturer)(manufacturer.asOutParam()); - ComAssertComRCRet(rc, false); - if (!manufacturer.isEmpty()) - USBFilterSetStringExact(&dev, USBFILTERIDX_MANUFACTURER_STR, Utf8Str(manufacturer).c_str(), true); - - Bstr product; - rc = aUSBDevice->COMGETTER(Product)(product.asOutParam()); - ComAssertComRCRet(rc, false); - if (!product.isEmpty()) - USBFilterSetStringExact(&dev, USBFILTERIDX_PRODUCT_STR, Utf8Str(product).c_str(), true); - - Bstr serialNumber; - rc = aUSBDevice->COMGETTER(SerialNumber)(serialNumber.asOutParam()); - ComAssertComRCRet(rc, false); - if (!serialNumber.isEmpty()) - USBFilterSetStringExact(&dev, USBFILTERIDX_SERIAL_NUMBER_STR, Utf8Str(serialNumber).c_str(), true); - - Bstr address; - rc = aUSBDevice->COMGETTER(Address)(address.asOutParam()); - ComAssertComRCRet(rc, false); - - USHORT port = 0; - rc = aUSBDevice->COMGETTER(Port)(&port); - ComAssertComRCRet(rc, false); - USBFilterSetNumExact(&dev, USBFILTERIDX_PORT, port, true); - - BOOL remote = FALSE; - rc = aUSBDevice->COMGETTER(Remote)(&remote); - ComAssertComRCRet(rc, false); - ComAssertRet(remote == TRUE, false); - - bool match = false; - - /* apply self filters */ - for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); - it != m->llDeviceFilters->end(); - ++it) - { - AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS); - const USBDeviceFilter::Data &aData = (*it)->getData(); - - if (!aData.mActive) - continue; - if (!aData.mRemote.isMatch(remote)) - continue; - if (!USBFilterMatch(&aData.mUSBFilter, &dev)) - continue; - - match = true; - *aMaskedIfs = aData.mMaskedIfs; - break; - } - - LogFlowThisFunc(("returns: %d\n", match)); - LogFlowThisFuncLeave(); - - return match; + return m->bd->strName; } -/** - * Notifies the proxy service about all filters as requested by the - * @a aInsertFilters argument. - * - * @param aInsertFilters @c true to insert filters, @c false to remove. - * - * @note Locks this object for reading. - */ -HRESULT USBController::notifyProxy(bool aInsertFilters) +USBControllerType_T USBController::getControllerType() const { - LogFlowThisFunc(("aInsertFilters=%RTbool\n", aInsertFilters)); - - AutoCaller autoCaller(this); - AssertComRCReturn(autoCaller.rc(), false); - - AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); - - USBProxyService *service = m->pHost->usbProxyService(); - AssertReturn(service, E_FAIL); - - DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); - while (it != m->llDeviceFilters->end()) - { - USBDeviceFilter *flt = *it; /* resolve ambiguity (for ComPtr below) */ - - /* notify the proxy (only if the filter is active) */ - if ( flt->getData().mActive - && flt->getData().mRemote.isMatch(false) /* and if the filter is NOT remote */ - ) - { - if (aInsertFilters) - { - AssertReturn(flt->getId() == NULL, E_FAIL); - flt->getId() = service->insertFilter(&flt->getData().mUSBFilter); - } - else - { - /* It's possible that the given filter was not inserted the proxy - * when this method gets called (as a result of an early VM - * process crash for example. So, don't assert that ID != NULL. */ - if (flt->getId() != NULL) - { - service->removeFilter(flt->getId()); - flt->getId() = NULL; - } - } - } - ++it; - } - - return S_OK; + return m->bd->enmType; } -Machine* USBController::getMachine() +ComObjPtr<USBController> USBController::getPeer() { - return m->pParent; + return m->pPeer; } -#endif /* VBOX_WITH_USB */ - // private methods ///////////////////////////////////////////////////////////////////////////// /* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/USBDeviceFilterImpl.cpp b/src/VBox/Main/src-server/USBDeviceFilterImpl.cpp index 0c8046e9..47857b4e 100644 --- a/src/VBox/Main/src-server/USBDeviceFilterImpl.cpp +++ b/src/VBox/Main/src-server/USBDeviceFilterImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -16,7 +16,7 @@ */ #include "USBDeviceFilterImpl.h" -#include "USBControllerImpl.h" +#include "USBDeviceFiltersImpl.h" #include "MachineImpl.h" #include "HostImpl.h" @@ -203,7 +203,7 @@ void USBDeviceFilter::FinalRelease() * * @param aParent Handle of the parent object. */ -HRESULT USBDeviceFilter::init(USBController *aParent, +HRESULT USBDeviceFilter::init(USBDeviceFilters *aParent, const settings::USBDeviceFilter &data) { LogFlowThisFunc(("aParent=%p\n", aParent)); @@ -277,7 +277,7 @@ HRESULT USBDeviceFilter::init(USBController *aParent, * * @param aParent Handle of the parent object. */ -HRESULT USBDeviceFilter::init(USBController *aParent, IN_BSTR aName) +HRESULT USBDeviceFilter::init(USBDeviceFilters *aParent, IN_BSTR aName) { LogFlowThisFunc(("aParent=%p\n", aParent)); @@ -326,7 +326,7 @@ HRESULT USBDeviceFilter::init(USBController *aParent, IN_BSTR aName) * @note Locks @a aThat object for writing if @a aReshare is @c true, or for * reading if @a aReshare is false. */ -HRESULT USBDeviceFilter::init (USBController *aParent, USBDeviceFilter *aThat, +HRESULT USBDeviceFilter::init (USBDeviceFilters *aParent, USBDeviceFilter *aThat, bool aReshare /* = false */) { LogFlowThisFunc(("aParent=%p, aThat=%p, aReshare=%RTbool\n", @@ -379,7 +379,7 @@ HRESULT USBDeviceFilter::init (USBController *aParent, USBDeviceFilter *aThat, * * @note Locks @a aThat object for reading. */ -HRESULT USBDeviceFilter::initCopy (USBController *aParent, USBDeviceFilter *aThat) +HRESULT USBDeviceFilter::initCopy (USBDeviceFilters *aParent, USBDeviceFilter *aThat) { LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat)); diff --git a/src/VBox/Main/src-server/USBDeviceFiltersImpl.cpp b/src/VBox/Main/src-server/USBDeviceFiltersImpl.cpp new file mode 100644 index 00000000..09b42f5c --- /dev/null +++ b/src/VBox/Main/src-server/USBDeviceFiltersImpl.cpp @@ -0,0 +1,1089 @@ +/* $Id: USBDeviceFiltersImpl.cpp $ */ +/** @file + * Implementation of IUSBController. + */ + +/* + * Copyright (C) 2005-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include "USBDeviceFiltersImpl.h" + +#include "Global.h" +#include "MachineImpl.h" +#include "VirtualBoxImpl.h" +#include "HostImpl.h" +#ifdef VBOX_WITH_USB +# include "USBDeviceImpl.h" +# include "HostUSBDeviceImpl.h" +# include "USBProxyService.h" +# include "USBDeviceFilterImpl.h" +#endif + +#include <iprt/string.h> +#include <iprt/cpp/utils.h> + +#include <VBox/err.h> +#include <VBox/settings.h> +#include <VBox/com/array.h> + +#include <algorithm> + +#include "AutoStateDep.h" +#include "AutoCaller.h" +#include "Logging.h" + +// defines +///////////////////////////////////////////////////////////////////////////// + +typedef std::list< ComObjPtr<USBDeviceFilter> > DeviceFilterList; + +struct USBDeviceFilters::Data +{ + Data(Machine *pMachine) + : pParent(pMachine), + pHost(pMachine->getVirtualBox()->host()) + { } + + ~Data() + {}; + + Machine * const pParent; + Host * const pHost; + + // peer machine's USB device filters list + const ComObjPtr<USBDeviceFilters> pPeer; + +#ifdef VBOX_WITH_USB + // List of device filters. + Backupable<DeviceFilterList> llDeviceFilters; +#endif +}; + + + +// constructor / destructor +///////////////////////////////////////////////////////////////////////////// + +DEFINE_EMPTY_CTOR_DTOR(USBDeviceFilters) + +HRESULT USBDeviceFilters::FinalConstruct() +{ + return BaseFinalConstruct(); +} + +void USBDeviceFilters::FinalRelease() +{ + uninit(); + BaseFinalRelease(); +} + +// public initializer/uninitializer for internal purposes only +///////////////////////////////////////////////////////////////////////////// + +/** + * Initializes the USB controller object. + * + * @returns COM result indicator. + * @param aParent Pointer to our parent object. + */ +HRESULT USBDeviceFilters::init(Machine *aParent) +{ + LogFlowThisFunc(("aParent=%p\n", aParent)); + + ComAssertRet(aParent, E_INVALIDARG); + + /* Enclose the state transition NotReady->InInit->Ready */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + m = new Data(aParent); + + /* mPeer is left null */ +#ifdef VBOX_WITH_USB + m->llDeviceFilters.allocate(); +#endif + + /* Confirm a successful initialization */ + autoInitSpan.setSucceeded(); + + return S_OK; +} + +/** + * Initializes the USB devic filters object given another USB filters object + * (a kind of copy constructor). This object shares data with + * the object passed as an argument. + * + * @returns COM result indicator. + * @param aParent Pointer to our parent object. + * @param aPeer The object to share. + * + * @note This object must be destroyed before the original object + * it shares data with is destroyed. + */ +HRESULT USBDeviceFilters::init(Machine *aParent, USBDeviceFilters *aPeer) +{ + LogFlowThisFunc(("aParent=%p, aPeer=%p\n", aParent, aPeer)); + + ComAssertRet(aParent && aPeer, E_INVALIDARG); + + /* Enclose the state transition NotReady->InInit->Ready */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + m = new Data(aParent); + + unconst(m->pPeer) = aPeer; + + AutoWriteLock thatlock(aPeer COMMA_LOCKVAL_SRC_POS); + +#ifdef VBOX_WITH_USB + /* create copies of all filters */ + m->llDeviceFilters.allocate(); + DeviceFilterList::const_iterator it = aPeer->m->llDeviceFilters->begin(); + while (it != aPeer->m->llDeviceFilters->end()) + { + ComObjPtr<USBDeviceFilter> pFilter; + pFilter.createObject(); + pFilter->init(this, *it); + m->llDeviceFilters->push_back(pFilter); + ++it; + } +#endif /* VBOX_WITH_USB */ + + /* Confirm a successful initialization */ + autoInitSpan.setSucceeded(); + + return S_OK; +} + + +/** + * Initializes the USB controller object given another guest object + * (a kind of copy constructor). This object makes a private copy of data + * of the original object passed as an argument. + */ +HRESULT USBDeviceFilters::initCopy(Machine *aParent, USBDeviceFilters *aPeer) +{ + LogFlowThisFunc(("aParent=%p, aPeer=%p\n", aParent, aPeer)); + + ComAssertRet(aParent && aPeer, E_INVALIDARG); + + /* Enclose the state transition NotReady->InInit->Ready */ + AutoInitSpan autoInitSpan(this); + AssertReturn(autoInitSpan.isOk(), E_FAIL); + + m = new Data(aParent); + + /* mPeer is left null */ + + AutoWriteLock thatlock(aPeer COMMA_LOCKVAL_SRC_POS); + +#ifdef VBOX_WITH_USB + /* create private copies of all filters */ + m->llDeviceFilters.allocate(); + DeviceFilterList::const_iterator it = aPeer->m->llDeviceFilters->begin(); + while (it != aPeer->m->llDeviceFilters->end()) + { + ComObjPtr<USBDeviceFilter> pFilter; + pFilter.createObject(); + pFilter->initCopy(this, *it); + m->llDeviceFilters->push_back(pFilter); + ++it; + } +#endif /* VBOX_WITH_USB */ + + /* Confirm a successful initialization */ + autoInitSpan.setSucceeded(); + + return S_OK; +} + + +/** + * Uninitializes the instance and sets the ready flag to FALSE. + * Called either from FinalRelease() or by the parent when it gets destroyed. + */ +void USBDeviceFilters::uninit() +{ + LogFlowThisFunc(("\n")); + + /* Enclose the state transition Ready->InUninit->NotReady */ + AutoUninitSpan autoUninitSpan(this); + if (autoUninitSpan.uninitDone()) + return; + +#ifdef VBOX_WITH_USB + // uninit all device filters on the list (it's a standard std::list not an ObjectsList + // so we must uninit() manually) + for (DeviceFilterList::iterator it = m->llDeviceFilters->begin(); + it != m->llDeviceFilters->end(); + ++it) + (*it)->uninit(); + + m->llDeviceFilters.free(); +#endif + + unconst(m->pPeer) = NULL; + unconst(m->pParent) = NULL; + + delete m; + m = NULL; +} + + +// IUSBDeviceFilters properties +///////////////////////////////////////////////////////////////////////////// + +#ifndef VBOX_WITH_USB +/** + * Fake class for build without USB. + * We need an empty collection & enum for deviceFilters, that's all. + */ +class ATL_NO_VTABLE USBDeviceFilter : + public VirtualBoxBase, + VBOX_SCRIPTABLE_IMPL(IUSBDeviceFilter) +{ +public: + DECLARE_NOT_AGGREGATABLE(USBDeviceFilter) + DECLARE_PROTECT_FINAL_CONSTRUCT() + BEGIN_COM_MAP(USBDeviceFilter) + VBOX_DEFAULT_INTERFACE_ENTRIES(IUSBDeviceFilter) + END_COM_MAP() + + DECLARE_EMPTY_CTOR_DTOR(USBDeviceFilter) + + // IUSBDeviceFilter properties + STDMETHOD(COMGETTER(Name))(BSTR *aName); + STDMETHOD(COMSETTER(Name))(IN_BSTR aName); + STDMETHOD(COMGETTER(Active))(BOOL *aActive); + STDMETHOD(COMSETTER(Active))(BOOL aActive); + STDMETHOD(COMGETTER(VendorId))(BSTR *aVendorId); + STDMETHOD(COMSETTER(VendorId))(IN_BSTR aVendorId); + STDMETHOD(COMGETTER(ProductId))(BSTR *aProductId); + STDMETHOD(COMSETTER(ProductId))(IN_BSTR aProductId); + STDMETHOD(COMGETTER(Revision))(BSTR *aRevision); + STDMETHOD(COMSETTER(Revision))(IN_BSTR aRevision); + STDMETHOD(COMGETTER(Manufacturer))(BSTR *aManufacturer); + STDMETHOD(COMSETTER(Manufacturer))(IN_BSTR aManufacturer); + STDMETHOD(COMGETTER(Product))(BSTR *aProduct); + STDMETHOD(COMSETTER(Product))(IN_BSTR aProduct); + STDMETHOD(COMGETTER(SerialNumber))(BSTR *aSerialNumber); + STDMETHOD(COMSETTER(SerialNumber))(IN_BSTR aSerialNumber); + STDMETHOD(COMGETTER(Port))(BSTR *aPort); + STDMETHOD(COMSETTER(Port))(IN_BSTR aPort); + STDMETHOD(COMGETTER(Remote))(BSTR *aRemote); + STDMETHOD(COMSETTER(Remote))(IN_BSTR aRemote); + STDMETHOD(COMGETTER(MaskedInterfaces))(ULONG *aMaskedIfs); + STDMETHOD(COMSETTER(MaskedInterfaces))(ULONG aMaskedIfs); +}; +#endif /* !VBOX_WITH_USB */ + + +STDMETHODIMP USBDeviceFilters::COMGETTER(DeviceFilters)(ComSafeArrayOut(IUSBDeviceFilter *, aDevicesFilters)) +{ +#ifdef VBOX_WITH_USB + CheckComArgOutSafeArrayPointerValid(aDevicesFilters); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + SafeIfaceArray<IUSBDeviceFilter> collection(*m->llDeviceFilters.data()); + collection.detachTo(ComSafeArrayOutArg(aDevicesFilters)); + + return S_OK; +#else + NOREF(aDevicesFilters); +# ifndef RT_OS_WINDOWS + NOREF(aDevicesFiltersSize); +# endif + ReturnComNotImplemented(); +#endif +} + +// IUSBDeviceFilters methods +///////////////////////////////////////////////////////////////////////////// + +STDMETHODIMP USBDeviceFilters::CreateDeviceFilter(IN_BSTR aName, + IUSBDeviceFilter **aFilter) +{ +#ifdef VBOX_WITH_USB + CheckComArgOutPointerValid(aFilter); + + CheckComArgStrNotEmptyOrNull(aName); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + /* the machine needs to be mutable */ + AutoMutableStateDependency adep(m->pParent); + if (FAILED(adep.rc())) return adep.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + ComObjPtr<USBDeviceFilter> pFilter; + pFilter.createObject(); + HRESULT rc = pFilter->init(this, aName); + ComAssertComRCRetRC(rc); + rc = pFilter.queryInterfaceTo(aFilter); + AssertComRCReturnRC(rc); + + return S_OK; +#else + NOREF(aName); + NOREF(aFilter); + ReturnComNotImplemented(); +#endif +} + +STDMETHODIMP USBDeviceFilters::InsertDeviceFilter(ULONG aPosition, + IUSBDeviceFilter *aFilter) +{ +#ifdef VBOX_WITH_USB + + CheckComArgNotNull(aFilter); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + /* the machine needs to be mutable */ + AutoMutableStateDependency adep(m->pParent); + if (FAILED(adep.rc())) return adep.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + ComObjPtr<USBDeviceFilter> pFilter = static_cast<USBDeviceFilter*>(aFilter); + + if (pFilter->mInList) + return setError(VBOX_E_INVALID_OBJECT_STATE, + tr("The given USB device pFilter is already in the list")); + + /* backup the list before modification */ + m->llDeviceFilters.backup(); + + /* iterate to the position... */ + DeviceFilterList::iterator it; + if (aPosition < m->llDeviceFilters->size()) + { + it = m->llDeviceFilters->begin(); + std::advance(it, aPosition); + } + else + it = m->llDeviceFilters->end(); + /* ...and insert */ + m->llDeviceFilters->insert(it, pFilter); + pFilter->mInList = true; + + /* notify the proxy (only when it makes sense) */ + if (pFilter->getData().mActive && Global::IsOnline(adep.machineState()) + && pFilter->getData().mRemote.isMatch(false)) + { + USBProxyService *pProxySvc = m->pHost->usbProxyService(); + ComAssertRet(pProxySvc, E_FAIL); + + ComAssertRet(pFilter->getId() == NULL, E_FAIL); + pFilter->getId() = pProxySvc->insertFilter(&pFilter->getData().mUSBFilter); + } + + alock.release(); + AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS); + m->pParent->setModified(Machine::IsModified_USB); + mlock.release(); + + return S_OK; + +#else /* VBOX_WITH_USB */ + + NOREF(aPosition); + NOREF(aFilter); + ReturnComNotImplemented(); + +#endif /* VBOX_WITH_USB */ +} + +STDMETHODIMP USBDeviceFilters::RemoveDeviceFilter(ULONG aPosition, + IUSBDeviceFilter **aFilter) +{ +#ifdef VBOX_WITH_USB + + CheckComArgOutPointerValid(aFilter); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + /* the machine needs to be mutable */ + AutoMutableStateDependency adep(m->pParent); + if (FAILED(adep.rc())) return adep.rc(); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + + if (!m->llDeviceFilters->size()) + return setError(E_INVALIDARG, + tr("The USB device pFilter list is empty")); + + if (aPosition >= m->llDeviceFilters->size()) + return setError(E_INVALIDARG, + tr("Invalid position: %lu (must be in range [0, %lu])"), + aPosition, m->llDeviceFilters->size() - 1); + + /* backup the list before modification */ + m->llDeviceFilters.backup(); + + ComObjPtr<USBDeviceFilter> pFilter; + { + /* iterate to the position... */ + DeviceFilterList::iterator it = m->llDeviceFilters->begin(); + std::advance(it, aPosition); + /* ...get an element from there... */ + pFilter = *it; + /* ...and remove */ + pFilter->mInList = false; + m->llDeviceFilters->erase(it); + } + + /* cancel sharing (make an independent copy of data) */ + pFilter->unshare(); + + pFilter.queryInterfaceTo(aFilter); + + /* notify the proxy (only when it makes sense) */ + if (pFilter->getData().mActive && Global::IsOnline(adep.machineState()) + && pFilter->getData().mRemote.isMatch(false)) + { + USBProxyService *pProxySvc = m->pHost->usbProxyService(); + ComAssertRet(pProxySvc, E_FAIL); + + ComAssertRet(pFilter->getId() != NULL, E_FAIL); + pProxySvc->removeFilter(pFilter->getId()); + pFilter->getId() = NULL; + } + + alock.release(); + AutoWriteLock mlock(m->pParent COMMA_LOCKVAL_SRC_POS); + m->pParent->setModified(Machine::IsModified_USB); + mlock.release(); + + return S_OK; + +#else /* VBOX_WITH_USB */ + + NOREF(aPosition); + NOREF(aFilter); + ReturnComNotImplemented(); + +#endif /* VBOX_WITH_USB */ +} + +// public methods only for internal purposes +///////////////////////////////////////////////////////////////////////////// + +/** + * Loads settings from the given machine node. + * May be called once right after this object creation. + * + * @param aMachineNode <Machine> node. + * + * @note Does not lock "this" as Machine::loadHardware, which calls this, does not lock either. + */ +HRESULT USBDeviceFilters::loadSettings(const settings::USB &data) +{ + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + /* Note: we assume that the default values for attributes of optional + * nodes are assigned in the Data::Data() constructor and don't do it + * here. It implies that this method may only be called after constructing + * a new USBDeviceFilters object while all its data fields are in the default + * values. Exceptions are fields whose creation time defaults don't match + * values that should be applied when these fields are not explicitly set + * in the settings file (for backwards compatibility reasons). This takes + * place when a setting of a newly created object must default to A while + * the same setting of an object loaded from the old settings file must + * default to B. */ + +#ifdef VBOX_WITH_USB + for (settings::USBDeviceFiltersList::const_iterator it = data.llDeviceFilters.begin(); + it != data.llDeviceFilters.end(); + ++it) + { + const settings::USBDeviceFilter &f = *it; + ComObjPtr<USBDeviceFilter> pFilter; + pFilter.createObject(); + HRESULT rc = pFilter->init(this, // parent + f); + if (FAILED(rc)) return rc; + + m->llDeviceFilters->push_back(pFilter); + pFilter->mInList = true; + } +#endif /* VBOX_WITH_USB */ + + return S_OK; +} + +/** + * Saves settings to the given machine node. + * + * @param aMachineNode <Machine> node. + * + * @note Locks this object for reading. + */ +HRESULT USBDeviceFilters::saveSettings(settings::USB &data) +{ + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + +#ifdef VBOX_WITH_USB + data.llDeviceFilters.clear(); + + for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); + it != m->llDeviceFilters->end(); + ++it) + { + AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS); + const USBDeviceFilter::Data &filterData = (*it)->getData(); + + Bstr str; + + settings::USBDeviceFilter f; + f.strName = filterData.mName; + f.fActive = !!filterData.mActive; + (*it)->COMGETTER(VendorId)(str.asOutParam()); + f.strVendorId = str; + (*it)->COMGETTER(ProductId)(str.asOutParam()); + f.strProductId = str; + (*it)->COMGETTER(Revision)(str.asOutParam()); + f.strRevision = str; + (*it)->COMGETTER(Manufacturer)(str.asOutParam()); + f.strManufacturer = str; + (*it)->COMGETTER(Product)(str.asOutParam()); + f.strProduct = str; + (*it)->COMGETTER(SerialNumber)(str.asOutParam()); + f.strSerialNumber = str; + (*it)->COMGETTER(Port)(str.asOutParam()); + f.strPort = str; + f.strRemote = filterData.mRemote.string(); + f.ulMaskedInterfaces = filterData.mMaskedIfs; + + data.llDeviceFilters.push_back(f); + } +#endif /* VBOX_WITH_USB */ + + return S_OK; +} + +/** @note Locks objects for writing! */ +void USBDeviceFilters::rollback() +{ + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + /* we need the machine state */ + AutoAnyStateDependency adep(m->pParent); + AssertComRCReturnVoid(adep.rc()); + + AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + +#ifdef VBOX_WITH_USB + + if (m->llDeviceFilters.isBackedUp()) + { + USBProxyService *pProxySvc = m->pHost->usbProxyService(); + Assert(pProxySvc); + + /* uninitialize all new filters (absent in the backed up list) */ + DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); + DeviceFilterList *backedList = m->llDeviceFilters.backedUpData(); + while (it != m->llDeviceFilters->end()) + { + if (std::find(backedList->begin(), backedList->end(), *it) == + backedList->end()) + { + /* notify the proxy (only when it makes sense) */ + if ((*it)->getData().mActive && + Global::IsOnline(adep.machineState()) + && (*it)->getData().mRemote.isMatch(false)) + { + USBDeviceFilter *pFilter = *it; + Assert(pFilter->getId() != NULL); + pProxySvc->removeFilter(pFilter->getId()); + pFilter->getId() = NULL; + } + + (*it)->uninit(); + } + ++it; + } + + if (Global::IsOnline(adep.machineState())) + { + /* find all removed old filters (absent in the new list) + * and insert them back to the USB proxy */ + it = backedList->begin(); + while (it != backedList->end()) + { + if (std::find(m->llDeviceFilters->begin(), m->llDeviceFilters->end(), *it) == + m->llDeviceFilters->end()) + { + /* notify the proxy (only when necessary) */ + if ((*it)->getData().mActive + && (*it)->getData().mRemote.isMatch(false)) + { + USBDeviceFilter *pFilter = *it; /* resolve ambiguity */ + Assert(pFilter->getId() == NULL); + pFilter->getId() = pProxySvc->insertFilter(&pFilter->getData().mUSBFilter); + } + } + ++it; + } + } + + /* restore the list */ + m->llDeviceFilters.rollback(); + } + + /* here we don't depend on the machine state any more */ + adep.release(); + + /* rollback any changes to filters after restoring the list */ + DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); + while (it != m->llDeviceFilters->end()) + { + if ((*it)->isModified()) + { + (*it)->rollback(); + /* call this to notify the USB proxy about changes */ + onDeviceFilterChange(*it); + } + ++it; + } + +#endif /* VBOX_WITH_USB */ +} + +/** + * @note Locks this object for writing, together with the peer object (also + * for writing) if there is one. + */ +void USBDeviceFilters::commit() +{ + /* sanity */ + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + /* sanity too */ + AutoCaller peerCaller(m->pPeer); + AssertComRCReturnVoid(peerCaller.rc()); + + /* lock both for writing since we modify both (mPeer is "master" so locked + * first) */ + AutoMultiWriteLock2 alock(m->pPeer, this COMMA_LOCKVAL_SRC_POS); + +#ifdef VBOX_WITH_USB + bool commitFilters = false; + + if (m->llDeviceFilters.isBackedUp()) + { + m->llDeviceFilters.commit(); + + /* apply changes to peer */ + if (m->pPeer) + { + AutoWriteLock peerlock(m->pPeer COMMA_LOCKVAL_SRC_POS); + + /* commit all changes to new filters (this will reshare data with + * peers for those who have peers) */ + DeviceFilterList *newList = new DeviceFilterList(); + DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); + while (it != m->llDeviceFilters->end()) + { + (*it)->commit(); + + /* look if this filter has a peer filter */ + ComObjPtr<USBDeviceFilter> peer = (*it)->peer(); + if (!peer) + { + /* no peer means the filter is a newly created one; + * create a peer owning data this filter share it with */ + peer.createObject(); + peer->init(m->pPeer, *it, true /* aReshare */); + } + else + { + /* remove peer from the old list */ + m->pPeer->m->llDeviceFilters->remove(peer); + } + /* and add it to the new list */ + newList->push_back(peer); + + ++it; + } + + /* uninit old peer's filters that are left */ + it = m->pPeer->m->llDeviceFilters->begin(); + while (it != m->pPeer->m->llDeviceFilters->end()) + { + (*it)->uninit(); + ++it; + } + + /* attach new list of filters to our peer */ + m->pPeer->m->llDeviceFilters.attach(newList); + } + else + { + /* we have no peer (our parent is the newly created machine); + * just commit changes to filters */ + commitFilters = true; + } + } + else + { + /* the list of filters itself is not changed, + * just commit changes to filters themselves */ + commitFilters = true; + } + + if (commitFilters) + { + DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); + while (it != m->llDeviceFilters->end()) + { + (*it)->commit(); + ++it; + } + } +#endif /* VBOX_WITH_USB */ +} + +/** + * @note Locks this object for writing, together with the peer object + * represented by @a aThat (locked for reading). + */ +void USBDeviceFilters::copyFrom(USBDeviceFilters *aThat) +{ + AssertReturnVoid(aThat != NULL); + + /* sanity */ + AutoCaller autoCaller(this); + AssertComRCReturnVoid(autoCaller.rc()); + + /* sanity too */ + AutoCaller thatCaller(aThat); + AssertComRCReturnVoid(thatCaller.rc()); + + /* even more sanity */ + AutoAnyStateDependency adep(m->pParent); + AssertComRCReturnVoid(adep.rc()); + /* Machine::copyFrom() may not be called when the VM is running */ + AssertReturnVoid(!Global::IsOnline(adep.machineState())); + + /* peer is not modified, lock it for reading (aThat is "master" so locked + * first) */ + AutoReadLock rl(aThat COMMA_LOCKVAL_SRC_POS); + AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS); + +#ifdef VBOX_WITH_USB + + /* Note that we won't inform the USB proxy about new filters since the VM is + * not running when we are here and therefore no need to do so */ + + /* create private copies of all filters */ + m->llDeviceFilters.backup(); + m->llDeviceFilters->clear(); + for (DeviceFilterList::const_iterator it = aThat->m->llDeviceFilters->begin(); + it != aThat->m->llDeviceFilters->end(); + ++it) + { + ComObjPtr<USBDeviceFilter> pFilter; + pFilter.createObject(); + pFilter->initCopy(this, *it); + m->llDeviceFilters->push_back(pFilter); + } + +#endif /* VBOX_WITH_USB */ +} + +#ifdef VBOX_WITH_USB + +/** + * Called by setter methods of all USB device filters. + * + * @note Locks nothing. + */ +HRESULT USBDeviceFilters::onDeviceFilterChange(USBDeviceFilter *aFilter, + BOOL aActiveChanged /* = FALSE */) +{ + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + /* we need the machine state */ + AutoAnyStateDependency adep(m->pParent); + AssertComRCReturnRC(adep.rc()); + + /* nothing to do if the machine isn't running */ + if (!Global::IsOnline(adep.machineState())) + return S_OK; + + /* we don't modify our data fields -- no need to lock */ + + if ( aFilter->mInList + && m->pParent->isRegistered()) + { + USBProxyService *pProxySvc = m->pHost->usbProxyService(); + ComAssertRet(pProxySvc, E_FAIL); + + if (aActiveChanged) + { + if (aFilter->getData().mRemote.isMatch(false)) + { + /* insert/remove the filter from the proxy */ + if (aFilter->getData().mActive) + { + ComAssertRet(aFilter->getId() == NULL, E_FAIL); + aFilter->getId() = pProxySvc->insertFilter(&aFilter->getData().mUSBFilter); + } + else + { + ComAssertRet(aFilter->getId() != NULL, E_FAIL); + pProxySvc->removeFilter(aFilter->getId()); + aFilter->getId() = NULL; + } + } + } + else + { + if (aFilter->getData().mActive) + { + /* update the filter in the proxy */ + ComAssertRet(aFilter->getId() != NULL, E_FAIL); + pProxySvc->removeFilter(aFilter->getId()); + if (aFilter->getData().mRemote.isMatch(false)) + { + aFilter->getId() = pProxySvc->insertFilter(&aFilter->getData().mUSBFilter); + } + } + } + } + + return S_OK; +} + +/** + * Returns true if the given USB device matches to at least one of + * this controller's USB device filters. + * + * A HostUSBDevice specific version. + * + * @note Locks this object for reading. + */ +bool USBDeviceFilters::hasMatchingFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs) +{ + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), false); + + /* It is not possible to work with USB device if there is no USB controller present. */ + if (!m->pParent->isUSBControllerPresent()) + return false; + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + /* apply self filters */ + for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); + it != m->llDeviceFilters->end(); + ++it) + { + AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS); + if (aDevice->isMatch((*it)->getData())) + { + *aMaskedIfs = (*it)->getData().mMaskedIfs; + return true; + } + } + + return false; +} + +/** + * Returns true if the given USB device matches to at least one of + * this controller's USB device filters. + * + * A generic version that accepts any IUSBDevice on input. + * + * @note + * This method MUST correlate with HostUSBDevice::isMatch() + * in the sense of the device matching logic. + * + * @note Locks this object for reading. + */ +bool USBDeviceFilters::hasMatchingFilter(IUSBDevice *aUSBDevice, ULONG *aMaskedIfs) +{ + LogFlowThisFuncEnter(); + + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), false); + + /* It is not possible to work with USB device if there is no USB controller present. */ + if (!m->pParent->isUSBControllerPresent()) + return false; + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + HRESULT rc = S_OK; + + /* query fields */ + USBFILTER dev; + USBFilterInit(&dev, USBFILTERTYPE_CAPTURE); + + USHORT vendorId = 0; + rc = aUSBDevice->COMGETTER(VendorId)(&vendorId); + ComAssertComRCRet(rc, false); + ComAssertRet(vendorId, false); + int vrc = USBFilterSetNumExact(&dev, USBFILTERIDX_VENDOR_ID, vendorId, true); AssertRC(vrc); + + USHORT productId = 0; + rc = aUSBDevice->COMGETTER(ProductId)(&productId); + ComAssertComRCRet(rc, false); + vrc = USBFilterSetNumExact(&dev, USBFILTERIDX_PRODUCT_ID, productId, true); AssertRC(vrc); + + USHORT revision; + rc = aUSBDevice->COMGETTER(Revision)(&revision); + ComAssertComRCRet(rc, false); + vrc = USBFilterSetNumExact(&dev, USBFILTERIDX_DEVICE, revision, true); AssertRC(vrc); + + Bstr manufacturer; + rc = aUSBDevice->COMGETTER(Manufacturer)(manufacturer.asOutParam()); + ComAssertComRCRet(rc, false); + if (!manufacturer.isEmpty()) + USBFilterSetStringExact(&dev, USBFILTERIDX_MANUFACTURER_STR, Utf8Str(manufacturer).c_str(), true); + + Bstr product; + rc = aUSBDevice->COMGETTER(Product)(product.asOutParam()); + ComAssertComRCRet(rc, false); + if (!product.isEmpty()) + USBFilterSetStringExact(&dev, USBFILTERIDX_PRODUCT_STR, Utf8Str(product).c_str(), true); + + Bstr serialNumber; + rc = aUSBDevice->COMGETTER(SerialNumber)(serialNumber.asOutParam()); + ComAssertComRCRet(rc, false); + if (!serialNumber.isEmpty()) + USBFilterSetStringExact(&dev, USBFILTERIDX_SERIAL_NUMBER_STR, Utf8Str(serialNumber).c_str(), true); + + Bstr address; + rc = aUSBDevice->COMGETTER(Address)(address.asOutParam()); + ComAssertComRCRet(rc, false); + + USHORT port = 0; + rc = aUSBDevice->COMGETTER(Port)(&port); + ComAssertComRCRet(rc, false); + USBFilterSetNumExact(&dev, USBFILTERIDX_PORT, port, true); + + BOOL remote = FALSE; + rc = aUSBDevice->COMGETTER(Remote)(&remote); + ComAssertComRCRet(rc, false); + ComAssertRet(remote == TRUE, false); + + bool match = false; + + /* apply self filters */ + for (DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); + it != m->llDeviceFilters->end(); + ++it) + { + AutoWriteLock filterLock(*it COMMA_LOCKVAL_SRC_POS); + const USBDeviceFilter::Data &aData = (*it)->getData(); + + if (!aData.mActive) + continue; + if (!aData.mRemote.isMatch(remote)) + continue; + if (!USBFilterMatch(&aData.mUSBFilter, &dev)) + continue; + + match = true; + *aMaskedIfs = aData.mMaskedIfs; + break; + } + + LogFlowThisFunc(("returns: %d\n", match)); + LogFlowThisFuncLeave(); + + return match; +} + +/** + * Notifies the proxy pProxySvc about all filters as requested by the + * @a aInsertFilters argument. + * + * @param aInsertFilters @c true to insert filters, @c false to remove. + * + * @note Locks this object for reading. + */ +HRESULT USBDeviceFilters::notifyProxy(bool aInsertFilters) +{ + LogFlowThisFunc(("aInsertFilters=%RTbool\n", aInsertFilters)); + + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), false); + + AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); + + USBProxyService *pProxySvc = m->pHost->usbProxyService(); + AssertReturn(pProxySvc, E_FAIL); + + DeviceFilterList::const_iterator it = m->llDeviceFilters->begin(); + while (it != m->llDeviceFilters->end()) + { + USBDeviceFilter *pFilter = *it; /* resolve ambiguity (for ComPtr below) */ + + /* notify the proxy (only if the filter is active) */ + if ( pFilter->getData().mActive + && pFilter->getData().mRemote.isMatch(false) /* and if the filter is NOT remote */ + ) + { + if (aInsertFilters) + { + AssertReturn(pFilter->getId() == NULL, E_FAIL); + pFilter->getId() = pProxySvc->insertFilter(&pFilter->getData().mUSBFilter); + } + else + { + /* It's possible that the given filter was not inserted the proxy + * when this method gets called (as a result of an early VM + * process crash for example. So, don't assert that ID != NULL. */ + if (pFilter->getId() != NULL) + { + pProxySvc->removeFilter(pFilter->getId()); + pFilter->getId() = NULL; + } + } + } + ++it; + } + + return S_OK; +} + +Machine* USBDeviceFilters::getMachine() +{ + return m->pParent; +} + +#endif /* VBOX_WITH_USB */ + +// private methods +///////////////////////////////////////////////////////////////////////////// +/* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/VFSExplorerImpl.cpp b/src/VBox/Main/src-server/VFSExplorerImpl.cpp index e94b7225..9f50b667 100644 --- a/src/VBox/Main/src-server/VFSExplorerImpl.cpp +++ b/src/VBox/Main/src-server/VFSExplorerImpl.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2009 Oracle Corporation + * Copyright (C) 2009-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -622,7 +622,7 @@ STDMETHODIMP VFSExplorer::CdUp(IProgress **aProgress) return Cd(Bstr(strUpPath).raw(), aProgress); } -STDMETHODIMP VFSExplorer::EntryList(ComSafeArrayOut(BSTR, aNames), ComSafeArrayOut(VFSFileType_T, aTypes), ComSafeArrayOut(ULONG, aSizes), ComSafeArrayOut(ULONG, aModes)) +STDMETHODIMP VFSExplorer::EntryList(ComSafeArrayOut(BSTR, aNames), ComSafeArrayOut(VFSFileType_T, aTypes), ComSafeArrayOut(LONG64, aSizes), ComSafeArrayOut(ULONG, aModes)) { if (ComSafeArrayOutIsNull(aNames) || ComSafeArrayOutIsNull(aTypes)) @@ -635,7 +635,7 @@ STDMETHODIMP VFSExplorer::EntryList(ComSafeArrayOut(BSTR, aNames), ComSafeArrayO com::SafeArray<BSTR> sfaNames(m->entryList.size()); com::SafeArray<ULONG> sfaTypes(m->entryList.size()); - com::SafeArray<ULONG> sfaSizes(m->entryList.size()); + com::SafeArray<LONG64> sfaSizes(m->entryList.size()); com::SafeArray<ULONG> sfaModes(m->entryList.size()); std::list<VFSExplorer::Data::DirEntry>::const_iterator it; diff --git a/src/VBox/Main/src-server/VRDEServerImpl.cpp b/src/VBox/Main/src-server/VRDEServerImpl.cpp index 5862545a..a717affe 100644 --- a/src/VBox/Main/src-server/VRDEServerImpl.cpp +++ b/src/VBox/Main/src-server/VRDEServerImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -29,6 +29,7 @@ #include <VBox/err.h> #include <VBox/sup.h> +#include <VBox/com/array.h> #include <VBox/RemoteDesktop/VRDE.h> @@ -72,7 +73,7 @@ void VRDEServer::FinalRelease() * * @param aParent Handle of the parent object. */ -HRESULT VRDEServer::init (Machine *aParent) +HRESULT VRDEServer::init(Machine *aParent) { LogFlowThisFunc(("aParent=%p\n", aParent)); @@ -111,7 +112,7 @@ HRESULT VRDEServer::init (Machine *aParent) * * @note Locks @a aThat object for reading. */ -HRESULT VRDEServer::init (Machine *aParent, VRDEServer *aThat) +HRESULT VRDEServer::init(Machine *aParent, VRDEServer *aThat) { LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat)); @@ -124,11 +125,11 @@ HRESULT VRDEServer::init (Machine *aParent, VRDEServer *aThat) unconst(mParent) = aParent; unconst(mPeer) = aThat; - AutoCaller thatCaller (aThat); + AutoCaller thatCaller(aThat); AssertComRCReturnRC(thatCaller.rc()); AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS); - mData.share (aThat->mData); + mData.share(aThat->mData); /* Confirm a successful initialization */ autoInitSpan.setSucceeded(); @@ -143,7 +144,7 @@ HRESULT VRDEServer::init (Machine *aParent, VRDEServer *aThat) * * @note Locks @a aThat object for reading. */ -HRESULT VRDEServer::initCopy (Machine *aParent, VRDEServer *aThat) +HRESULT VRDEServer::initCopy(Machine *aParent, VRDEServer *aThat) { LogFlowThisFunc(("aParent=%p, aThat=%p\n", aParent, aThat)); @@ -156,11 +157,11 @@ HRESULT VRDEServer::initCopy (Machine *aParent, VRDEServer *aThat) unconst(mParent) = aParent; /* mPeer is left null */ - AutoCaller thatCaller (aThat); + AutoCaller thatCaller(aThat); AssertComRCReturnRC(thatCaller.rc()); AutoReadLock thatLock(aThat COMMA_LOCKVAL_SRC_POS); - mData.attachCopy (aThat->mData); + mData.attachCopy(aThat->mData); /* Confirm a successful initialization */ autoInitSpan.setSucceeded(); @@ -245,7 +246,7 @@ HRESULT VRDEServer::saveSettings(settings::VRDESettings &data) // IVRDEServer properties ///////////////////////////////////////////////////////////////////////////// -STDMETHODIMP VRDEServer::COMGETTER(Enabled) (BOOL *aEnabled) +STDMETHODIMP VRDEServer::COMGETTER(Enabled)(BOOL *aEnabled) { CheckComArgOutPointerValid(aEnabled); @@ -257,17 +258,19 @@ STDMETHODIMP VRDEServer::COMGETTER(Enabled) (BOOL *aEnabled) return S_OK; } -STDMETHODIMP VRDEServer::COMSETTER(Enabled) (BOOL aEnabled) +STDMETHODIMP VRDEServer::COMSETTER(Enabled)(BOOL aEnabled) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); /* the machine can also be in saved state for this property to change */ - AutoMutableOrSavedStateDependency adep (mParent); + AutoMutableOrSavedStateDependency adep(mParent); if (FAILED(adep.rc())) return adep.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); + HRESULT rc = S_OK; + if (mData->mEnabled != aEnabled) { mData.backup(); @@ -283,10 +286,10 @@ STDMETHODIMP VRDEServer::COMSETTER(Enabled) (BOOL aEnabled) /* Avoid deadlock when onVRDEServerChange eventually calls SetExtraData. */ adep.release(); - mParent->onVRDEServerChange(/* aRestart */ TRUE); + rc = mParent->onVRDEServerChange(/* aRestart */ TRUE); } - return S_OK; + return rc; } static int portParseNumber(uint16_t *pu16Port, const char *pszStart, const char *pszEnd) @@ -373,15 +376,15 @@ static int vrdpServerVerifyPortsString(Bstr ports) return VINF_SUCCESS; } -STDMETHODIMP VRDEServer::SetVRDEProperty (IN_BSTR aKey, IN_BSTR aValue) +STDMETHODIMP VRDEServer::SetVRDEProperty(IN_BSTR aKey, IN_BSTR aValue) { LogFlowThisFunc(("\n")); AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* The machine needs to be mutable. */ - AutoMutableStateDependency adep(mParent); + /* the machine can also be in saved state for this property to change */ + AutoMutableOrSavedStateDependency adep(mParent); if (FAILED(adep.rc())) return adep.rc(); Bstr key = aKey; @@ -460,7 +463,7 @@ STDMETHODIMP VRDEServer::SetVRDEProperty (IN_BSTR aKey, IN_BSTR aValue) return S_OK; } -STDMETHODIMP VRDEServer::GetVRDEProperty (IN_BSTR aKey, BSTR *aValue) +STDMETHODIMP VRDEServer::GetVRDEProperty(IN_BSTR aKey, BSTR *aValue) { CheckComArgOutPointerValid(aValue); @@ -627,7 +630,7 @@ STDMETHODIMP VRDEServer::COMGETTER(VRDEProperties)(ComSafeArrayOut(BSTR, aProper return S_OK; } -STDMETHODIMP VRDEServer::COMGETTER(AuthType) (AuthType_T *aType) +STDMETHODIMP VRDEServer::COMGETTER(AuthType)(AuthType_T *aType) { CheckComArgOutPointerValid(aType); @@ -641,13 +644,13 @@ STDMETHODIMP VRDEServer::COMGETTER(AuthType) (AuthType_T *aType) return S_OK; } -STDMETHODIMP VRDEServer::COMSETTER(AuthType) (AuthType_T aType) +STDMETHODIMP VRDEServer::COMSETTER(AuthType)(AuthType_T aType) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(mParent); + /* the machine can also be in saved state for this property to change */ + AutoMutableOrSavedStateDependency adep(mParent); if (FAILED(adep.rc())) return adep.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); @@ -670,7 +673,7 @@ STDMETHODIMP VRDEServer::COMSETTER(AuthType) (AuthType_T aType) return S_OK; } -STDMETHODIMP VRDEServer::COMGETTER(AuthTimeout) (ULONG *aTimeout) +STDMETHODIMP VRDEServer::COMGETTER(AuthTimeout)(ULONG *aTimeout) { CheckComArgOutPointerValid(aTimeout); @@ -684,13 +687,13 @@ STDMETHODIMP VRDEServer::COMGETTER(AuthTimeout) (ULONG *aTimeout) return S_OK; } -STDMETHODIMP VRDEServer::COMSETTER(AuthTimeout) (ULONG aTimeout) +STDMETHODIMP VRDEServer::COMSETTER(AuthTimeout)(ULONG aTimeout) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(mParent); + /* the machine can also be in saved state for this property to change */ + AutoMutableOrSavedStateDependency adep(mParent); if (FAILED(adep.rc())) return adep.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); @@ -717,7 +720,7 @@ STDMETHODIMP VRDEServer::COMSETTER(AuthTimeout) (ULONG aTimeout) return S_OK; } -STDMETHODIMP VRDEServer::COMGETTER(AuthLibrary) (BSTR *aLibrary) +STDMETHODIMP VRDEServer::COMGETTER(AuthLibrary)(BSTR *aLibrary) { CheckComArgOutPointerValid(aLibrary); @@ -748,13 +751,13 @@ STDMETHODIMP VRDEServer::COMGETTER(AuthLibrary) (BSTR *aLibrary) return S_OK; } -STDMETHODIMP VRDEServer::COMSETTER(AuthLibrary) (IN_BSTR aLibrary) +STDMETHODIMP VRDEServer::COMSETTER(AuthLibrary)(IN_BSTR aLibrary) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(mParent); + /* the machine can also be in saved state for this property to change */ + AutoMutableOrSavedStateDependency adep(mParent); if (FAILED(adep.rc())) return adep.rc(); Bstr bstrLibrary(aLibrary); @@ -779,8 +782,7 @@ STDMETHODIMP VRDEServer::COMSETTER(AuthLibrary) (IN_BSTR aLibrary) return S_OK; } -STDMETHODIMP VRDEServer::COMGETTER(AllowMultiConnection) ( - BOOL *aAllowMultiConnection) +STDMETHODIMP VRDEServer::COMGETTER(AllowMultiConnection)(BOOL *aAllowMultiConnection) { CheckComArgOutPointerValid(aAllowMultiConnection); @@ -794,14 +796,13 @@ STDMETHODIMP VRDEServer::COMGETTER(AllowMultiConnection) ( return S_OK; } -STDMETHODIMP VRDEServer::COMSETTER(AllowMultiConnection) ( - BOOL aAllowMultiConnection) +STDMETHODIMP VRDEServer::COMSETTER(AllowMultiConnection)(BOOL aAllowMultiConnection) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(mParent); + /* the machine can also be in saved state for this property to change */ + AutoMutableOrSavedStateDependency adep(mParent); if (FAILED(adep.rc())) return adep.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); @@ -824,8 +825,7 @@ STDMETHODIMP VRDEServer::COMSETTER(AllowMultiConnection) ( return S_OK; } -STDMETHODIMP VRDEServer::COMGETTER(ReuseSingleConnection) ( - BOOL *aReuseSingleConnection) +STDMETHODIMP VRDEServer::COMGETTER(ReuseSingleConnection)(BOOL *aReuseSingleConnection) { CheckComArgOutPointerValid(aReuseSingleConnection); @@ -839,14 +839,13 @@ STDMETHODIMP VRDEServer::COMGETTER(ReuseSingleConnection) ( return S_OK; } -STDMETHODIMP VRDEServer::COMSETTER(ReuseSingleConnection) ( - BOOL aReuseSingleConnection) +STDMETHODIMP VRDEServer::COMSETTER(ReuseSingleConnection)(BOOL aReuseSingleConnection) { AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(mParent); + /* the machine can also be in saved state for this property to change */ + AutoMutableOrSavedStateDependency adep(mParent); if (FAILED(adep.rc())) return adep.rc(); AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); @@ -869,7 +868,7 @@ STDMETHODIMP VRDEServer::COMSETTER(ReuseSingleConnection) ( return S_OK; } -STDMETHODIMP VRDEServer::COMGETTER(VRDEExtPack) (BSTR *aExtPack) +STDMETHODIMP VRDEServer::COMGETTER(VRDEExtPack)(BSTR *aExtPack) { CheckComArgOutPointerValid(aExtPack); @@ -919,8 +918,8 @@ STDMETHODIMP VRDEServer::COMSETTER(VRDEExtPack)(IN_BSTR aExtPack) HRESULT hrc = autoCaller.rc(); if (SUCCEEDED(hrc)) { - /* the machine needs to be mutable */ - AutoMutableStateDependency adep(mParent); + /* the machine can also be in saved state for this property to change */ + AutoMutableOrSavedStateDependency adep(mParent); hrc = adep.rc(); if (SUCCEEDED(hrc)) { @@ -994,11 +993,11 @@ void VRDEServer::commit() { /* sanity */ AutoCaller autoCaller(this); - AssertComRCReturnVoid (autoCaller.rc()); + AssertComRCReturnVoid(autoCaller.rc()); /* sanity too */ - AutoCaller peerCaller (mPeer); - AssertComRCReturnVoid (peerCaller.rc()); + AutoCaller peerCaller(mPeer); + AssertComRCReturnVoid(peerCaller.rc()); /* lock both for writing since we modify both (mPeer is "master" so locked * first) */ @@ -1010,7 +1009,7 @@ void VRDEServer::commit() if (mPeer) { /* attach new data to the peer and reshare it */ - mPeer->mData.attach (mData); + mPeer->mData.attach(mData); } } } @@ -1019,17 +1018,17 @@ void VRDEServer::commit() * @note Locks this object for writing, together with the peer object * represented by @a aThat (locked for reading). */ -void VRDEServer::copyFrom (VRDEServer *aThat) +void VRDEServer::copyFrom(VRDEServer *aThat) { - AssertReturnVoid (aThat != NULL); + AssertReturnVoid(aThat != NULL); /* sanity */ AutoCaller autoCaller(this); - AssertComRCReturnVoid (autoCaller.rc()); + AssertComRCReturnVoid(autoCaller.rc()); /* sanity too */ - AutoCaller thatCaller (aThat); - AssertComRCReturnVoid (thatCaller.rc()); + AutoCaller thatCaller(aThat); + AssertComRCReturnVoid(thatCaller.rc()); /* peer is not modified, lock it for reading (aThat is "master" so locked * first) */ @@ -1037,6 +1036,6 @@ void VRDEServer::copyFrom (VRDEServer *aThat) AutoWriteLock wl(this COMMA_LOCKVAL_SRC_POS); /* this will back up current data */ - mData.assignCopy (aThat->mData); + mData.assignCopy(aThat->mData); } /* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/VirtualBoxImpl.cpp b/src/VBox/Main/src-server/VirtualBoxImpl.cpp index 64135154..7c319236 100644 --- a/src/VBox/Main/src-server/VirtualBoxImpl.cpp +++ b/src/VBox/Main/src-server/VirtualBoxImpl.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2012 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -35,6 +35,7 @@ #include <VBox/com/com.h> #include <VBox/com/array.h> #include "VBox/com/EventQueue.h" +#include "VBox/com/MultiResult.h" #include <VBox/err.h> #include <VBox/param.h> @@ -60,8 +61,9 @@ #include "USBControllerImpl.h" #include "SystemPropertiesImpl.h" #include "GuestOSTypeImpl.h" -#include "DHCPServerRunner.h" +#include "NetworkServiceRunner.h" #include "DHCPServerImpl.h" +#include "NATNetworkImpl.h" #ifdef VBOX_WITH_RESOURCE_USAGE_API # include "PerformanceImpl.h" #endif /* VBOX_WITH_RESOURCE_USAGE_API */ @@ -71,10 +73,10 @@ # include "ExtPackManagerImpl.h" #endif #include "AutostartDb.h" +#include "ClientWatcher.h" #include "AutoCaller.h" #include "Logging.h" -#include "objectslist.h" #ifdef RT_OS_WINDOWS # include "win/svchlp.h" @@ -110,12 +112,11 @@ Bstr VirtualBox::sPackageType; // static Bstr VirtualBox::sAPIVersion; -#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER -/** Table for adaptive timeouts in the client watcher. The counter starts at - * the maximum value and decreases to 0. */ -static const RTMSINTERVAL s_updateAdaptTimeouts[] = { 500, 200, 100, 50, 20, 10, 5 }; -#endif +// static +std::map<Bstr, int> VirtualBox::sNatNetworkNameToRefCount; +// static leaked (todo: find better place to free it.) +RWLockHandle *VirtualBox::spMtxNatNetworkNameToRefCountLock; //////////////////////////////////////////////////////////////////////////////// // // CallbackEvent class @@ -163,24 +164,11 @@ protected: // //////////////////////////////////////////////////////////////////////////////// -#if defined(RT_OS_WINDOWS) - #define UPDATEREQARG NULL - #define UPDATEREQTYPE HANDLE -#elif defined(RT_OS_OS2) - #define UPDATEREQARG NIL_RTSEMEVENT - #define UPDATEREQTYPE RTSEMEVENT -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) - #define UPDATEREQARG - #define UPDATEREQTYPE RTSEMEVENT -#else -# error "Port me!" -#endif - -typedef ObjectsList<Machine> MachinesOList; typedef ObjectsList<Medium> MediaOList; typedef ObjectsList<GuestOSType> GuestOSTypesOList; typedef ObjectsList<SharedFolder> SharedFoldersOList; typedef ObjectsList<DHCPServer> DHCPServersOList; +typedef ObjectsList<NATNetwork> NATNetworksOList; typedef std::map<Guid, ComPtr<IProgress> > ProgressMap; typedef std::map<Guid, ComObjPtr<Medium> > HardDiskMap; @@ -208,9 +196,10 @@ struct VirtualBox::Data allSharedFolders(lockSharedFolders), lockDHCPServers(LOCKCLASS_LISTOFOTHEROBJECTS), allDHCPServers(lockDHCPServers), + lockNATNetworks(LOCKCLASS_LISTOFOTHEROBJECTS), + allNATNetworks(lockNATNetworks), mtxProgressOperations(LOCKCLASS_PROGRESSLIST), - updateReq(UPDATEREQARG), - threadClientWatcher(NIL_RTTHREAD), + pClientWatcher(NULL), threadAsyncEvent(NIL_RTTHREAD), pAsyncEventQ(NULL), pAutostartDb(NULL), @@ -286,17 +275,13 @@ struct VirtualBox::Data RWLockHandle lockDHCPServers; DHCPServersOList allDHCPServers; + RWLockHandle lockNATNetworks; + NATNetworksOList allNATNetworks; + RWLockHandle mtxProgressOperations; ProgressMap mapProgressOperations; - // the following are data for the client watcher thread - const UPDATEREQTYPE updateReq; -#ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER - uint8_t updateAdaptCtr; -#endif - const RTTHREAD threadClientWatcher; - typedef std::list<RTPROCESS> ProcessList; - ProcessList llProcesses; + ClientWatcher * const pClientWatcher; // the following are data for the async event thread const RTTHREAD threadAsyncEvent; @@ -385,6 +370,9 @@ HRESULT VirtualBox::init() sPackageType = VBOX_PACKAGE_STRING; if (sAPIVersion.isEmpty()) sAPIVersion = VBOX_API_VERSION_STRING; + if (!spMtxNatNetworkNameToRefCountLock) + spMtxNatNetworkNameToRefCountLock = new RWLockHandle(LOCKCLASS_VIRTUALBOXOBJECT); + LogFlowThisFunc(("Version: %ls, Package: %ls, API Version: %ls\n", sVersion.raw(), sPackageType.raw(), sAPIVersion.raw())); /* Get the VirtualBox home directory. */ @@ -485,7 +473,7 @@ HRESULT VirtualBox::init() dumpAllBackRefs(); #endif - /* net services */ + /* net services - dhcp services */ for (settings::DHCPServersList::const_iterator it = m->pMainConfigFile->llDhcpServers.begin(); it != m->pMainConfigFile->llDhcpServers.end(); ++it) @@ -501,9 +489,27 @@ HRESULT VirtualBox::init() if (FAILED(rc)) throw rc; } + /* net services - nat networks */ + for (settings::NATNetworksList::const_iterator it = m->pMainConfigFile->llNATNetworks.begin(); + it != m->pMainConfigFile->llNATNetworks.end(); + ++it) + { + const settings::NATNetwork &net = *it; + + ComObjPtr<NATNetwork> pNATNetwork; + if (SUCCEEDED(rc = pNATNetwork.createObject())) + { + rc = pNATNetwork->init(this, net); + AssertComRCReturnRC(rc); + } + + rc = registerNATNetwork(pNATNetwork, false /* aSaveRegistry */); + AssertComRCReturnRC(rc); + } + /* events */ if (SUCCEEDED(rc = unconst(m->pEventSource).createObject())) - rc = m->pEventSource->init(static_cast<IVirtualBox*>(this)); + rc = m->pEventSource->init(); if (FAILED(rc)) throw rc; #ifdef VBOX_WITH_EXTPACK @@ -527,27 +533,21 @@ HRESULT VirtualBox::init() if (SUCCEEDED(rc)) { - /* start the client watcher thread */ -#if defined(RT_OS_WINDOWS) - unconst(m->updateReq) = ::CreateEvent(NULL, FALSE, FALSE, NULL); -#elif defined(RT_OS_OS2) - RTSemEventCreate(&unconst(m->updateReq)); -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) - RTSemEventCreate(&unconst(m->updateReq)); - ASMAtomicUoWriteU8(&m->updateAdaptCtr, 0); -#else -# error "Port me!" -#endif - int vrc = RTThreadCreate(&unconst(m->threadClientWatcher), - ClientWatcher, - (void *)this, - 0, - RTTHREADTYPE_MAIN_WORKER, - RTTHREADFLAGS_WAITABLE, - "Watcher"); - ComAssertRC(vrc); - if (RT_FAILURE(vrc)) - rc = E_FAIL; + /* set up client monitoring */ + try + { + unconst(m->pClientWatcher) = new ClientWatcher(this); + if (!m->pClientWatcher->isReady()) + { + delete m->pClientWatcher; + unconst(m->pClientWatcher) = NULL; + rc = E_FAIL; + } + } + catch (std::bad_alloc &) + { + rc = E_OUTOFMEMORY; + } } if (SUCCEEDED(rc)) @@ -807,41 +807,20 @@ void VirtualBox::uninit() LogFlowThisFunc(("Releasing event source...\n")); if (m->pEventSource) { - // we don't perform uninit() as it's possible that some pending event refers to this source + // Must uninit the event source here, because it makes no sense that + // it survives longer than the base object. If someone gets an event + // with such an event source then that's life and it has to be dealt + // with appropriately on the API client side. + m->pEventSource->uninit(); unconst(m->pEventSource).setNull(); } LogFlowThisFunc(("Terminating the client watcher...\n")); - if (m->threadClientWatcher != NIL_RTTHREAD) - { - /* signal the client watcher thread */ - updateClientWatcher(); - /* wait for the termination */ - RTThreadWait(m->threadClientWatcher, RT_INDEFINITE_WAIT, NULL); - unconst(m->threadClientWatcher) = NIL_RTTHREAD; - } - m->llProcesses.clear(); -#if defined(RT_OS_WINDOWS) - if (m->updateReq != NULL) - { - ::CloseHandle(m->updateReq); - unconst(m->updateReq) = NULL; - } -#elif defined(RT_OS_OS2) - if (m->updateReq != NIL_RTSEMEVENT) + if (m->pClientWatcher) { - RTSemEventDestroy(m->updateReq); - unconst(m->updateReq) = NIL_RTSEMEVENT; + delete m->pClientWatcher; + unconst(m->pClientWatcher) = NULL; } -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) - if (m->updateReq != NIL_RTSEMEVENT) - { - RTSemEventDestroy(m->updateReq); - unconst(m->updateReq) = NIL_RTSEMEVENT; - } -#else -# error "Port me!" -#endif delete m->pAutostartDb; @@ -1129,6 +1108,7 @@ VirtualBox::COMGETTER(PerformanceCollector)(IPerformanceCollector **aPerformance return S_OK; #else /* !VBOX_WITH_RESOURCE_USAGE_API */ + NOREF(aPerformanceCollector); ReturnComNotImplemented(); #endif /* !VBOX_WITH_RESOURCE_USAGE_API */ } @@ -1148,6 +1128,31 @@ VirtualBox::COMGETTER(DHCPServers)(ComSafeArrayOut(IDHCPServer *, aDHCPServers)) return S_OK; } + +STDMETHODIMP +VirtualBox::COMGETTER(NATNetworks)(ComSafeArrayOut(INATNetwork *, aNATNetworks)) +{ +#ifdef VBOX_WITH_NAT_SERVICE + CheckComArgOutSafeArrayPointerValid(aNATNetworks); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + AutoReadLock al(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS); + SafeIfaceArray<INATNetwork> nets(m->allNATNetworks.getList()); + nets.detachTo(ComSafeArrayOutArg(aNATNetworks)); + + return S_OK; +#else + NOREF(aNATNetworks); +# ifndef RT_OS_WINDOWS + NOREF(aNATNetworksSize); +# endif + return E_NOTIMPL; +#endif +} + + STDMETHODIMP VirtualBox::COMGETTER(EventSource)(IEventSource ** aEventSource) { @@ -1452,8 +1457,16 @@ STDMETHODIMP VirtualBox::ComposeMachineFilename(IN_BSTR aName, pcszNext += strFlag.length() + 1; } } - if (id.isEmpty()) + + if (id.isZero()) fDirectoryIncludesUUID = false; + else if (!id.isValid()) + { + /* do something else */ + return setError(E_INVALIDARG, + tr("'%ls' is not a valid Guid"), + id.toStringCurly().c_str()); + } Utf8Str strGroup(aGroup); if (strGroup.isEmpty()) @@ -1514,13 +1527,13 @@ void sanitiseMachineFilename(Utf8Str &strName) * *nix, or be otherwise difficult for shells to handle (I would have * preferred to remove the space and brackets too). We also remove all * characters which need UTF-16 surrogate pairs for Windows's benefit. */ -#ifdef RT_STRICT RTUNICP aCpSet[] = { ' ', ' ', '(', ')', '-', '.', '0', '9', 'A', 'Z', 'a', 'z', '_', '_', 0xa0, 0xd7af, '\0' }; -#endif char *pszName = strName.mutableRaw(); - Assert(RTStrPurgeComplementSet(pszName, aCpSet, '_') >= 0); + int cReplacements = RTStrPurgeComplementSet(pszName, aCpSet, '_'); + Assert(cReplacements >= 0); + NOREF(cReplacements); /* No leading dot or dash. */ if (pszName[0] == '.' || pszName[0] == '-') pszName[0] = '_'; @@ -1648,8 +1661,15 @@ STDMETHODIMP VirtualBox::CreateMachine(IN_BSTR aSettingsFile, } } /* Create UUID if none was specified. */ - if (id.isEmpty()) + if (id.isZero()) id.create(); + else if (!id.isValid()) + { + /* do something else */ + return setError(E_INVALIDARG, + tr("'%ls' is not a valid Guid"), + id.toStringCurly().c_str()); + } /* NULL settings file means compose automatically */ Bstr bstrSettingsFile(aSettingsFile); @@ -1779,7 +1799,8 @@ STDMETHODIMP VirtualBox::FindMachine(IN_BSTR aNameOrId, IMachine **aMachine) ComObjPtr<Machine> pMachineFound; Guid id(aNameOrId); - if (!id.isEmpty()) + if (id.isValid() && !id.isZero()) + rc = findMachine(id, true /* fPermitInaccessible */, true /* setError */, @@ -1939,6 +1960,7 @@ STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation, AutoCaller autoCaller(this); if (FAILED(autoCaller.rc())) return autoCaller.rc(); + Guid id(aLocation); ComObjPtr<Medium> pMedium; // have to get write lock as the whole find/update sequence must be done @@ -1951,18 +1973,22 @@ STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation, switch (deviceType) { case DeviceType_HardDisk: - rc = findHardDiskByLocation(aLocation, - false, /* aSetError */ - &pMedium); + if (id.isValid() && !id.isZero()) + rc = findHardDiskById(id, false /* setError */, &pMedium); + else + rc = findHardDiskByLocation(aLocation, + false, /* aSetError */ + &pMedium); break; case DeviceType_Floppy: case DeviceType_DVD: - rc = findDVDOrFloppyImage(deviceType, - NULL, /* guid */ - aLocation, - false, /* aSetError */ - &pMedium); + if (id.isValid() && !id.isZero()) + rc = findDVDOrFloppyImage(deviceType, &id, Utf8Str::Empty, + false /* setError */, &pMedium); + else + rc = findDVDOrFloppyImage(deviceType, NULL, aLocation, + false /* setError */, &pMedium); // enforce read-only for DVDs even if caller specified ReadWrite if (deviceType == DeviceType_DVD) @@ -1973,7 +1999,6 @@ STDMETHODIMP VirtualBox::OpenMedium(IN_BSTR aLocation, return setError(E_INVALIDARG, "Device type must be HardDisk, DVD or Floppy %d", deviceType); } - if (pMedium.isNull()) { pMedium.createObject(); @@ -2768,7 +2793,7 @@ VirtualBox::SVCHelperClientThread(RTTHREAD aThread, void *aUser) #endif /* RT_OS_WINDOWS */ /** - * Sends a signal to the client watcher thread to rescan the set of machines + * Sends a signal to the client watcher to rescan the set of machines * that have open sessions. * * @note Doesn't lock anything. @@ -2778,35 +2803,23 @@ void VirtualBox::updateClientWatcher() AutoCaller autoCaller(this); AssertComRCReturnVoid(autoCaller.rc()); - AssertReturnVoid(m->threadClientWatcher != NIL_RTTHREAD); - - /* sent an update request */ -#if defined(RT_OS_WINDOWS) - ::SetEvent(m->updateReq); -#elif defined(RT_OS_OS2) - RTSemEventSignal(m->updateReq); -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) - ASMAtomicUoWriteU8(&m->updateAdaptCtr, RT_ELEMENTS(s_updateAdaptTimeouts) - 1); - RTSemEventSignal(m->updateReq); -#else -# error "Port me!" -#endif + AssertPtrReturnVoid(m->pClientWatcher); + m->pClientWatcher->update(); } /** * Adds the given child process ID to the list of processes to be reaped. * This call should be followed by #updateClientWatcher() to take the effect. + * + * @note Doesn't lock anything. */ void VirtualBox::addProcessToReap(RTPROCESS pid) { AutoCaller autoCaller(this); AssertComRCReturnVoid(autoCaller.rc()); - /// @todo (dmik) Win32? -#ifndef RT_OS_WINDOWS - AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); - m->llProcesses.push_back(pid); -#endif + AssertPtrReturnVoid(m->pClientWatcher); + m->pClientWatcher->addProcess(pid); } /** Event for onMachineStateChange(), onMachineDataChange(), onMachineRegistered() */ @@ -2977,8 +2990,8 @@ struct SnapshotEvent : public VirtualBox::CallbackEvent virtual HRESULT prepareEventDesc(IEventSource* aSource, VBoxEventDesc& aEvDesc) { - return aEvDesc.init(aSource, VBoxEventType_OnSnapshotTaken, - machineId.toUtf16().raw(), snapshotId.toUtf16().raw()); + return aEvDesc.init(aSource, mWhat, machineId.toUtf16().raw(), + snapshotId.toUtf16().raw()); } Guid machineId; @@ -3043,65 +3056,108 @@ void VirtualBox::onGuestPropertyChange(const Guid &aMachineId, IN_BSTR aName, postEvent(new GuestPropertyEvent(this, aMachineId, aName, aValue, aFlags)); } -/** Event for onMachineUninit(), this is not a CallbackEvent */ -class MachineUninitEvent : public Event +/** + * @note Doesn't lock any object. + */ +void VirtualBox::onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName, + NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort, + IN_BSTR aGuestIp, uint16_t aGuestPort) { -public: + fireNATRedirectEvent(m->pEventSource, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp, + aHostPort, aGuestIp, aGuestPort); +} - MachineUninitEvent(VirtualBox *aVirtualBox, Machine *aMachine) - : mVirtualBox(aVirtualBox), mMachine(aMachine) - { - Assert(aVirtualBox); - Assert(aMachine); - } +void VirtualBox::onNATNetworkChange(IN_BSTR aName) +{ + fireNATNetworkChangedEvent(m->pEventSource, aName); +} - void *handler() - { -#ifdef VBOX_WITH_RESOURCE_USAGE_API - /* Handle unregistering metrics here, as it is not vital to get - * it done immediately. It reduces the number of locks needed and - * the lock contention in SessionMachine::uninit. */ - { - AutoWriteLock mLock(mMachine COMMA_LOCKVAL_SRC_POS); - mMachine->unregisterMetrics(mVirtualBox->performanceCollector(), mMachine); - } -#endif /* VBOX_WITH_RESOURCE_USAGE_API */ +void VirtualBox::onNATNetworkStartStop(IN_BSTR aName, BOOL fStart) +{ + fireNATNetworkStartStopEvent(m->pEventSource, aName, fStart); +} +void VirtualBox::onNATNetworkSetting(IN_BSTR aNetworkName, BOOL aEnabled, + IN_BSTR aNetwork, IN_BSTR aGateway, + BOOL aAdvertiseDefaultIpv6RouteEnabled, + BOOL fNeedDhcpServer) +{ + fireNATNetworkSettingEvent(m->pEventSource, aNetworkName, aEnabled, + aNetwork, aGateway, + aAdvertiseDefaultIpv6RouteEnabled, fNeedDhcpServer); +} - return NULL; - } +void VirtualBox::onNATNetworkPortForward(IN_BSTR aNetworkName, BOOL create, BOOL fIpv6, + IN_BSTR aRuleName, NATProtocol_T proto, + IN_BSTR aHostIp, LONG aHostPort, + IN_BSTR aGuestIp, LONG aGuestPort) +{ + fireNATNetworkPortForwardEvent(m->pEventSource, aNetworkName, create, + fIpv6, aRuleName, proto, + aHostIp, aHostPort, + aGuestIp, aGuestPort); +} -private: - /** - * Note that this is a weak ref -- the CallbackEvent handler thread - * is bound to the lifetime of the VirtualBox instance, so it's safe. - */ - VirtualBox *mVirtualBox; +void VirtualBox::onHostNameResolutionConfigurationChange() +{ + if (m->pEventSource) + fireHostNameResolutionConfigurationChangeEvent(m->pEventSource); +} - /** Reference to the machine object. */ - ComObjPtr<Machine> mMachine; -}; -/** - * Trigger internal event. This isn't meant to be signalled to clients. - * @note Doesn't lock any object. - */ -void VirtualBox::onMachineUninit(Machine *aMachine) +int VirtualBox::natNetworkRefInc(IN_BSTR aNetworkName) { - postEvent(new MachineUninitEvent(this, aMachine)); + AutoWriteLock safeLock(*spMtxNatNetworkNameToRefCountLock COMMA_LOCKVAL_SRC_POS); + Bstr name(aNetworkName); + + if (!sNatNetworkNameToRefCount[name]) + { + ComPtr<INATNetwork> nat; + HRESULT rc = FindNATNetworkByName(aNetworkName, nat.asOutParam()); + if (FAILED(rc)) return -1; + + rc = nat->Start(Bstr("whatever").raw()); + if (SUCCEEDED(rc)) + LogRel(("Started NAT network '%ls'\n", aNetworkName)); + else + LogRel(("Error %Rhrc starting NAT network '%ls'\n", rc, aNetworkName)); + AssertComRCReturn(rc, -1); + } + + sNatNetworkNameToRefCount[name]++; + + return sNatNetworkNameToRefCount[name]; } -/** - * @note Doesn't lock any object. - */ -void VirtualBox::onNatRedirectChange(const Guid &aMachineId, ULONG ulSlot, bool fRemove, IN_BSTR aName, - NATProtocol_T aProto, IN_BSTR aHostIp, uint16_t aHostPort, - IN_BSTR aGuestIp, uint16_t aGuestPort) + +int VirtualBox::natNetworkRefDec(IN_BSTR aNetworkName) { - fireNATRedirectEvent(m->pEventSource, aMachineId.toUtf16().raw(), ulSlot, fRemove, aName, aProto, aHostIp, - aHostPort, aGuestIp, aGuestPort); + AutoWriteLock safeLock(*spMtxNatNetworkNameToRefCountLock COMMA_LOCKVAL_SRC_POS); + Bstr name(aNetworkName); + + if (!sNatNetworkNameToRefCount[name]) + return 0; + + sNatNetworkNameToRefCount[name]--; + + if (!sNatNetworkNameToRefCount[name]) + { + ComPtr<INATNetwork> nat; + HRESULT rc = FindNATNetworkByName(aNetworkName, nat.asOutParam()); + if (FAILED(rc)) return -1; + + rc = nat->Stop(); + if (SUCCEEDED(rc)) + LogRel(("Stopped NAT network '%ls'\n", aNetworkName)); + else + LogRel(("Error %Rhrc stopping NAT network '%ls'\n", rc, aNetworkName)); + AssertComRCReturn(rc, -1); + } + + return sNatNetworkNameToRefCount[name]; } + /** * @note Locks this object for reading. */ @@ -3161,6 +3217,19 @@ void VirtualBox::getOpenedMachines(SessionMachinesList &aMachines, } /** + * Gets a reference to the machine list. This is the real thing, not a copy, + * so bad things will happen if the caller doesn't hold the necessary lock. + * + * @returns reference to machine list + * + * @note Caller must hold the VirtualBox object lock at least for reading. + */ +VirtualBox::MachinesOList &VirtualBox::getMachinesList(void) +{ + return m->allMachines; +} + +/** * Searches for a machine object with the given ID in the collection * of registered machines. * @@ -3402,7 +3471,7 @@ HRESULT VirtualBox::findHardDiskById(const Guid &id, bool aSetError, ComObjPtr<Medium> *aHardDisk /*= NULL*/) { - AssertReturn(!id.isEmpty(), E_INVALIDARG); + AssertReturn(!id.isZero(), E_INVALIDARG); // we use the hard disks map, but it is protected by the // hard disk _list_ lock handle @@ -3609,12 +3678,19 @@ HRESULT VirtualBox::findRemoveableMedium(DeviceType_T mediumType, bool aSetError, ComObjPtr<Medium> &pMedium) { - if (uuid.isEmpty()) + if (uuid.isZero()) { // that's easy pMedium.setNull(); return S_OK; } + else if (!uuid.isValid()) + { + /* handling of case invalid GUID */ + return setError(VBOX_E_OBJECT_NOT_FOUND, + tr("Guid '%ls' is invalid"), + uuid.toString().c_str()); + } // first search for host drive with that UUID HRESULT rc = m->pHost->findHostDriveById(mediumType, @@ -3816,7 +3892,7 @@ HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId, Utf8Str &aConflict, ComObjPtr<Medium> *ppMedium) { - AssertReturn(!aId.isEmpty() && !aLocation.isEmpty(), E_FAIL); + AssertReturn(!aId.isZero() && !aLocation.isEmpty(), E_FAIL); AssertReturn(ppMedium, E_INVALIDARG); aConflict.setNull(); @@ -3829,7 +3905,7 @@ HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId, ComObjPtr<Medium> pMediumFound; const char *pcszType = NULL; - if (!aId.isEmpty()) + if (aId.isValid() && !aId.isZero()) rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound); if (FAILED(rc) && !aLocation.isEmpty()) rc = findHardDiskByLocation(aLocation, false /* aSetError */, &pMediumFound); @@ -3873,6 +3949,49 @@ HRESULT VirtualBox::checkMediaForConflicts(const Guid &aId, } /** + * Checks whether the given UUID is already in use by one medium for the + * given device type. + * + * @returns true if the UUID is already in use + * fale otherwise + * @param aId The UUID to check. + * @param deviceType The device type the UUID is going to be checked for + * conflicts. + */ +bool VirtualBox::isMediaUuidInUse(const Guid &aId, DeviceType_T deviceType) +{ + /* A zero UUID is invalid here, always claim that it is already used. */ + AssertReturn(!aId.isZero(), true); + + AutoReadLock alock(getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); + + HRESULT rc = S_OK; + bool fInUse = false; + + ComObjPtr<Medium> pMediumFound; + + switch (deviceType) + { + case DeviceType_HardDisk: + rc = findHardDiskById(aId, false /* aSetError */, &pMediumFound); + break; + case DeviceType_DVD: + rc = findDVDOrFloppyImage(DeviceType_DVD, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound); + break; + case DeviceType_Floppy: + rc = findDVDOrFloppyImage(DeviceType_Floppy, &aId, Utf8Str::Empty, false /* aSetError */, &pMediumFound); + break; + default: + AssertMsgFailed(("Invalid device type %d\n", deviceType)); + } + + if (SUCCEEDED(rc) && pMediumFound) + fInUse = true; + + return fInUse; +} + +/** * Called from Machine::prepareSaveSettings() when it has detected * that a machine has been renamed. Such renames will require * updating the global media registry during the @@ -4129,6 +4248,23 @@ HRESULT VirtualBox::saveSettings() } } +#ifdef VBOX_WITH_NAT_SERVICE + /* Saving NAT Network configuration */ + m->pMainConfigFile->llNATNetworks.clear(); + { + AutoReadLock natNetworkLock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS); + for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin(); + it != m->allNATNetworks.end(); + ++it) + { + settings::NATNetwork n; + rc = (*it)->saveSettings(n); + if (FAILED(rc)) throw rc; + m->pMainConfigFile->llNATNetworks.push_back(n); + } + } +#endif + // leave extra data alone, it's still in the config file // host data (USB filters) @@ -4418,7 +4554,7 @@ void VirtualBox::pushMediumToListWithChildren(MediaList &llMedia, Medium *pMediu */ HRESULT VirtualBox::unregisterMachineMedia(const Guid &uuidMachine) { - Assert(!uuidMachine.isEmpty()); + Assert(!uuidMachine.isZero() && uuidMachine.isValid()); LogFlowFuncEnter(); @@ -4670,6 +4806,18 @@ const Utf8Str& VirtualBox::settingsFilePath() } /** + * Returns the lock handle which protects the machines list. As opposed + * to version 3.1 and earlier, these lists are no longer protected by the + * VirtualBox lock, but by this more specialized lock. Mind the locking + * order: always request this lock after the VirtualBox object lock but + * before the locks of any machine object. See AutoLock.h. + */ +RWLockHandle& VirtualBox::getMachinesListLockHandle() +{ + return m->lockMachines; +} + +/** * Returns the lock handle which protects the media trees (hard disks, * DVDs, floppies). As opposed to version 3.1 and earlier, these lists * are no longer protected by the VirtualBox lock, but by this more @@ -4683,541 +4831,62 @@ RWLockHandle& VirtualBox::getMediaTreeLockHandle() } /** - * Thread function that watches the termination of all client processes - * that have opened sessions using IMachine::LockMachine() + * Thread function that handles custom events posted using #postEvent(). */ // static -DECLCALLBACK(int) VirtualBox::ClientWatcher(RTTHREAD /* thread */, void *pvUser) +DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser) { LogFlowFuncEnter(); - VirtualBox *that = (VirtualBox*)pvUser; - Assert(that); - - typedef std::vector< ComObjPtr<Machine> > MachineVector; - typedef std::vector< ComObjPtr<SessionMachine> > SessionMachineVector; - - SessionMachineVector machines; - MachineVector spawnedMachines; - - size_t cnt = 0; - size_t cntSpawned = 0; - - VirtualBoxBase::initializeComForThread(); - -#if defined(RT_OS_WINDOWS) + AssertReturn(pvUser, VERR_INVALID_POINTER); - /// @todo (dmik) processes reaping! + HRESULT hr = com::Initialize(); + if (FAILED(hr)) + return VERR_COM_UNEXPECTED; - HANDLE handles[MAXIMUM_WAIT_OBJECTS]; - handles[0] = that->m->updateReq; + int rc = VINF_SUCCESS; - do + try { - AutoCaller autoCaller(that); - /* VirtualBox has been early uninitialized, terminate */ - if (!autoCaller.isOk()) - break; - - do - { - /* release the caller to let uninit() ever proceed */ - autoCaller.release(); - - DWORD rc = ::WaitForMultipleObjects((DWORD)(1 + cnt + cntSpawned), - handles, - FALSE, - INFINITE); - - /* Restore the caller before using VirtualBox. If it fails, this - * means VirtualBox is being uninitialized and we must terminate. */ - autoCaller.add(); - if (!autoCaller.isOk()) - break; - - bool update = false; - - if (rc == WAIT_OBJECT_0) - { - /* update event is signaled */ - update = true; - } - else if (rc > WAIT_OBJECT_0 && rc <= (WAIT_OBJECT_0 + cnt)) - { - /* machine mutex is released */ - (machines[rc - WAIT_OBJECT_0 - 1])->checkForDeath(); - update = true; - } - else if (rc > WAIT_ABANDONED_0 && rc <= (WAIT_ABANDONED_0 + cnt)) - { - /* machine mutex is abandoned due to client process termination */ - (machines[rc - WAIT_ABANDONED_0 - 1])->checkForDeath(); - update = true; - } - else if (rc > WAIT_OBJECT_0 + cnt && rc <= (WAIT_OBJECT_0 + cntSpawned)) - { - /* spawned VM process has terminated (normally or abnormally) */ - (spawnedMachines[rc - WAIT_OBJECT_0 - cnt - 1])-> - checkForSpawnFailure(); - update = true; - } - - if (update) - { - /* close old process handles */ - for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++i) - CloseHandle(handles[i]); - - // lock the machines list for reading - AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); - - /* obtain a new set of opened machines */ - cnt = 0; - machines.clear(); - - for (MachinesOList::iterator it = that->m->allMachines.begin(); - it != that->m->allMachines.end(); - ++it) - { - /// @todo handle situations with more than 64 objects - AssertMsgBreak((1 + cnt) <= MAXIMUM_WAIT_OBJECTS, - ("MAXIMUM_WAIT_OBJECTS reached")); - - ComObjPtr<SessionMachine> sm; - HANDLE ipcSem; - if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem)) - { - machines.push_back(sm); - handles[1 + cnt] = ipcSem; - ++cnt; - } - } - - LogFlowFunc(("UPDATE: direct session count = %d\n", cnt)); - - /* obtain a new set of spawned machines */ - cntSpawned = 0; - spawnedMachines.clear(); + /* Create an event queue for the current thread. */ + EventQueue *pEventQueue = new EventQueue(); + AssertPtr(pEventQueue); - for (MachinesOList::iterator it = that->m->allMachines.begin(); - it != that->m->allMachines.end(); - ++it) - { - /// @todo handle situations with more than 64 objects - AssertMsgBreak((1 + cnt + cntSpawned) <= MAXIMUM_WAIT_OBJECTS, - ("MAXIMUM_WAIT_OBJECTS reached")); - - RTPROCESS pid; - if ((*it)->isSessionSpawning(&pid)) - { - HANDLE ph = OpenProcess(SYNCHRONIZE, FALSE, pid); - AssertMsg(ph != NULL, ("OpenProcess (pid=%d) failed with %d\n", - pid, GetLastError())); - if (rc == 0) - { - spawnedMachines.push_back(*it); - handles[1 + cnt + cntSpawned] = ph; - ++cntSpawned; - } - } - } - - LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned)); - - // machines lock unwinds here - } - } - while (true); - } - while (0); - - /* close old process handles */ - for (size_t i = 1 + cnt; i < 1 + cnt + cntSpawned; ++ i) - CloseHandle(handles[i]); - - /* release sets of machines if any */ - machines.clear(); - spawnedMachines.clear(); - - ::CoUninitialize(); - -#elif defined(RT_OS_OS2) - - /// @todo (dmik) processes reaping! + /* Return the queue to the one who created this thread. */ + *(static_cast <EventQueue **>(pvUser)) = pEventQueue; - /* according to PMREF, 64 is the maximum for the muxwait list */ - SEMRECORD handles[64]; + /* signal that we're ready. */ + RTThreadUserSignal(thread); - HMUX muxSem = NULLHANDLE; - - do - { - AutoCaller autoCaller(that); - /* VirtualBox has been early uninitialized, terminate */ - if (!autoCaller.isOk()) - break; - - do + /* + * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes + * we must not stop processing events and delete the pEventQueue object. This must + * be done ONLY when we stop this loop via interruptEventQueueProcessing(). + * See @bugref{5724}. + */ + for (;;) { - /* release the caller to let uninit() ever proceed */ - autoCaller.release(); - - int vrc = RTSemEventWait(that->m->updateReq, 500); - - /* Restore the caller before using VirtualBox. If it fails, this - * means VirtualBox is being uninitialized and we must terminate. */ - autoCaller.add(); - if (!autoCaller.isOk()) - break; - - bool update = false; - bool updateSpawned = false; - - if (RT_SUCCESS(vrc)) - { - /* update event is signaled */ - update = true; - updateSpawned = true; - } - else - { - AssertMsg(vrc == VERR_TIMEOUT || vrc == VERR_INTERRUPTED, - ("RTSemEventWait returned %Rrc\n", vrc)); - - /* are there any mutexes? */ - if (cnt > 0) - { - /* figure out what's going on with machines */ - - unsigned long semId = 0; - APIRET arc = ::DosWaitMuxWaitSem(muxSem, - SEM_IMMEDIATE_RETURN, &semId); - - if (arc == NO_ERROR) - { - /* machine mutex is normally released */ - Assert(semId >= 0 && semId < cnt); - if (semId >= 0 && semId < cnt) - { -#if 0//def DEBUG - { - AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS); - LogFlowFunc(("released mutex: machine='%ls'\n", - machines[semId]->name().raw())); - } -#endif - machines[semId]->checkForDeath(); - } - update = true; - } - else if (arc == ERROR_SEM_OWNER_DIED) - { - /* machine mutex is abandoned due to client process - * termination; find which mutex is in the Owner Died - * state */ - for (size_t i = 0; i < cnt; ++ i) - { - PID pid; TID tid; - unsigned long reqCnt; - arc = DosQueryMutexSem((HMTX)handles[i].hsemCur, &pid, &tid, &reqCnt); - if (arc == ERROR_SEM_OWNER_DIED) - { - /* close the dead mutex as asked by PMREF */ - ::DosCloseMutexSem((HMTX)handles[i].hsemCur); - - Assert(i >= 0 && i < cnt); - if (i >= 0 && i < cnt) - { -#if 0//def DEBUG - { - AutoReadLock machineLock(machines[semId] COMMA_LOCKVAL_SRC_POS); - LogFlowFunc(("mutex owner dead: machine='%ls'\n", - machines[i]->name().raw())); - } -#endif - machines[i]->checkForDeath(); - } - } - } - update = true; - } - else - AssertMsg(arc == ERROR_INTERRUPT || arc == ERROR_TIMEOUT, - ("DosWaitMuxWaitSem returned %d\n", arc)); - } - - /* are there any spawning sessions? */ - if (cntSpawned > 0) - { - for (size_t i = 0; i < cntSpawned; ++ i) - updateSpawned |= (spawnedMachines[i])-> - checkForSpawnFailure(); - } - } - - if (update || updateSpawned) + rc = pEventQueue->processEventQueue(RT_INDEFINITE_WAIT); + if (rc == VERR_INTERRUPTED) { - AutoReadLock thatLock(that COMMA_LOCKVAL_SRC_POS); - - if (update) - { - /* close the old muxsem */ - if (muxSem != NULLHANDLE) - ::DosCloseMuxWaitSem(muxSem); - - /* obtain a new set of opened machines */ - cnt = 0; - machines.clear(); - - for (MachinesOList::iterator it = that->m->allMachines.begin(); - it != that->m->allMachines.end(); ++ it) - { - /// @todo handle situations with more than 64 objects - AssertMsg(cnt <= 64 /* according to PMREF */, - ("maximum of 64 mutex semaphores reached (%d)", - cnt)); - - ComObjPtr<SessionMachine> sm; - HMTX ipcSem; - if ((*it)->isSessionOpenOrClosing(sm, NULL, &ipcSem)) - { - machines.push_back(sm); - handles[cnt].hsemCur = (HSEM)ipcSem; - handles[cnt].ulUser = cnt; - ++ cnt; - } - } - - LogFlowFunc(("UPDATE: direct session count = %d\n", cnt)); - - if (cnt > 0) - { - /* create a new muxsem */ - APIRET arc = ::DosCreateMuxWaitSem(NULL, &muxSem, cnt, - handles, - DCMW_WAIT_ANY); - AssertMsg(arc == NO_ERROR, - ("DosCreateMuxWaitSem returned %d\n", arc)); - NOREF(arc); - } - } - - if (updateSpawned) - { - /* obtain a new set of spawned machines */ - spawnedMachines.clear(); - - for (MachinesOList::iterator it = that->m->allMachines.begin(); - it != that->m->allMachines.end(); ++ it) - { - if ((*it)->isSessionSpawning()) - spawnedMachines.push_back(*it); - } - - cntSpawned = spawnedMachines.size(); - LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned)); - } + LogFlow(("Event queue processing ended with rc=%Rrc\n", rc)); + rc = VINF_SUCCESS; /* Set success when exiting. */ + break; } } - while (true); - } - while (0); - - /* close the muxsem */ - if (muxSem != NULLHANDLE) - ::DosCloseMuxWaitSem(muxSem); - - /* release sets of machines if any */ - machines.clear(); - spawnedMachines.clear(); - -#elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER) - bool update = false; - bool updateSpawned = false; - - do + delete pEventQueue; + } + catch (std::bad_alloc &ba) { - AutoCaller autoCaller(that); - if (!autoCaller.isOk()) - break; - - do - { - /* release the caller to let uninit() ever proceed */ - autoCaller.release(); - - /* determine wait timeout adaptively: after updating information - * relevant to the client watcher, check a few times more - * frequently. This ensures good reaction time when the signalling - * has to be done a bit before the actual change for technical - * reasons, and saves CPU cycles when no activities are expected. */ - RTMSINTERVAL cMillies; - { - uint8_t uOld, uNew; - do - { - uOld = ASMAtomicUoReadU8(&that->m->updateAdaptCtr); - uNew = uOld ? uOld - 1 : uOld; - } while (!ASMAtomicCmpXchgU8(&that->m->updateAdaptCtr, uNew, uOld)); - Assert(uOld <= RT_ELEMENTS(s_updateAdaptTimeouts) - 1); - cMillies = s_updateAdaptTimeouts[uOld]; - } - - int rc = RTSemEventWait(that->m->updateReq, cMillies); - - /* - * Restore the caller before using VirtualBox. If it fails, this - * means VirtualBox is being uninitialized and we must terminate. - */ - autoCaller.add(); - if (!autoCaller.isOk()) - break; - - if (RT_SUCCESS(rc) || update || updateSpawned) - { - /* RT_SUCCESS(rc) means an update event is signaled */ - - // lock the machines list for reading - AutoReadLock thatLock(that->m->allMachines.getLockHandle() COMMA_LOCKVAL_SRC_POS); - - if (RT_SUCCESS(rc) || update) - { - /* obtain a new set of opened machines */ - machines.clear(); - - for (MachinesOList::iterator it = that->m->allMachines.begin(); - it != that->m->allMachines.end(); - ++it) - { - ComObjPtr<SessionMachine> sm; - if ((*it)->isSessionOpenOrClosing(sm)) - machines.push_back(sm); - } - - cnt = machines.size(); - LogFlowFunc(("UPDATE: direct session count = %d\n", cnt)); - } - - if (RT_SUCCESS(rc) || updateSpawned) - { - /* obtain a new set of spawned machines */ - spawnedMachines.clear(); - - for (MachinesOList::iterator it = that->m->allMachines.begin(); - it != that->m->allMachines.end(); - ++it) - { - if ((*it)->isSessionSpawning()) - spawnedMachines.push_back(*it); - } - - cntSpawned = spawnedMachines.size(); - LogFlowFunc(("UPDATE: spawned session count = %d\n", cntSpawned)); - } - - // machines lock unwinds here - } - - update = false; - for (size_t i = 0; i < cnt; ++ i) - update |= (machines[i])->checkForDeath(); - - updateSpawned = false; - for (size_t i = 0; i < cntSpawned; ++ i) - updateSpawned |= (spawnedMachines[i])->checkForSpawnFailure(); - - /* reap child processes */ - { - AutoWriteLock alock(that COMMA_LOCKVAL_SRC_POS); - if (that->m->llProcesses.size()) - { - LogFlowFunc(("UPDATE: child process count = %d\n", - that->m->llProcesses.size())); - VirtualBox::Data::ProcessList::iterator it = that->m->llProcesses.begin(); - while (it != that->m->llProcesses.end()) - { - RTPROCESS pid = *it; - RTPROCSTATUS status; - int vrc = ::RTProcWait(pid, RTPROCWAIT_FLAGS_NOBLOCK, &status); - if (vrc == VINF_SUCCESS) - { - LogFlowFunc(("pid %d (%x) was reaped, status=%d, reason=%d\n", - pid, pid, status.iStatus, - status.enmReason)); - it = that->m->llProcesses.erase(it); - } - else - { - LogFlowFunc(("pid %d (%x) was NOT reaped, vrc=%Rrc\n", - pid, pid, vrc)); - if (vrc != VERR_PROCESS_RUNNING) - { - /* remove the process if it is not already running */ - it = that->m->llProcesses.erase(it); - } - else - ++ it; - } - } - } - } - } - while (true); + rc = VERR_NO_MEMORY; + NOREF(ba); } - while (0); - - /* release sets of machines if any */ - machines.clear(); - spawnedMachines.clear(); - -#else -# error "Port me!" -#endif - - VirtualBoxBase::uninitializeComForThread(); - LogFlowFuncLeave(); - return 0; -} - -/** - * Thread function that handles custom events posted using #postEvent(). - */ -// static -DECLCALLBACK(int) VirtualBox::AsyncEventHandler(RTTHREAD thread, void *pvUser) -{ - LogFlowFuncEnter(); - - AssertReturn(pvUser, VERR_INVALID_POINTER); - - com::Initialize(); - - // create an event queue for the current thread - EventQueue *eventQ = new EventQueue(); - AssertReturn(eventQ, VERR_NO_MEMORY); - - // return the queue to the one who created this thread - *(static_cast <EventQueue **>(pvUser)) = eventQ; - // signal that we're ready - RTThreadUserSignal(thread); - - /* - * In case of spurious wakeups causing VERR_TIMEOUTs and/or other return codes - * we must not stop processing events and delete the "eventQ" object. This must - * be done ONLY when we stop this loop via interruptEventQueueProcessing(). - * See @bugref{5724}. - */ - while (eventQ->processEventQueue(RT_INDEFINITE_WAIT) != VERR_INTERRUPTED) - /* nothing */ ; - - delete eventQ; com::Shutdown(); - - LogFlowFuncLeave(); - - return 0; + LogFlowFuncLeaveRC(rc); + return rc; } @@ -5422,4 +5091,204 @@ HRESULT VirtualBox::unregisterDHCPServer(DHCPServer *aDHCPServer, return rc; } + +/** + * NAT Network + */ + +STDMETHODIMP VirtualBox::CreateNATNetwork(IN_BSTR aName, INATNetwork ** aNatNetwork) +{ +#ifdef VBOX_WITH_NAT_SERVICE + CheckComArgStrNotEmptyOrNull(aName); + CheckComArgNotNull(aNatNetwork); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + ComObjPtr<NATNetwork> natNetwork; + natNetwork.createObject(); + HRESULT rc = natNetwork->init(this, aName); + if (FAILED(rc)) return rc; + + rc = registerNATNetwork(natNetwork, true); + if (FAILED(rc)) return rc; + + natNetwork.queryInterfaceTo(aNatNetwork); + + fireNATNetworkCreationDeletionEvent(m->pEventSource, aName, TRUE); + return rc; +#else + NOREF(aName); + NOREF(aNatNetwork); + return E_NOTIMPL; +#endif +} + +STDMETHODIMP VirtualBox::FindNATNetworkByName(IN_BSTR aName, INATNetwork ** aNetwork) +{ +#ifdef VBOX_WITH_NAT_SERVICE + CheckComArgStrNotEmptyOrNull(aName); + CheckComArgNotNull(aNetwork); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + + HRESULT rc; + Bstr bstr; + ComPtr<NATNetwork> found; + + AutoReadLock alock(m->allNATNetworks.getLockHandle() COMMA_LOCKVAL_SRC_POS); + + for (NATNetworksOList::const_iterator it = m->allNATNetworks.begin(); + it != m->allNATNetworks.end(); + ++it) + { + rc = (*it)->COMGETTER(NetworkName)(bstr.asOutParam()); + if (FAILED(rc)) return rc; + + if (bstr == aName) + { + found = *it; + break; + } + } + + if (!found) + return E_INVALIDARG; + + return found.queryInterfaceTo(aNetwork); +#else + NOREF(aName); + NOREF(aNetwork); + return E_NOTIMPL; +#endif +} + +STDMETHODIMP VirtualBox::RemoveNATNetwork(INATNetwork * aNetwork) +{ +#ifdef VBOX_WITH_NAT_SERVICE + CheckComArgNotNull(aNetwork); + + AutoCaller autoCaller(this); + if (FAILED(autoCaller.rc())) return autoCaller.rc(); + Bstr name; + HRESULT rc; + NATNetwork *network = static_cast<NATNetwork *>(aNetwork); + rc = network->COMGETTER(NetworkName)(name.asOutParam()); + rc = unregisterNATNetwork(network, true); + fireNATNetworkCreationDeletionEvent(m->pEventSource, name.raw(), FALSE); + return rc; +#else + NOREF(aNetwork); + return E_NOTIMPL; +#endif + +} +/** + * Remembers the given NAT network in the settings. + * + * @param aNATNetwork NAT Network object to remember. + * @param aSaveSettings @c true to save settings to disk (default). + * + * + * @note Locks this object for writing and @a aNATNetwork for reading. + */ +HRESULT VirtualBox::registerNATNetwork(NATNetwork *aNATNetwork, + bool aSaveSettings /*= true*/) +{ +#ifdef VBOX_WITH_NAT_SERVICE + AssertReturn(aNATNetwork != NULL, E_INVALIDARG); + + AutoCaller autoCaller(this); + AssertComRCReturnRC(autoCaller.rc()); + + AutoCaller natNetworkCaller(aNATNetwork); + AssertComRCReturnRC(natNetworkCaller.rc()); + + Bstr name; + HRESULT rc; + rc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam()); + AssertComRCReturnRC(rc); + + /* returned value isn't 0 and aSaveSettings is true + * means that we create duplicate, otherwise we just load settings. + */ + if ( sNatNetworkNameToRefCount[name] + && aSaveSettings) + AssertComRCReturnRC(E_INVALIDARG); + + rc = S_OK; + + sNatNetworkNameToRefCount[name] = 0; + + m->allNATNetworks.addChild(aNATNetwork); + + if (aSaveSettings) + { + AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS); + rc = saveSettings(); + vboxLock.release(); + + if (FAILED(rc)) + unregisterNATNetwork(aNATNetwork, false /* aSaveSettings */); + } + + return rc; +#else + NOREF(aNATNetwork); + NOREF(aSaveSettings); + /* No panic please (silently ignore) */ + return S_OK; +#endif +} + +/** + * Removes the given NAT network from the settings. + * + * @param aNATNetwork NAT network object to remove. + * @param aSaveSettings @c true to save settings to disk (default). + * + * When @a aSaveSettings is @c true, this operation may fail because of the + * failed #saveSettings() method it calls. In this case, the DHCP server + * will NOT be removed from the settingsi when this method returns. + * + * @note Locks this object for writing. + */ +HRESULT VirtualBox::unregisterNATNetwork(NATNetwork *aNATNetwork, + bool aSaveSettings /*= true*/) +{ +#ifdef VBOX_WITH_NAT_SERVICE + AssertReturn(aNATNetwork != NULL, E_INVALIDARG); + + AutoCaller autoCaller(this); + AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); + + AutoCaller natNetworkCaller(aNATNetwork); + AssertComRCReturn(natNetworkCaller.rc(), natNetworkCaller.rc()); + + Bstr name; + HRESULT rc = aNATNetwork->COMGETTER(NetworkName)(name.asOutParam()); + /* Hm, there're still running clients. */ + if (FAILED(rc) || sNatNetworkNameToRefCount[name]) + AssertComRCReturnRC(E_INVALIDARG); + + m->allNATNetworks.removeChild(aNATNetwork); + + if (aSaveSettings) + { + AutoWriteLock vboxLock(this COMMA_LOCKVAL_SRC_POS); + rc = saveSettings(); + vboxLock.release(); + + if (FAILED(rc)) + registerNATNetwork(aNATNetwork, false /* aSaveSettings */); + } + + return rc; +#else + NOREF(aNATNetwork); + NOREF(aSaveSettings); + return E_NOTIMPL; +#endif +} /* vi: set tabstop=4 shiftwidth=4 expandtab: */ diff --git a/src/VBox/Main/src-server/darwin/HostDnsServiceDarwin.cpp b/src/VBox/Main/src-server/darwin/HostDnsServiceDarwin.cpp new file mode 100644 index 00000000..6e91e7e6 --- /dev/null +++ b/src/VBox/Main/src-server/darwin/HostDnsServiceDarwin.cpp @@ -0,0 +1,256 @@ +/* $Id: HostDnsServiceDarwin.cpp $ */ +/** @file + * Darwin specific DNS information fetching. + */ + +/* + * Copyright (C) 2004-2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <VBox/com/string.h> +#include <VBox/com/ptr.h> + + +#include <iprt/err.h> +#include <iprt/thread.h> +#include <iprt/semaphore.h> + +#include <CoreFoundation/CoreFoundation.h> +#include <SystemConfiguration/SCDynamicStore.h> + +#include <string> +#include <vector> +#include "../HostDnsService.h" + + +struct HostDnsServiceDarwin::Data +{ + SCDynamicStoreRef m_store; + CFRunLoopSourceRef m_DnsWatcher; + CFRunLoopRef m_RunLoopRef; + CFRunLoopSourceRef m_Stopper; + bool m_fStop; + RTSEMEVENT m_evtStop; + static void performShutdownCallback(void *); +}; + + +static const CFStringRef kStateNetworkGlobalDNSKey = CFSTR("State:/Network/Global/DNS"); + + +HostDnsServiceDarwin::HostDnsServiceDarwin():HostDnsMonitor(true),m(NULL) +{ + m = new HostDnsServiceDarwin::Data(); +} + + +HostDnsServiceDarwin::~HostDnsServiceDarwin() +{ + if (!m) + return; + + monitorThreadShutdown(); + + CFRelease(m->m_RunLoopRef); + + CFRelease(m->m_DnsWatcher); + + CFRelease(m->m_store); + + RTSemEventDestroy(m->m_evtStop); + + delete m; + m = NULL; +} + + +void HostDnsServiceDarwin::hostDnsServiceStoreCallback(void *, void *, void *info) +{ + HostDnsServiceDarwin *pThis = (HostDnsServiceDarwin *)info; + + ALock l(pThis); + pThis->updateInfo(); + pThis->notifyAll(); +} + + +HRESULT HostDnsServiceDarwin::init() +{ + SCDynamicStoreContext ctx; + RT_ZERO(ctx); + + ctx.info = this; + + m->m_store = SCDynamicStoreCreate(NULL, CFSTR("org.virtualbox.VBoxSVC"), + (SCDynamicStoreCallBack)HostDnsServiceDarwin::hostDnsServiceStoreCallback, + &ctx); + AssertReturn(m->m_store, E_FAIL); + + m->m_DnsWatcher = SCDynamicStoreCreateRunLoopSource(NULL, m->m_store, 0); + if (!m->m_DnsWatcher) + return E_OUTOFMEMORY; + + int rc = RTSemEventCreate(&m->m_evtStop); + AssertRCReturn(rc, E_FAIL); + + CFRunLoopSourceContext sctx; + RT_ZERO(sctx); + sctx.perform = HostDnsServiceDarwin::Data::performShutdownCallback; + m->m_Stopper = CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &sctx); + AssertReturn(m->m_Stopper, E_FAIL); + + HRESULT hrc = HostDnsMonitor::init(); + AssertComRCReturn(hrc, hrc); + + return updateInfo(); +} + + +void HostDnsServiceDarwin::monitorThreadShutdown() +{ + ALock l(this); + if (!m->m_fStop) + { + CFRunLoopSourceSignal(m->m_Stopper); + CFRunLoopWakeUp(m->m_RunLoopRef); + + RTSemEventWait(m->m_evtStop, RT_INDEFINITE_WAIT); + } +} + + +int HostDnsServiceDarwin::monitorWorker() +{ + m->m_RunLoopRef = CFRunLoopGetCurrent(); + AssertReturn(m->m_RunLoopRef, VERR_INTERNAL_ERROR); + + CFRetain(m->m_RunLoopRef); + + CFArrayRef watchingArrayRef = CFArrayCreate(NULL, + (const void **)&kStateNetworkGlobalDNSKey, + 1, &kCFTypeArrayCallBacks); + if (!watchingArrayRef) + { + CFRelease(m->m_DnsWatcher); + return E_OUTOFMEMORY; + } + + if(SCDynamicStoreSetNotificationKeys(m->m_store, watchingArrayRef, NULL)) + CFRunLoopAddSource(CFRunLoopGetCurrent(), m->m_DnsWatcher, kCFRunLoopCommonModes); + + CFRelease(watchingArrayRef); + + monitorThreadInitializationDone(); + + while (!m->m_fStop) + { + CFRunLoopRun(); + } + + CFRelease(m->m_RunLoopRef); + + /* We're notifying stopper thread. */ + RTSemEventSignal(m->m_evtStop); + + return VINF_SUCCESS; +} + + +HRESULT HostDnsServiceDarwin::updateInfo() +{ + CFPropertyListRef propertyRef = SCDynamicStoreCopyValue(m->m_store, + kStateNetworkGlobalDNSKey); + /** + * 0:vvl@nb-mbp-i7-2(0)# scutil + * > get State:/Network/Global/DNS + * > d.show + * <dictionary> { + * DomainName : vvl-domain + * SearchDomains : <array> { + * 0 : vvl-domain + * 1 : de.vvl-domain.com + * } + * ServerAddresses : <array> { + * 0 : 192.168.1.4 + * 1 : 192.168.1.1 + * 2 : 8.8.4.4 + * } + * } + */ + + if (!propertyRef) + return S_OK; + + HostDnsInformation info; + CFStringRef domainNameRef = (CFStringRef)CFDictionaryGetValue( + static_cast<CFDictionaryRef>(propertyRef), CFSTR("DomainName")); + if (domainNameRef) + { + const char *pszDomainName = CFStringGetCStringPtr(domainNameRef, + CFStringGetSystemEncoding()); + if (pszDomainName) + info.domain = pszDomainName; + } + + int i, arrayCount; + CFArrayRef serverArrayRef = (CFArrayRef)CFDictionaryGetValue( + static_cast<CFDictionaryRef>(propertyRef), CFSTR("ServerAddresses")); + if (serverArrayRef) + { + arrayCount = CFArrayGetCount(serverArrayRef); + for (i = 0; i < arrayCount; ++i) + { + CFStringRef serverAddressRef = (CFStringRef)CFArrayGetValueAtIndex(serverArrayRef, i); + if (!serverArrayRef) + continue; + + const char *pszServerAddress = CFStringGetCStringPtr(serverAddressRef, + CFStringGetSystemEncoding()); + if (!pszServerAddress) + continue; + + info.servers.push_back(std::string(pszServerAddress)); + } + } + + CFArrayRef searchArrayRef = (CFArrayRef)CFDictionaryGetValue( + static_cast<CFDictionaryRef>(propertyRef), CFSTR("SearchDomains")); + if (searchArrayRef) + { + arrayCount = CFArrayGetCount(searchArrayRef); + + for (i = 0; i < arrayCount; ++i) + { + CFStringRef searchStringRef = (CFStringRef)CFArrayGetValueAtIndex(searchArrayRef, i); + if (!searchArrayRef) + continue; + + const char *pszSearchString = CFStringGetCStringPtr(searchStringRef, + CFStringGetSystemEncoding()); + if (!pszSearchString) + continue; + + info.searchList.push_back(std::string(pszSearchString)); + } + } + + CFRelease(propertyRef); + + setInfo(info); + + return S_OK; +} + +void HostDnsServiceDarwin::Data::performShutdownCallback(void *info) +{ + HostDnsServiceDarwin::Data *pThis = static_cast<HostDnsServiceDarwin::Data *>(info); + pThis->m_fStop = true; +} diff --git a/src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp b/src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp index a83297e4..5e68d02b 100644 --- a/src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp +++ b/src/VBox/Main/src-server/darwin/HostPowerDarwin.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -25,43 +25,43 @@ #define POWER_SOURCE_OUTLET 1 #define POWER_SOURCE_BATTERY 2 -HostPowerServiceDarwin::HostPowerServiceDarwin (VirtualBox *aVirtualBox) - : HostPowerService (aVirtualBox) - , mThread (NULL) - , mRootPort (MACH_PORT_NULL) - , mNotifyPort (nil) - , mRunLoop (nil) - , mCritical (false) +HostPowerServiceDarwin::HostPowerServiceDarwin(VirtualBox *aVirtualBox) + : HostPowerService(aVirtualBox) + , mThread(NULL) + , mRootPort(MACH_PORT_NULL) + , mNotifyPort(nil) + , mRunLoop(nil) + , mCritical(false) { /* Create the new worker thread. */ - int rc = RTThreadCreate (&mThread, HostPowerServiceDarwin::powerChangeNotificationThread, this, 65536, - RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "MainPower"); + int rc = RTThreadCreate(&mThread, HostPowerServiceDarwin::powerChangeNotificationThread, this, 65536, + RTTHREADTYPE_IO, RTTHREADFLAGS_WAITABLE, "MainPower"); if (RT_FAILURE(rc)) - LogFlow (("RTThreadCreate failed with %Rrc\n", rc)); + LogFlow(("RTThreadCreate failed with %Rrc\n", rc)); } HostPowerServiceDarwin::~HostPowerServiceDarwin() { /* Jump out of the run loop. */ - CFRunLoopStop (mRunLoop); + CFRunLoopStop(mRunLoop); /* Remove the sleep notification port from the application runloop. */ - CFRunLoopRemoveSource (CFRunLoopGetCurrent(), - IONotificationPortGetRunLoopSource (mNotifyPort), - kCFRunLoopCommonModes); + CFRunLoopRemoveSource(CFRunLoopGetCurrent(), + IONotificationPortGetRunLoopSource(mNotifyPort), + kCFRunLoopCommonModes); /* Deregister for system sleep notifications. */ - IODeregisterForSystemPower (&mNotifierObject); + IODeregisterForSystemPower(&mNotifierObject); /* IORegisterForSystemPower implicitly opens the Root Power Domain * IOService so we close it here. */ - IOServiceClose (mRootPort); + IOServiceClose(mRootPort); /* Destroy the notification port allocated by IORegisterForSystemPower */ - IONotificationPortDestroy (mNotifyPort); + IONotificationPortDestroy(mNotifyPort); } -DECLCALLBACK(int) HostPowerServiceDarwin::powerChangeNotificationThread (RTTHREAD /* ThreadSelf */, void *pInstance) +DECLCALLBACK(int) HostPowerServiceDarwin::powerChangeNotificationThread(RTTHREAD /* ThreadSelf */, void *pInstance) { - HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *> (pInstance); + HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *>(pInstance); /* We have to initial set the critical state of the battery, cause we want * not the HostPowerService to inform about that state when a VM starts. @@ -69,19 +69,19 @@ DECLCALLBACK(int) HostPowerServiceDarwin::powerChangeNotificationThread (RTTHREA pPowerObj->checkBatteryCriticalLevel(); /* Register to receive system sleep notifications */ - pPowerObj->mRootPort = IORegisterForSystemPower (pPowerObj, &pPowerObj->mNotifyPort, - HostPowerServiceDarwin::powerChangeNotificationHandler, - &pPowerObj->mNotifierObject); + pPowerObj->mRootPort = IORegisterForSystemPower(pPowerObj, &pPowerObj->mNotifyPort, + HostPowerServiceDarwin::powerChangeNotificationHandler, + &pPowerObj->mNotifierObject); if (pPowerObj->mRootPort == MACH_PORT_NULL) { - LogFlow (("IORegisterForSystemPower failed\n")); + LogFlow(("IORegisterForSystemPower failed\n")); return VERR_NOT_SUPPORTED; } pPowerObj->mRunLoop = CFRunLoopGetCurrent(); /* Add the notification port to the application runloop */ - CFRunLoopAddSource (pPowerObj->mRunLoop, - IONotificationPortGetRunLoopSource (pPowerObj->mNotifyPort), - kCFRunLoopCommonModes); + CFRunLoopAddSource(pPowerObj->mRunLoop, + IONotificationPortGetRunLoopSource(pPowerObj->mNotifyPort), + kCFRunLoopCommonModes); /* Register for all battery change events. The handler will check for low * power events itself. */ @@ -96,10 +96,10 @@ DECLCALLBACK(int) HostPowerServiceDarwin::powerChangeNotificationThread (RTTHREA return VINF_SUCCESS; } -void HostPowerServiceDarwin::powerChangeNotificationHandler (void *pvData, io_service_t /* service */, natural_t messageType, void *pMessageArgument) +void HostPowerServiceDarwin::powerChangeNotificationHandler(void *pvData, io_service_t /* service */, natural_t messageType, void *pMessageArgument) { - HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *> (pvData); - Log (( "powerChangeNotificationHandler: messageType %08lx, arg %08lx\n", (long unsigned int)messageType, (long unsigned int)pMessageArgument)); + HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *>(pvData); + Log(( "powerChangeNotificationHandler: messageType %08lx, arg %08lx\n", (long unsigned int)messageType, (long unsigned int)pMessageArgument)); switch (messageType) { @@ -113,18 +113,18 @@ void HostPowerServiceDarwin::powerChangeNotificationHandler (void *pvData, io_se * you don't acknowledge this power change by calling either * IOAllowPowerChange or IOCancelPowerChange, the system will * wait 30 seconds then go to sleep. */ - IOAllowPowerChange (pPowerObj->mRootPort, reinterpret_cast<long> (pMessageArgument)); + IOAllowPowerChange(pPowerObj->mRootPort, reinterpret_cast<long>(pMessageArgument)); break; } case kIOMessageSystemWillSleep: { /* The system will go for sleep. */ - pPowerObj->notify (HostPowerEvent_Suspend); + pPowerObj->notify(Reason_HostSuspend); /* If you do not call IOAllowPowerChange or IOCancelPowerChange to * acknowledge this message, sleep will be delayed by 30 seconds. * NOTE: If you call IOCancelPowerChange to deny sleep it returns * kIOReturnSuccess, however the system WILL still go to sleep. */ - IOAllowPowerChange (pPowerObj->mRootPort, reinterpret_cast<long> (pMessageArgument)); + IOAllowPowerChange(pPowerObj->mRootPort, reinterpret_cast<long>(pMessageArgument)); break; } case kIOMessageSystemWillPowerOn: @@ -135,7 +135,7 @@ void HostPowerServiceDarwin::powerChangeNotificationHandler (void *pvData, io_se case kIOMessageSystemHasPoweredOn: { /* System has finished the wake up process. */ - pPowerObj->notify (HostPowerEvent_Resume); + pPowerObj->notify(Reason_HostResume); break; } default: @@ -143,11 +143,11 @@ void HostPowerServiceDarwin::powerChangeNotificationHandler (void *pvData, io_se } } -void HostPowerServiceDarwin::lowPowerHandler (void *pvData) +void HostPowerServiceDarwin::lowPowerHandler(void *pvData) { - HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *> (pvData); + HostPowerServiceDarwin *pPowerObj = static_cast<HostPowerServiceDarwin *>(pvData); - /* Following role for sending the BatteryLow event (5% is critical): + /* Following role for sending the BatteryLow event(5% is critical): * - Not at VM start even if the battery is in an critical state already. * - When the power cord is removed so the power supply change from AC to * battery & the battery is in an critical state nothing is triggered. @@ -156,15 +156,15 @@ void HostPowerServiceDarwin::lowPowerHandler (void *pvData) * changed from normal to critical. The state transition from critical to * normal triggers nothing. */ bool fCriticalStateChanged = false; - pPowerObj->checkBatteryCriticalLevel (&fCriticalStateChanged); + pPowerObj->checkBatteryCriticalLevel(&fCriticalStateChanged); if (fCriticalStateChanged) - pPowerObj->notify (HostPowerEvent_BatteryLow); + pPowerObj->notify(Reason_HostBatteryLow); } -void HostPowerServiceDarwin::checkBatteryCriticalLevel (bool *pfCriticalChanged) +void HostPowerServiceDarwin::checkBatteryCriticalLevel(bool *pfCriticalChanged) { CFTypeRef pBlob = IOPSCopyPowerSourcesInfo(); - CFArrayRef pSources = IOPSCopyPowerSourcesList (pBlob); + CFArrayRef pSources = IOPSCopyPowerSourcesList(pBlob); CFDictionaryRef pSource = NULL; const void *psValue; @@ -172,30 +172,30 @@ void HostPowerServiceDarwin::checkBatteryCriticalLevel (bool *pfCriticalChanged) int powerSource = POWER_SOURCE_OUTLET; bool critical = false; - if (CFArrayGetCount (pSources) > 0) + if (CFArrayGetCount(pSources) > 0) { - for (int i = 0; i < CFArrayGetCount (pSources); ++i) + for (int i = 0; i < CFArrayGetCount(pSources); ++i) { - pSource = IOPSGetPowerSourceDescription (pBlob, CFArrayGetValueAtIndex (pSources, i)); + pSource = IOPSGetPowerSourceDescription(pBlob, CFArrayGetValueAtIndex(pSources, i)); /* If the source is empty skip over to the next one. */ if (!pSource) continue; /* Skip all power sources which are currently not present like a * second battery. */ - if (CFDictionaryGetValue (pSource, CFSTR (kIOPSIsPresentKey)) == kCFBooleanFalse) + if (CFDictionaryGetValue(pSource, CFSTR(kIOPSIsPresentKey)) == kCFBooleanFalse) continue; /* Only internal power types are of interest. */ - result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSTransportTypeKey), &psValue); + result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSTransportTypeKey), &psValue); if (result && - CFStringCompare ((CFStringRef)psValue, CFSTR (kIOPSInternalType), 0) == kCFCompareEqualTo) + CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSInternalType), 0) == kCFCompareEqualTo) { /* First check which power source we are connect on. */ - result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSPowerSourceStateKey), &psValue); + result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSPowerSourceStateKey), &psValue); if (result && - CFStringCompare ((CFStringRef)psValue, CFSTR (kIOPSACPowerValue), 0) == kCFCompareEqualTo) + CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSACPowerValue), 0) == kCFCompareEqualTo) powerSource = POWER_SOURCE_OUTLET; else if (result && - CFStringCompare ((CFStringRef)psValue, CFSTR (kIOPSBatteryPowerValue), 0) == kCFCompareEqualTo) + CFStringCompare((CFStringRef)psValue, CFSTR(kIOPSBatteryPowerValue), 0) == kCFCompareEqualTo) powerSource = POWER_SOURCE_BATTERY; int curCapacity = 0; @@ -203,22 +203,22 @@ void HostPowerServiceDarwin::checkBatteryCriticalLevel (bool *pfCriticalChanged) float remCapacity = 0.0f; /* Fetch the current capacity value of the power source */ - result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSCurrentCapacityKey), &psValue); + result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSCurrentCapacityKey), &psValue); if (result) - CFNumberGetValue ((CFNumberRef)psValue, kCFNumberSInt32Type, &curCapacity); + CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &curCapacity); /* Fetch the maximum capacity value of the power source */ - result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSMaxCapacityKey), &psValue); + result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSMaxCapacityKey), &psValue); if (result) - CFNumberGetValue ((CFNumberRef)psValue, kCFNumberSInt32Type, &maxCapacity); + CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &maxCapacity); /* Calculate the remaining capacity in percent */ remCapacity = ((float)curCapacity/(float)maxCapacity * 100.0); /* Check for critical. 5 percent is default. */ int criticalValue = 5; - result = CFDictionaryGetValueIfPresent (pSource, CFSTR (kIOPSDeadWarnLevelKey), &psValue); + result = CFDictionaryGetValueIfPresent(pSource, CFSTR(kIOPSDeadWarnLevelKey), &psValue); if (result) - CFNumberGetValue ((CFNumberRef)psValue, kCFNumberSInt32Type, &criticalValue); + CFNumberGetValue((CFNumberRef)psValue, kCFNumberSInt32Type, &criticalValue); critical = (remCapacity < criticalValue); /* We have to take action only if we are on battery, the * previous state wasn't critical, the state has changed & the @@ -228,14 +228,14 @@ void HostPowerServiceDarwin::checkBatteryCriticalLevel (bool *pfCriticalChanged) mCritical != critical && pfCriticalChanged) *pfCriticalChanged = true; - Log (("checkBatteryCriticalLevel: Remains: %d.%d%% Critical: %d Critical State Changed: %d\n", (int)remCapacity, (int)(remCapacity * 10) % 10, critical, pfCriticalChanged?*pfCriticalChanged:-1)); + Log(("checkBatteryCriticalLevel: Remains: %d.%d%% Critical: %d Critical State Changed: %d\n", (int)remCapacity, (int)(remCapacity * 10) % 10, critical, pfCriticalChanged?*pfCriticalChanged:-1)); } } } /* Save the new state */ mCritical = critical; - CFRelease (pBlob); - CFRelease (pSources); + CFRelease(pBlob); + CFRelease(pSources); } diff --git a/src/VBox/Main/src-server/darwin/NetIf-darwin.cpp b/src/VBox/Main/src-server/darwin/NetIf-darwin.cpp index 99cf52e2..cfde818e 100644 --- a/src/VBox/Main/src-server/darwin/NetIf-darwin.cpp +++ b/src/VBox/Main/src-server/darwin/NetIf-darwin.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2011 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -85,7 +85,7 @@ int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) memcpy(pNew->szName, pEtherNICs->szName, cbNameLen); struct ifreq IfReq; - strcpy(IfReq.ifr_name, pNew->szShortName); + RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pNew->szShortName); if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0) { Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno)); @@ -391,7 +391,7 @@ int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) if (pSdl->sdl_type == IFT_ETHER) { struct ifreq IfReq; - strcpy(IfReq.ifr_name, pNew->szShortName); + RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pNew->szShortName); if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0) { Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno)); @@ -401,7 +401,7 @@ int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) pNew->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN; HostNetworkInterfaceType_T enmType; - if (strncmp("vboxnet", pNew->szName, 7)) + if (strncmp(pNew->szName, RT_STR_TUPLE("vboxnet"))) enmType = HostNetworkInterfaceType_Bridged; else enmType = HostNetworkInterfaceType_HostOnly; @@ -511,7 +511,7 @@ int NetIfGetConfigByName(PNETIFINFO pInfo) pInfo->Uuid = uuid; struct ifreq IfReq; - strcpy(IfReq.ifr_name, pInfo->szShortName); + RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pInfo->szShortName); if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0) { Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno)); @@ -528,4 +528,17 @@ int NetIfGetConfigByName(PNETIFINFO pInfo) return rc; } +/** + * Retrieve the physical link speed in megabits per second. If the interface is + * not up or otherwise unavailable the zero speed is returned. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param puMbits Where to store the link speed. + */ +int NetIfGetLinkSpeed(const char * /*pcszIfName*/, uint32_t * /*puMbits*/) +{ + return VERR_NOT_IMPLEMENTED; +} #endif diff --git a/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp b/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp index e3daa585..ea040bd0 100644 --- a/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp +++ b/src/VBox/Main/src-server/darwin/PerformanceDarwin.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -24,7 +24,9 @@ #include <sys/errno.h> #include <iprt/err.h> #include <iprt/log.h> +#include <iprt/mp.h> #include <iprt/param.h> +#include <iprt/system.h> #include "Performance.h" /* The following declarations are missing in 10.4.x SDK */ @@ -63,7 +65,8 @@ public: virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total); virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used); private: - ULONG totalRAM; + ULONG totalRAM; + uint32_t nCpus; }; CollectorHAL *createHAL() @@ -73,19 +76,19 @@ CollectorHAL *createHAL() CollectorDarwin::CollectorDarwin() { - uint64_t hostMemory; - int mib[2]; - size_t size; - - mib[0] = CTL_HW; - mib[1] = HW_MEMSIZE; - - size = sizeof(hostMemory); - if (sysctl(mib, 2, &hostMemory, &size, NULL, 0) == -1) { - Log(("sysctl() -> %s", strerror(errno))); - hostMemory = 0; + uint64_t cb; + int rc = RTSystemQueryTotalRam(&cb); + if (RT_FAILURE(rc)) + totalRAM = 0; + else + totalRAM = (ULONG)(cb / 1024); + nCpus = RTMpGetOnlineCount(); + Assert(nCpus); + if (nCpus == 0) + { + /* It is rather unsual to have no CPUs, but the show must go on. */ + nCpus = 1; } - totalRAM = (ULONG)(hostMemory / 1024); } int CollectorDarwin::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle) @@ -112,23 +115,16 @@ int CollectorDarwin::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_ int CollectorDarwin::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) { - kern_return_t krc; - mach_msg_type_number_t count; - vm_statistics_data_t info; - - count = HOST_VM_INFO_COUNT; - - krc = host_statistics(mach_host_self(), HOST_VM_INFO, (host_info_t)&info, &count); - if (krc != KERN_SUCCESS) + AssertReturn(totalRAM, VERR_INTERNAL_ERROR); + uint64_t cb; + int rc = RTSystemQueryAvailableRam(&cb); + if (RT_SUCCESS(rc)) { - Log(("host_statistics() -> %s", mach_error_string(krc))); - return RTErrConvertFromDarwinKern(krc); + *total = totalRAM; + *available = cb / 1024; + *used = *total - *available; } - - *total = totalRAM; - *available = info.free_count * (PAGE_SIZE / 1024); - *used = *total - *available; - return VINF_SUCCESS; + return rc; } static int getProcessInfo(RTPROCESS process, struct proc_taskinfo *tinfo) @@ -156,8 +152,12 @@ int CollectorDarwin::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uin int rc = getProcessInfo(process, &tinfo); if (RT_SUCCESS(rc)) { - *user = tinfo.pti_total_user; - *kernel = tinfo.pti_total_system; + /* + * Adjust user and kernel values so 100% is when ALL cores are fully + * utilized (see @bugref{6345}). + */ + *user = tinfo.pti_total_user / nCpus; + *kernel = tinfo.pti_total_system / nCpus; *total = mach_absolute_time(); } return rc; @@ -175,10 +175,5 @@ int CollectorDarwin::getProcessMemoryUsage(RTPROCESS process, ULONG *used) return rc; } -int getDiskListByFs(const char *name, DiskList& list) -{ - return VERR_NOT_IMPLEMENTED; -} - } diff --git a/src/VBox/Main/src-server/darwin/iokit.cpp b/src/VBox/Main/src-server/darwin/iokit.cpp index 0045e7c0..69c572f2 100644 --- a/src/VBox/Main/src-server/darwin/iokit.cpp +++ b/src/VBox/Main/src-server/darwin/iokit.cpp @@ -8,7 +8,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -373,7 +373,7 @@ static void darwinDumpDictCallback(const void *pvKey, const void *pvValue, void double rd; CFIndex iCF; } u; - memset(&u, 0, sizeof(u)); + RT_ZERO(u); CFNumberType NumType = CFNumberGetType((CFNumberRef)pvValue); if (CFNumberGetValue((CFNumberRef)pvValue, NumType, &u)) { @@ -1354,7 +1354,7 @@ PDARWINDVD DarwinGetDVDDrives(void) if (*pszVendor && *pszProduct) RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", pszVendor, pszProduct, i); else - RTStrPrintf(szName, sizeof(szName), "%s %s (#%u)", *pszVendor ? pszVendor : pszProduct, i); + RTStrPrintf(szName, sizeof(szName), "%s (#%u)", *pszVendor ? pszVendor : pszProduct, i); break; } } diff --git a/src/VBox/Main/src-server/darwin/iokit.h b/src/VBox/Main/src-server/darwin/iokit.h index 8cb80a52..ff81b3c7 100644 --- a/src/VBox/Main/src-server/darwin/iokit.h +++ b/src/VBox/Main/src-server/darwin/iokit.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp index b654f5a6..c65e47c3 100644 --- a/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp +++ b/src/VBox/Main/src-server/freebsd/HostHardwareFreeBSD.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -31,7 +31,6 @@ #include <iprt/mem.h> #include <iprt/param.h> #include <iprt/path.h> -#include <iprt/thread.h> /* for RTThreadSleep() */ #include <iprt/string.h> #ifdef RT_OS_FREEBSD @@ -172,8 +171,8 @@ static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess) struct dev_match_pattern DeviceMatchPattern; struct dev_match_result *paMatches = NULL; - memset(&DeviceCCB, 0, sizeof(union ccb)); - memset(&DeviceMatchPattern, 0, sizeof(struct device_match_pattern)); + RT_ZERO(DeviceCCB); + RT_ZERO(DeviceMatchPattern); /* We want to get all devices. */ DeviceCCB.ccb_h.func_code = XPT_DEV_MATCH; @@ -236,9 +235,9 @@ static int getDVDInfoFromCAM(DriveInfoList *pList, bool *pfSuccess) struct periph_match_result *pPeriphResult = NULL; unsigned iPeriphMatch = 0; - memset(&PeriphCCB, 0, sizeof(union ccb)); - memset(&PeriphMatchPattern, 0, sizeof(struct dev_match_pattern)); - memset(aPeriphMatches, 0, sizeof(aPeriphMatches)); + RT_ZERO(PeriphCCB); + RT_ZERO(PeriphMatchPattern); + RT_ZERO(aPeriphMatches); /* This time we only want the specific nodes for the device. */ PeriphCCB.ccb_h.func_code = XPT_DEV_MATCH; diff --git a/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp b/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp index 7066dab9..049aaaf6 100644 --- a/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp +++ b/src/VBox/Main/src-server/freebsd/NetIf-freebsd.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -43,6 +43,7 @@ #include <net/if_dl.h> #include <netinet/in.h> +#include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <errno.h> @@ -271,7 +272,7 @@ int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) if (pSdl->sdl_type == IFT_ETHER || pSdl->sdl_type == IFT_L2VLAN) { struct ifreq IfReq; - strcpy(IfReq.ifr_name, pNew->szShortName); + RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pNew->szShortName); if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0) { Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno)); @@ -281,7 +282,7 @@ int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) pNew->enmStatus = (IfReq.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN; HostNetworkInterfaceType_T enmType; - if (strncmp("vboxnet", pNew->szName, 7)) + if (strncmp(pNew->szName, RT_STR_TUPLE("vboxnet"))) enmType = HostNetworkInterfaceType_Bridged; else enmType = HostNetworkInterfaceType_HostOnly; @@ -388,7 +389,7 @@ int NetIfGetConfigByName(PNETIFINFO pInfo) pInfo->Uuid = uuid; struct ifreq IfReq; - strcpy(IfReq.ifr_name, pInfo->szShortName); + RTStrCopy(IfReq.ifr_name, sizeof(IfReq.ifr_name), pInfo->szShortName); if (ioctl(sock, SIOCGIFFLAGS, &IfReq) < 0) { Log(("NetIfList: ioctl(SIOCGIFFLAGS) -> %d\n", errno)); @@ -405,3 +406,16 @@ int NetIfGetConfigByName(PNETIFINFO pInfo) return rc; } +/** + * Retrieve the physical link speed in megabits per second. If the interface is + * not up or otherwise unavailable the zero speed is returned. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param puMbits Where to store the link speed. + */ +int NetIfGetLinkSpeed(const char * /*pcszIfName*/, uint32_t * /*puMbits*/) +{ + return VERR_NOT_IMPLEMENTED; +} diff --git a/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp index 0b817842..6770c75b 100644 --- a/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp +++ b/src/VBox/Main/src-server/freebsd/PerformanceFreeBSD.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008-2009 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -39,7 +39,7 @@ CollectorHAL *createHAL() int CollectorFreeBSD::getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorFreeBSD::getHostCpuMHz(ULONG *mhz) @@ -101,12 +101,17 @@ int CollectorFreeBSD::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *avail int CollectorFreeBSD::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorFreeBSD::getProcessMemoryUsage(RTPROCESS process, ULONG *used) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; +} + +int getDiskListByFs(const char *name, DiskList& list) +{ + return VERR_NOT_IMPLEMENTED; } } /* namespace pm */ diff --git a/src/VBox/Main/src-server/freebsd/USBProxyServiceFreeBSD.cpp b/src/VBox/Main/src-server/freebsd/USBProxyServiceFreeBSD.cpp index ef8856a3..98535312 100644 --- a/src/VBox/Main/src-server/freebsd/USBProxyServiceFreeBSD.cpp +++ b/src/VBox/Main/src-server/freebsd/USBProxyServiceFreeBSD.cpp @@ -266,7 +266,7 @@ PUSBDEVICE USBProxyServiceFreeBSD::getDevices(void) LogFlowFunc((": %s opened successfully\n", pszDevicePath)); struct usb_device_info UsbDevInfo; - memset(&UsbDevInfo, 0, sizeof(struct usb_device_info)); + RT_ZERO(UsbDevInfo); rc = ioctl(FileUsb, USB_GET_DEVICEINFO, &UsbDevInfo); if (rc < 0) diff --git a/src/VBox/Main/src-server/generic/AutostartDb-generic.cpp b/src/VBox/Main/src-server/generic/AutostartDb-generic.cpp index 923e75fe..07aafe23 100644 --- a/src/VBox/Main/src-server/generic/AutostartDb-generic.cpp +++ b/src/VBox/Main/src-server/generic/AutostartDb-generic.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2010 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -78,7 +78,7 @@ int AutostartDb::autostartModifyDb(bool fAutostart, bool fAddVM) char abBuf[16 + 1]; /* trailing \0 */ uint32_t cAutostartVms = 0; - memset(abBuf, 0, sizeof(abBuf)); + RT_ZERO(abBuf); /* Check if the file was just created. */ if (cbFile) diff --git a/src/VBox/Main/src-server/generic/NetIf-generic.cpp b/src/VBox/Main/src-server/generic/NetIf-generic.cpp index fdf72d6f..2f99585b 100644 --- a/src/VBox/Main/src-server/generic/NetIf-generic.cpp +++ b/src/VBox/Main/src-server/generic/NetIf-generic.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2009-2010 Oracle Corporation + * Copyright (C) 2009-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -21,6 +21,15 @@ #include <iprt/env.h> #include <iprt/path.h> #include <iprt/param.h> +#include <sys/ioctl.h> +#include <netinet/in.h> +#include <net/if.h> +#include <errno.h> +#include <unistd.h> + +#if defined(RT_OS_SOLARIS) +# include <sys/sockio.h> +#endif #if defined(RT_OS_LINUX) || defined(RT_OS_DARWIN) # include <cstdio> @@ -218,6 +227,11 @@ int NetIfCreateHostOnlyNetworkInterface(VirtualBox *pVBox, char szBuf[128]; /* We are not interested in long error messages. */ if (fgets(szBuf, sizeof(szBuf), fp)) { + /* Remove trailing new line characters. */ + char *pLast = szBuf + strlen(szBuf) - 1; + if (pLast >= szBuf && *pLast == '\n') + *pLast = 0; + if (!strncmp(VBOXNETADPCTL_NAME ":", szBuf, sizeof(VBOXNETADPCTL_NAME))) { progress->notifyComplete(E_FAIL, @@ -227,9 +241,6 @@ int NetIfCreateHostOnlyNetworkInterface(VirtualBox *pVBox, pclose(fp); return E_FAIL; } - char *pLast = szBuf + strlen(szBuf) - 1; - if (pLast >= szBuf && *pLast == '\n') - *pLast = 0; size_t cbNameLen = strlen(szBuf) + 1; PNETIFINFO pInfo = (PNETIFINFO)RTMemAllocZ(RT_OFFSETOF(NETIFINFO, szName[cbNameLen])); @@ -307,7 +318,7 @@ int NetIfRemoveHostOnlyNetworkInterface(VirtualBox *pVBox, IN_GUID aId, ComPtr<IHost> host; int rc = VINF_SUCCESS; HRESULT hr = pVBox->COMGETTER(Host)(host.asOutParam()); - if(SUCCEEDED(hr)) + if (SUCCEEDED(hr)) { Bstr ifname; ComPtr<IHostNetworkInterface> iface; @@ -320,7 +331,7 @@ int NetIfRemoveHostOnlyNetworkInterface(VirtualBox *pVBox, IN_GUID aId, rc = progress->init(pVBox, host, Bstr("Removing host network interface").raw(), FALSE /* aCancelable */); - if(SUCCEEDED(rc)) + if (SUCCEEDED(rc)) { progress.queryInterfaceTo(aProgress); rc = NetIfAdpCtl(Utf8Str(ifname).c_str(), "remove", NULL, NULL); @@ -352,8 +363,34 @@ int NetIfGetConfig(HostNetworkInterface * /* pIf */, NETIFINFO *) return VERR_NOT_IMPLEMENTED; } -int NetIfDhcpRediscover(VirtualBox * /* pVbox */, HostNetworkInterface * /* pIf */) +int NetIfDhcpRediscover(VirtualBox * /* pVBox */, HostNetworkInterface * /* pIf */) { return VERR_NOT_IMPLEMENTED; } +/** + * Obtain the current state of the interface. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param penmState Where to store the retrieved state. + */ +int NetIfGetState(const char *pcszIfName, NETIFSTATUS *penmState) +{ + int sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); + if (sock < 0) + return VERR_OUT_OF_RESOURCES; + struct ifreq Req; + RT_ZERO(Req); + RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pcszIfName); + if (ioctl(sock, SIOCGIFFLAGS, &Req) < 0) + { + Log(("NetIfGetState: ioctl(SIOCGIFFLAGS) -> %d\n", errno)); + *penmState = NETIF_S_UNKNOWN; + } + else + *penmState = (Req.ifr_flags & IFF_UP) ? NETIF_S_UP : NETIF_S_DOWN; + close(sock); + return VINF_SUCCESS; +} diff --git a/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp b/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp new file mode 100644 index 00000000..89de18df --- /dev/null +++ b/src/VBox/Main/src-server/linux/HostDnsServiceLinux.cpp @@ -0,0 +1,233 @@ +/* $Id: HostDnsServiceLinux.cpp $ */ +/** @file + * Linux specific DNS information fetching. + */ + +/* + * Copyright (C) 2013 Oracle Corporation + * + * This file is part of VirtualBox Open Source Edition (OSE), as + * available from http://www.virtualbox.org. This file is free software; + * you can redistribute it and/or modify it under the terms of the GNU + * General Public License (GPL) as published by the Free Software + * Foundation, in version 2 as it comes in the "COPYING" file of the + * VirtualBox OSE distribution. VirtualBox OSE is distributed in the + * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind. + */ + +#include <iprt/assert.h> +#include <iprt/err.h> +#include <iprt/initterm.h> +#include <iprt/file.h> +#include <iprt/log.h> +#include <iprt/stream.h> +#include <iprt/string.h> +#include <iprt/semaphore.h> +#include <iprt/thread.h> + +#include <errno.h> +#include <poll.h> +#include <string.h> +#include <unistd.h> + +#include <fcntl.h> + +#include <linux/limits.h> + +#include <sys/inotify.h> +#include <sys/types.h> +#include <sys/socket.h> + +#include <string> +#include <vector> +#include "../HostDnsService.h" + + +static int g_DnsMonitorStop[2]; + +static const std::string g_EtcFolder = "/etc"; +static const std::string g_ResolvConf = "resolv.conf"; +static const std::string g_ResolvConfFullPath = "/etc/resolv.conf"; + +class FileDescriptor +{ + public: + FileDescriptor(int d = -1):fd(d){} + + virtual ~FileDescriptor() { + if (fd != -1) + close(fd); + } + + int fileDescriptor() const {return fd;} + + protected: + int fd; +}; + + +class AutoNotify:public FileDescriptor +{ + public: + AutoNotify() + { + FileDescriptor::fd = inotify_init(); + AssertReturnVoid(FileDescriptor::fd != -1); + } +}; + +struct InotifyEventWithName +{ + struct inotify_event e; + char name[NAME_MAX]; +}; + +HostDnsServiceLinux::~HostDnsServiceLinux() +{ + monitorThreadShutdown(); +} + + +int HostDnsServiceLinux::monitorWorker() +{ + + AutoNotify a; + + int rc = socketpair(AF_LOCAL, SOCK_DGRAM, 0, g_DnsMonitorStop); + AssertMsgReturn(rc == 0, ("socketpair: failed (%d: %s)\n", errno, strerror(errno)), E_FAIL); + + FileDescriptor stopper0(g_DnsMonitorStop[0]); + FileDescriptor stopper1(g_DnsMonitorStop[1]); + + pollfd polls[2]; + RT_ZERO(polls); + + polls[0].fd = a.fileDescriptor(); + polls[0].events = POLLIN; + + polls[1].fd = g_DnsMonitorStop[1]; + polls[1].events = POLLIN; + + monitorThreadInitializationDone(); + + int wd[2]; + wd[0] = wd[1] = -1; + /* inotify inialization */ + wd[0] = inotify_add_watch(a.fileDescriptor(), + g_ResolvConfFullPath.c_str(), IN_CLOSE_WRITE|IN_DELETE_SELF); + + /** + * If /etc/resolv.conf exists we want to listen for movements: because + * # mv /etc/resolv.conf ... + * won't arm IN_DELETE_SELF on wd[0] instead it will fire IN_MOVE_FROM on wd[1]. + * + * Because on some distributions /etc/resolv.conf is link, wd[0] can't detect deletion, + * it's recognizible on directory level (wd[1]) only. + */ + wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), + wd[0] == -1 ? IN_MOVED_TO|IN_CREATE : IN_MOVED_FROM|IN_DELETE); + + struct InotifyEventWithName combo; + while(true) + { + rc = poll(polls, 2, -1); + if (rc == -1) + continue; + + AssertMsgReturn( ((polls[0].revents & (POLLERR|POLLNVAL)) == 0) + && ((polls[1].revents & (POLLERR|POLLNVAL)) == 0), + ("Debug Me"), VERR_INTERNAL_ERROR); + + if (polls[1].revents & POLLIN) + return VINF_SUCCESS; /* time to shutdown */ + + if (polls[0].revents & POLLIN) + { + RT_ZERO(combo); + ssize_t r = read(polls[0].fd, static_cast<void *>(&combo), sizeof(combo)); + + if (combo.e.wd == wd[0]) + { + if (combo.e.mask & IN_CLOSE_WRITE) + { + readResolvConf(); + /* notifyAll() takes required locks */ + notifyAll(); + } + else if (combo.e.mask & IN_DELETE_SELF) + { + inotify_rm_watch(a.fileDescriptor(), wd[0]); /* removes file watcher */ + inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), + IN_MOVED_TO|IN_CREATE); /* alter folder watcher */ + } + else if (combo.e.mask & IN_IGNORED) + { + wd[0] = -1; /* we want receive any events on this watch */ + } + else + { + /** + * It shouldn't happen, in release we will just ignore in debug + * we will have to chance to look at into inotify_event + */ + AssertMsgFailed(("Debug Me!!!")); + } + } + else if (combo.e.wd == wd[1]) + { + if ( combo.e.mask & IN_MOVED_FROM + || combo.e.mask & IN_DELETE) + { + if (g_ResolvConf == combo.e.name) + { + /** + * Our file has been moved so we should change watching mode. + */ + inotify_rm_watch(a.fileDescriptor(), wd[0]); + wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), + IN_MOVED_TO|IN_CREATE); + AssertMsg(wd[1] != -1, + ("It shouldn't happen, further investigation is needed\n")); + } + } + else + { + AssertMsg(combo.e.mask & (IN_MOVED_TO|IN_CREATE), + ("%RX32 event isn't expected, we are waiting for IN_MOVED|IN_CREATE\n", + combo.e.mask)); + if (g_ResolvConf == combo.e.name) + { + AssertMsg(wd[0] == -1, ("We haven't removed file watcher first\n")); + + /* alter folder watcher*/ + wd[1] = inotify_add_watch(a.fileDescriptor(), g_EtcFolder.c_str(), + IN_MOVED_FROM|IN_DELETE); + AssertMsg(wd[1] != -1, ("It shouldn't happen.\n")); + + wd[0] = inotify_add_watch(a.fileDescriptor(), + g_ResolvConfFullPath.c_str(), + IN_CLOSE_WRITE | IN_DELETE_SELF); + AssertMsg(wd[0] != -1, ("Adding watcher to file (%s) has been failed!\n", + g_ResolvConfFullPath.c_str())); + + /* Notify our listeners */ + readResolvConf(); + notifyAll(); + + } + } + } + else + { + /* It shouldn't happen */ + AssertMsgFailed(("Shouldn't happen! Please debug me!")); + } + } + } +} + + +void HostDnsServiceLinux::monitorThreadShutdown() +{ + send(g_DnsMonitorStop[0], "", 1, 0); +} diff --git a/src/VBox/Main/src-server/linux/HostHardwareLinux.cpp b/src/VBox/Main/src-server/linux/HostHardwareLinux.cpp index 2dc722cc..1ff26d81 100644 --- a/src/VBox/Main/src-server/linux/HostHardwareLinux.cpp +++ b/src/VBox/Main/src-server/linux/HostHardwareLinux.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2008-2010 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -37,7 +37,6 @@ #include <iprt/param.h> #include <iprt/path.h> #include <iprt/string.h> -#include <iprt/thread.h> /* for RTThreadSleep() */ #include <linux/cdrom.h> #include <linux/fd.h> @@ -1282,7 +1281,7 @@ hotplugInotifyImpl::hotplugInotifyImpl(const char *pcszDevicesRoot) : break; if (RT_FAILURE(rc = pipeCreateSimple(&mhWakeupPipeR, &mhWakeupPipeW))) break; - } while(0); + } while (0); mStatus = rc; if (RT_FAILURE(rc)) term(); diff --git a/src/VBox/Main/src-server/linux/NetIf-linux.cpp b/src/VBox/Main/src-server/linux/NetIf-linux.cpp index 6fe799cf..2384cc52 100644 --- a/src/VBox/Main/src-server/linux/NetIf-linux.cpp +++ b/src/VBox/Main/src-server/linux/NetIf-linux.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -37,14 +37,23 @@ #include "netif.h" #include "Logging.h" +/** + * Obtain the name of the interface used for default routing. + * + * NOTE: There is a copy in Devices/Network/testcase/tstIntNet-1.cpp. + * + * @returns VBox status code. + * + * @param pszName The buffer of IFNAMSIZ+1 length where to put the name. + */ static int getDefaultIfaceName(char *pszName) { FILE *fp = fopen("/proc/net/route", "r"); char szBuf[1024]; char szIfName[17]; - char szAddr[129]; - char szGateway[129]; - char szMask[129]; + uint32_t uAddr; + uint32_t uGateway; + uint32_t uMask; int iTmp; unsigned uFlags; @@ -52,13 +61,13 @@ static int getDefaultIfaceName(char *pszName) { while (fgets(szBuf, sizeof(szBuf)-1, fp)) { - int n = sscanf(szBuf, "%16s %128s %128s %X %d %d %d %128s %d %d %d\n", - szIfName, szAddr, szGateway, &uFlags, &iTmp, &iTmp, &iTmp, - szMask, &iTmp, &iTmp, &iTmp); + int n = sscanf(szBuf, "%16s %x %x %x %d %d %d %x %d %d %d\n", + szIfName, &uAddr, &uGateway, &uFlags, &iTmp, &iTmp, &iTmp, + &uMask, &iTmp, &iTmp, &iTmp); if (n < 10 || !(uFlags & RTF_UP)) continue; - if (strcmp(szAddr, "00000000") == 0 && strcmp(szMask, "00000000") == 0) + if (uAddr == 0 && uMask == 0) { fclose(fp); strncpy(pszName, szIfName, 16); @@ -71,14 +80,55 @@ static int getDefaultIfaceName(char *pszName) return VERR_INTERNAL_ERROR; } +static uint32_t getInterfaceSpeed(const char *pszName) +{ + /* + * I wish I could do simple ioctl here, but older kernels require root + * privileges for any ethtool commands. + */ + char szBuf[256]; + uint32_t uSpeed = 0; + /* First, we try to retrieve the speed via sysfs. */ + RTStrPrintf(szBuf, sizeof(szBuf), "/sys/class/net/%s/speed", pszName); + FILE *fp = fopen(szBuf, "r"); + if (fp) + { + if (fscanf(fp, "%u", &uSpeed) != 1) + uSpeed = 0; + fclose(fp); + } + if (uSpeed == 10) + { + /* Check the cable is plugged in at all */ + unsigned uCarrier = 0; + RTStrPrintf(szBuf, sizeof(szBuf), "/sys/class/net/%s/carrier", pszName); + fp = fopen(szBuf, "r"); + if (fp) + { + if (fscanf(fp, "%u", &uCarrier) != 1 || uCarrier == 0) + uSpeed = 0; + fclose(fp); + } + } + + if (uSpeed == 0) + { + /* Failed to get speed via sysfs, go to plan B. */ + int rc = NetIfAdpCtlOut(pszName, "speed", szBuf, sizeof(szBuf)); + if (RT_SUCCESS(rc)) + uSpeed = RTStrToUInt32(szBuf); + } + return uSpeed; +} + static int getInterfaceInfo(int iSocket, const char *pszName, PNETIFINFO pInfo) { // Zeroing out pInfo is a bad idea as it should contain both short and long names at // this point. So make sure the structure is cleared by the caller if necessary! // memset(pInfo, 0, sizeof(*pInfo)); struct ifreq Req; - memset(&Req, 0, sizeof(Req)); - strncpy(Req.ifr_name, pszName, sizeof(Req.ifr_name) - 1); + RT_ZERO(Req); + RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pszName); if (ioctl(iSocket, SIOCGIFHWADDR, &Req) >= 0) { switch (Req.ifr_hwaddr.sa_family) @@ -122,7 +172,7 @@ static int getInterfaceInfo(int iSocket, const char *pszName, PNETIFINFO pInfo) char szName[30]; for (;;) { - memset(szName, 0, sizeof(szName)); + RT_ZERO(szName); int n = fscanf(fp, "%08x%08x%08x%08x" " %02x %02x %02x %02x %20s\n", @@ -152,31 +202,10 @@ static int getInterfaceInfo(int iSocket, const char *pszName, PNETIFINFO pInfo) * Don't even try to get speed for non-Ethernet interfaces, it only * produces errors. */ - pInfo->uSpeedMbits = 0; if (pInfo->enmMediumType == NETIF_T_ETHERNET) - { - /* - * I wish I could do simple ioctl here, but older kernels require root - * privileges for any ethtool commands. - */ - char szBuf[256]; - /* First, we try to retrieve the speed via sysfs. */ - RTStrPrintf(szBuf, sizeof(szBuf), "/sys/class/net/%s/speed", pszName); - fp = fopen(szBuf, "r"); - if (fp) - { - if (fscanf(fp, "%u", &pInfo->uSpeedMbits) != 1) - pInfo->uSpeedMbits = 0; - fclose(fp); - } - if (pInfo->uSpeedMbits == 0) - { - /* Failed to get speed via sysfs, go to plan B. */ - int rc = NetIfAdpCtlOut(pszName, "speed", szBuf, sizeof(szBuf)); - if (RT_SUCCESS(rc)) - pInfo->uSpeedMbits = RTStrToUInt32(szBuf); - } - } + pInfo->uSpeedMbits = getInterfaceSpeed(pszName); + else + pInfo->uSpeedMbits = 0; } return VINF_SUCCESS; } @@ -216,7 +245,7 @@ int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) IfObj.createObject(); HostNetworkInterfaceType_T enmType; - if (strncmp("vboxnet", pszName, 7)) + if (strncmp(pszName, RT_STR_TUPLE("vboxnet"))) enmType = HostNetworkInterfaceType_Bridged; else enmType = HostNetworkInterfaceType_HostOnly; @@ -251,3 +280,35 @@ int NetIfGetConfigByName(PNETIFINFO pInfo) close(sock); return rc; } + +/** + * Retrieve the physical link speed in megabits per second. If the interface is + * not up or otherwise unavailable the zero speed is returned. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param puMbits Where to store the link speed. + */ +int NetIfGetLinkSpeed(const char *pcszIfName, uint32_t *puMbits) +{ + int sock = socket(AF_INET, SOCK_DGRAM, 0); + if (sock < 0) + return VERR_OUT_OF_RESOURCES; + struct ifreq Req; + RT_ZERO(Req); + RTStrCopy(Req.ifr_name, sizeof(Req.ifr_name), pcszIfName); + if (ioctl(sock, SIOCGIFHWADDR, &Req) >= 0) + { + if (ioctl(sock, SIOCGIFFLAGS, &Req) >= 0) + if (Req.ifr_flags & IFF_UP) + { + close(sock); + *puMbits = getInterfaceSpeed(pcszIfName); + return VINF_SUCCESS; + } + } + close(sock); + *puMbits = 0; + return VWRN_NOT_FOUND; +} diff --git a/src/VBox/Main/src-server/linux/PerformanceLinux.cpp b/src/VBox/Main/src-server/linux/PerformanceLinux.cpp index 4e165ee3..77ccb9b8 100644 --- a/src/VBox/Main/src-server/linux/PerformanceLinux.cpp +++ b/src/VBox/Main/src-server/linux/PerformanceLinux.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2008-2011 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -27,8 +27,11 @@ #include <iprt/ctype.h> #include <iprt/err.h> #include <iprt/param.h> +#include <iprt/path.h> #include <iprt/string.h> +#include <iprt/system.h> #include <iprt/mp.h> +#include <iprt/linux/sysfs.h> #include <map> #include <vector> @@ -36,6 +39,8 @@ #include "Logging.h" #include "Performance.h" +#define VBOXVOLINFO_NAME "VBoxVolInfo" + namespace pm { class CollectorLinux : public CollectorHAL @@ -45,15 +50,23 @@ public: virtual int preCollect(const CollectorHints& hints, uint64_t /* iTick */); virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available); virtual int getHostFilesystemUsage(const char *name, ULONG *total, ULONG *used, ULONG *available); + virtual int getHostDiskSize(const char *name, uint64_t *size); virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used); virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle); virtual int getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx); virtual int getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms); virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total); + + virtual int getDiskListByFs(const char *name, DiskList& listUsage, DiskList& listLoad); private: virtual int _getRawHostCpuLoad(); int getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uint64_t *cpuKernel, ULONG *memPagesUsed); + void getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits); + void addVolumeDependencies(const char *pcszVolume, DiskList& listDisks); + void addRaidDisks(const char *pcszDevice, DiskList& listDisks); + char *trimTrailingDigits(char *pszName); + char *trimNewline(char *pszName); struct VMProcessStats { @@ -68,6 +81,7 @@ private: uint64_t mUser, mKernel, mIdle; uint64_t mSingleUser, mSingleKernel, mSingleIdle; uint32_t mHZ; + ULONG totalRAM; }; CollectorHAL *createHAL() @@ -88,6 +102,13 @@ CollectorLinux::CollectorLinux() else mHZ = hz; LogFlowThisFunc(("mHZ=%u\n", mHZ)); + + uint64_t cb; + int rc = RTSystemQueryTotalRam(&cb); + if (RT_FAILURE(rc)) + totalRAM = 0; + else + totalRAM = (ULONG)(cb / 1024); } int CollectorLinux::preCollect(const CollectorHints& hints, uint64_t /* iTick */) @@ -190,35 +211,21 @@ int CollectorLinux::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint int CollectorLinux::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) { - int rc = VINF_SUCCESS; - ULONG buffers, cached; - FILE *f = fopen("/proc/meminfo", "r"); - - if (f) + AssertReturn(totalRAM, VERR_INTERNAL_ERROR); + uint64_t cb; + int rc = RTSystemQueryAvailableRam(&cb); + if (RT_SUCCESS(rc)) { - int processed = fscanf(f, "MemTotal: %u kB\n", total); - processed += fscanf(f, "MemFree: %u kB\n", available); - processed += fscanf(f, "Buffers: %u kB\n", &buffers); - processed += fscanf(f, "Cached: %u kB\n", &cached); - if (processed == 4) - { - *available += buffers + cached; - *used = *total - *available; - } - else - rc = VERR_FILE_IO_ERROR; - fclose(f); + *total = totalRAM; + *available = cb / 1024; + *used = *total - *available; } - else - rc = VERR_ACCESS_DENIED; - return rc; } int CollectorLinux::getHostFilesystemUsage(const char *path, ULONG *total, ULONG *used, ULONG *available) { struct statvfs stats; - const unsigned _MB = 1024 * 1024; if (statvfs(path, &stats) == -1) { @@ -226,13 +233,35 @@ int CollectorLinux::getHostFilesystemUsage(const char *path, ULONG *total, ULONG return VERR_ACCESS_DENIED; } uint64_t cbBlock = stats.f_frsize ? stats.f_frsize : stats.f_bsize; - *total = (ULONG)(cbBlock * stats.f_blocks / _MB); - *used = (ULONG)(cbBlock * (stats.f_blocks - stats.f_bfree) / _MB); - *available = (ULONG)(cbBlock * stats.f_bavail / _MB); + *total = (ULONG)(cbBlock * stats.f_blocks / _1M); + *used = (ULONG)(cbBlock * (stats.f_blocks - stats.f_bfree) / _1M); + *available = (ULONG)(cbBlock * stats.f_bavail / _1M); return VINF_SUCCESS; } +int CollectorLinux::getHostDiskSize(const char *pszFile, uint64_t *size) +{ + char *pszPath = NULL; + + RTStrAPrintf(&pszPath, "/sys/block/%s/size", pszFile); + Assert(pszPath); + + int rc = VINF_SUCCESS; + if (!RTLinuxSysFsExists(pszPath)) + rc = VERR_FILE_NOT_FOUND; + else + { + int64_t cSize = RTLinuxSysFsReadIntFile(0, pszPath); + if (cSize < 0) + rc = VERR_ACCESS_DENIED; + else + *size = cSize * 512; + } + RTStrFree(pszPath); + return rc; +} + int CollectorLinux::getProcessMemoryUsage(RTPROCESS process, ULONG *used) { VMProcessMap::const_iterator it = mProcessStats.find(process); @@ -261,9 +290,8 @@ int CollectorLinux::getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uin char buf[80]; /* @todo: this should be tied to max allowed proc name. */ RTStrAPrintf(&pszName, "/proc/%d/stat", process); - //printf("Opening %s...\n", pszName); FILE *f = fopen(pszName, "r"); - RTMemFree(pszName); + RTStrFree(pszName); if (f) { @@ -288,42 +316,35 @@ int CollectorLinux::getRawProcessStats(RTPROCESS process, uint64_t *cpuUser, uin return rc; } -int CollectorLinux::getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx) +int CollectorLinux::getRawHostNetworkLoad(const char *pszFile, uint64_t *rx, uint64_t *tx) { - int rc = VINF_SUCCESS; char szIfName[/*IFNAMSIZ*/ 16 + 36]; - long long unsigned int u64Rx, u64Tx; - RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/rx_bytes", name); - FILE *f = fopen(szIfName, "r"); - if (f) - { - if (fscanf(f, "%llu", &u64Rx) == 1) - *rx = u64Rx; - else - rc = VERR_FILE_IO_ERROR; - fclose(f); - RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/tx_bytes", name); - f = fopen(szIfName, "r"); - if (f) - { - if (fscanf(f, "%llu", &u64Tx) == 1) - *tx = u64Tx; - else - rc = VERR_FILE_IO_ERROR; - fclose(f); - } - else - rc = VERR_ACCESS_DENIED; - } - else - rc = VERR_ACCESS_DENIED; + RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/rx_bytes", pszFile); + if (!RTLinuxSysFsExists(szIfName)) + return VERR_FILE_NOT_FOUND; - return rc; + int64_t cSize = RTLinuxSysFsReadIntFile(0, szIfName); + if (cSize < 0) + return VERR_ACCESS_DENIED; + + *rx = cSize; + + RTStrPrintf(szIfName, sizeof(szIfName), "/sys/class/net/%s/statistics/tx_bytes", pszFile); + if (!RTLinuxSysFsExists(szIfName)) + return VERR_FILE_NOT_FOUND; + + cSize = RTLinuxSysFsReadIntFile(0, szIfName); + if (cSize < 0) + return VERR_ACCESS_DENIED; + + *tx = cSize; + return VINF_SUCCESS; } int CollectorLinux::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms) { +#if 0 int rc = VINF_SUCCESS; char szIfName[/*IFNAMSIZ*/ 16 + 36]; long long unsigned int u64Busy, tmp; @@ -344,28 +365,184 @@ int CollectorLinux::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint } else rc = VERR_ACCESS_DENIED; +#else + int rc = VERR_MISSING; + FILE *f = fopen("/proc/diskstats", "r"); + if (f) + { + char szBuf[128]; + while (fgets(szBuf, sizeof(szBuf), f)) + { + char *pszBufName = szBuf; + while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */ + while (RT_C_IS_DIGIT(*pszBufName)) ++pszBufName; /* Skip major */ + while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */ + while (RT_C_IS_DIGIT(*pszBufName)) ++pszBufName; /* Skip minor */ + while (*pszBufName == ' ') ++pszBufName; /* Skip spaces */ + + char *pszBufData = strchr(pszBufName, ' '); + if (!pszBufData) + { + LogRel(("CollectorLinux::getRawHostDiskLoad() failed to parse disk stats: %s\n", szBuf)); + continue; + } + *pszBufData++ = '\0'; + if (!strcmp(name, pszBufName)) + { + long long unsigned int u64Busy, tmp; + + if (sscanf(pszBufData, "%llu %llu %llu %llu %llu %llu %llu %llu %llu %llu %llu", + &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &tmp, &u64Busy, &tmp) == 11) + { + *disk_ms = u64Busy; + *total_ms = (uint64_t)(mSingleUser + mSingleKernel + mSingleIdle) * 1000 / mHZ; + rc = VINF_SUCCESS; + } + else + rc = VERR_FILE_IO_ERROR; + break; + } + } + fclose(f); + } +#endif return rc; } -static char *getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName) +char *CollectorLinux::trimNewline(char *pszName) +{ + unsigned cbName = strlen(pszName); + if (cbName == 0) + return pszName; + + char *pszEnd = pszName + cbName - 1; + while (pszEnd > pszName && *pszEnd == '\n') + pszEnd--; + pszEnd[1] = '\0'; + + return pszName; +} + +char *CollectorLinux::trimTrailingDigits(char *pszName) +{ + unsigned cbName = strlen(pszName); + if (cbName == 0) + return pszName; + + char *pszEnd = pszName + cbName - 1; + while (pszEnd > pszName && (RT_C_IS_DIGIT(*pszEnd) || *pszEnd == '\n')) + pszEnd--; + pszEnd[1] = '\0'; + + return pszName; +} + +/** + * Use the partition name to get the name of the disk. Any path component is stripped. + * if fTrimDigits is true, trailing digits are stripped as well, for example '/dev/sda5' + * is converted to 'sda'. + * + * @param pszDiskName Where to store the name of the disk. + * @param cbDiskName The size of the buffer pszDiskName points to. + * @param pszDevName The device name used to get the disk name. + * @param fTrimDigits Trim trailing digits (e.g. /dev/sda5) + */ +void CollectorLinux::getDiskName(char *pszDiskName, size_t cbDiskName, const char *pszDevName, bool fTrimDigits) { unsigned cbName = 0; unsigned cbDevName = strlen(pszDevName); const char *pszEnd = pszDevName + cbDevName - 1; - while (pszEnd > pszDevName && RT_C_IS_DIGIT(*pszEnd)) - pszEnd--; + if (fTrimDigits) + while (pszEnd > pszDevName && RT_C_IS_DIGIT(*pszEnd)) + pszEnd--; while (pszEnd > pszDevName && *pszEnd != '/') { cbName++; pszEnd--; } RTStrCopy(pszDiskName, RT_MIN(cbName + 1, cbDiskName), pszEnd + 1); - return pszDiskName; } +void CollectorLinux::addRaidDisks(const char *pcszDevice, DiskList& listDisks) +{ + FILE *f = fopen("/proc/mdstat", "r"); + if (f) + { + char szBuf[128]; + while (fgets(szBuf, sizeof(szBuf), f)) + { + char *pszBufName = szBuf; -int getDiskListByFs(const char *pszPath, DiskList& listDisks) + char *pszBufData = strchr(pszBufName, ' '); + if (!pszBufData) + { + LogRel(("CollectorLinux::addRaidDisks() failed to parse disk stats: %s\n", szBuf)); + continue; + } + *pszBufData++ = '\0'; + if (!strcmp(pcszDevice, pszBufName)) + { + while (*pszBufData == ':') ++pszBufData; /* Skip delimiter */ + while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */ + while (RT_C_IS_ALNUM(*pszBufData)) ++pszBufData; /* Skip status */ + while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */ + while (RT_C_IS_ALNUM(*pszBufData)) ++pszBufData; /* Skip type */ + + while (*pszBufData != '\0') + { + while (*pszBufData == ' ') ++pszBufData; /* Skip spaces */ + char *pszDisk = pszBufData; + while (RT_C_IS_ALPHA(*pszBufData)) + ++pszBufData; + if (*pszBufData) + { + *pszBufData++ = '\0'; + listDisks.push_back(RTCString(pszDisk)); + while (*pszBufData != '\0' && *pszBufData != ' ') + ++pszBufData; + } + else + listDisks.push_back(RTCString(pszDisk)); + } + break; + } + } + fclose(f); + } +} + +void CollectorLinux::addVolumeDependencies(const char *pcszVolume, DiskList& listDisks) +{ + char szVolInfo[RTPATH_MAX]; + int rc = RTPathAppPrivateArch(szVolInfo, + sizeof(szVolInfo) - sizeof("/" VBOXVOLINFO_NAME " ") - strlen(pcszVolume)); + if (RT_FAILURE(rc)) + { + LogRel(("VolInfo: Failed to get program path, rc=%Rrc\n", rc)); + return; + } + strcat(szVolInfo, "/" VBOXVOLINFO_NAME " "); + strcat(szVolInfo, pcszVolume); + + FILE *fp = popen(szVolInfo, "r"); + if (fp) + { + char szBuf[128]; + + while (fgets(szBuf, sizeof(szBuf), fp)) + if (strncmp(szBuf, RT_STR_TUPLE("dm-"))) + listDisks.push_back(RTCString(trimTrailingDigits(szBuf))); + else + listDisks.push_back(RTCString(trimNewline(szBuf))); + + pclose(fp); + } + else + listDisks.push_back(RTCString(pcszVolume)); +} + +int CollectorLinux::getDiskListByFs(const char *pszPath, DiskList& listUsage, DiskList& listLoad) { FILE *mtab = setmntent("/etc/mtab", "r"); if (mtab) @@ -373,10 +550,45 @@ int getDiskListByFs(const char *pszPath, DiskList& listDisks) struct mntent *mntent; while ((mntent = getmntent(mtab))) { + /* Skip rootfs entry, there must be another root mount. */ + if (strcmp(mntent->mnt_fsname, "rootfs") == 0) + continue; if (strcmp(pszPath, mntent->mnt_dir) == 0) { - char szDevName[32]; - listDisks.push_back(RTCString(getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname))); + char szDevName[128]; + char szFsName[1024]; + /* Try to resolve symbolic link if necessary. Yes, we access the file system here! */ + int rc = RTPathReal(mntent->mnt_fsname, szFsName, sizeof(szFsName)); + if (RT_FAILURE(rc)) + continue; /* something got wrong, just ignore this path */ + /* check against the actual mtab entry, NOT the real path as /dev/mapper/xyz is + * often a symlink to something else */ + if (!strncmp(mntent->mnt_fsname, RT_STR_TUPLE("/dev/mapper"))) + { + /* LVM */ + getDiskName(szDevName, sizeof(szDevName), mntent->mnt_fsname, false /*=fTrimDigits*/); + addVolumeDependencies(szDevName, listUsage); + listLoad = listUsage; + } + else if (!strncmp(szFsName, RT_STR_TUPLE("/dev/md"))) + { + /* Software RAID */ + getDiskName(szDevName, sizeof(szDevName), szFsName, false /*=fTrimDigits*/); + listUsage.push_back(RTCString(szDevName)); + addRaidDisks(szDevName, listLoad); + } + else + { + /* Plain disk partition. Trim the trailing digits to get the drive name */ + getDiskName(szDevName, sizeof(szDevName), szFsName, true /*=fTrimDigits*/); + listUsage.push_back(RTCString(szDevName)); + listLoad.push_back(RTCString(szDevName)); + } + if (listUsage.empty() || listLoad.empty()) + { + LogRel(("Failed to retrive disk info: getDiskName(%s) --> %s\n", + mntent->mnt_fsname, szDevName)); + } break; } } diff --git a/src/VBox/Main/src-server/linux/USBGetDevices.cpp b/src/VBox/Main/src-server/linux/USBGetDevices.cpp index d824a017..b4ef0df7 100644 --- a/src/VBox/Main/src-server/linux/USBGetDevices.cpp +++ b/src/VBox/Main/src-server/linux/USBGetDevices.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -357,11 +357,11 @@ static int usbReadSpeed(const char *pszValue, USBDEVICESPEED *pSpd, char **ppszN { pszValue = RTStrStripL(pszValue); /* verified with Linux 2.4.0 ... Linux 2.6.25 */ - if (!strncmp(pszValue, "1.5", 3)) + if (!strncmp(pszValue, RT_STR_TUPLE("1.5"))) *pSpd = USBDEVICESPEED_LOW; - else if (!strncmp(pszValue, "12 ", 3)) + else if (!strncmp(pszValue, RT_STR_TUPLE("12 "))) *pSpd = USBDEVICESPEED_FULL; - else if (!strncmp(pszValue, "480", 3)) + else if (!strncmp(pszValue, RT_STR_TUPLE("480"))) *pSpd = USBDEVICESPEED_HIGH; else *pSpd = USBDEVICESPEED_UNKNOWN; @@ -593,7 +593,7 @@ static PUSBDEVICE getDevicesFromUsbfs(const char *pcszUsbfsRoot, bool testfs) deviceFreeMembers(&Dev); /* Reset device state */ - memset(&Dev, 0, sizeof (Dev)); + RT_ZERO(Dev); Dev.enmState = USBDEVICESTATE_UNUSED; cHits = 1; @@ -1122,7 +1122,7 @@ static int usbGetPortFromSysfsPath(const char *pszPath, uint8_t *pu8Port) if (!pchDash && !pchDot) { /* No -/. so it must be a root hub. Check that it's usb<something>. */ - if (strncmp(pszLastComp, "usb", sizeof("usb") - 1) != 0) + if (strncmp(pszLastComp, RT_STR_TUPLE("usb")) != 0) { Log(("usbGetPortFromSysfsPath(%s): failed [2]\n", pszPath)); return VERR_INVALID_PARAMETER; @@ -1430,7 +1430,7 @@ void TestUSBSetInotifyAvailable(bool fHaveInotifyLibC, bool fHaveInotifyKernel) s_fHaveInotifyKernel = fHaveInotifyKernel; } # define dlsym testDLSym -# define close(a) do {} while(0) +# define close(a) do {} while (0) #endif /** Is inotify available and working on this system? This is a requirement @@ -1586,7 +1586,7 @@ void TestUSBSetEnv(const char *pcszEnvUsb, const char *pcszEnvUsbRoot) * what is available on the host and what if anything the user has specified * in the environment. * @returns iprt status value - * @param pfUsingUsbfsDevices on success this will be set to true if + * @param pfUsingUsbfsDevices on success this will be set to true if * the prefered access method is USBFS-like and to * false if it is sysfs/device node-like * @param ppcszDevicesRoot on success the root of the tree of USBFS-like diff --git a/src/VBox/Main/src-server/linux/USBProxyServiceLinux.cpp b/src/VBox/Main/src-server/linux/USBProxyServiceLinux.cpp index a89c3e00..82c97aa9 100644 --- a/src/VBox/Main/src-server/linux/USBProxyServiceLinux.cpp +++ b/src/VBox/Main/src-server/linux/USBProxyServiceLinux.cpp @@ -320,7 +320,7 @@ int USBProxyServiceLinux::waitUsbfs(RTMSINTERVAL aMillies) aMillies = 500; } - memset(&PollFds, 0, sizeof(PollFds)); + RT_ZERO(PollFds); PollFds[0].fd = RTFileToNative(mhFile); PollFds[0].events = POLLIN; PollFds[1].fd = RTPipeToNative(mhWakeupPipeR); diff --git a/src/VBox/Main/src-server/linux/vbox-libhal.cpp b/src/VBox/Main/src-server/linux/vbox-libhal.cpp index 2e7c2716..ade8f072 100644 --- a/src/VBox/Main/src-server/linux/vbox-libhal.cpp +++ b/src/VBox/Main/src-server/linux/vbox-libhal.cpp @@ -5,7 +5,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/os2/NetIf-os2.cpp b/src/VBox/Main/src-server/os2/NetIf-os2.cpp index d0c287b5..cac174a7 100644 --- a/src/VBox/Main/src-server/os2/NetIf-os2.cpp +++ b/src/VBox/Main/src-server/os2/NetIf-os2.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -33,23 +33,24 @@ int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) return VERR_NOT_IMPLEMENTED; } -int NetIfEnableStaticIpConfig(VirtualBox *pVbox, HostNetworkInterface * pIf, ULONG ip, ULONG mask) +int NetIfEnableStaticIpConfig(VirtualBox *pVBox, HostNetworkInterface * pIf, ULONG ip, ULONG mask) { return VERR_NOT_IMPLEMENTED; } -int NetIfEnableStaticIpConfigV6(VirtualBox *pVbox, HostNetworkInterface * pIf, IN_BSTR aIPV6Address, ULONG aIPV6MaskPrefixLength) +int NetIfEnableStaticIpConfigV6(VirtualBox *pVBox, HostNetworkInterface * pIf, IN_BSTR aIPV6Address, ULONG aIPV6MaskPrefixLength) { return VERR_NOT_IMPLEMENTED; } -int NetIfEnableDynamicIpConfig(VirtualBox *pVbox, HostNetworkInterface * pIf) +int NetIfEnableDynamicIpConfig(VirtualBox *pVBox, HostNetworkInterface * pIf) { return VERR_NOT_IMPLEMENTED; } -int NetIfDhcpRediscover(VirtualBox *pVbox, HostNetworkInterface * pIf) +int NetIfDhcpRediscover(VirtualBox *pVBox, HostNetworkInterface * pIf) { return VERR_NOT_IMPLEMENTED; } + diff --git a/src/VBox/Main/src-server/os2/PerformanceOs2.cpp b/src/VBox/Main/src-server/os2/PerformanceOs2.cpp index e004ab83..054932ff 100644 --- a/src/VBox/Main/src-server/os2/PerformanceOs2.cpp +++ b/src/VBox/Main/src-server/os2/PerformanceOs2.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -39,31 +39,26 @@ CollectorHAL *createHAL() int CollectorOS2::getHostCpuLoad(ULONG *user, ULONG *kernel, ULONG *idle) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorOS2::getHostCpuMHz(ULONG *mhz) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorOS2::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorOS2::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel) { - return E_NOTIMPL; + return VERR_NOT_IMPLEMENTED; } int CollectorOS2::getProcessMemoryUsage(RTPROCESS process, ULONG *used) { - return E_NOTIMPL; -} - -int getDiskListByFs(const char *name, DiskList& list) -{ return VERR_NOT_IMPLEMENTED; } diff --git a/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.cpp b/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.cpp index 4bd864f1..a38b7654 100644 --- a/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.cpp +++ b/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.h b/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.h index 4fc35b8a..e306dac0 100644 --- a/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.h +++ b/src/VBox/Main/src-server/solaris/DynLoadLibSolaris.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/solaris/NetIf-solaris.cpp b/src/VBox/Main/src-server/solaris/NetIf-solaris.cpp index bc700820..82e8a338 100644 --- a/src/VBox/Main/src-server/solaris/NetIf-solaris.cpp +++ b/src/VBox/Main/src-server/solaris/NetIf-solaris.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008-2011 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -24,6 +24,7 @@ #include <iprt/err.h> #include <iprt/ctype.h> +#include <iprt/mem.h> #include <iprt/path.h> #include <list> @@ -73,10 +74,10 @@ static uint32_t getInstance(const char *pszIfaceName, char *pszDevName) return uInstance; } -static uint64_t kstatGet(const char *name) +static uint32_t kstatGet(const char *name) { kstat_ctl_t *kc; - uint64_t uSpeed = 0; + uint32_t uSpeed = 0; if ((kc = kstat_open()) == 0) { @@ -84,14 +85,14 @@ static uint64_t kstatGet(const char *name) return 0; } - kstat_t *ksAdapter = kstat_lookup(kc, "link", -1, (char *)name); + kstat_t *ksAdapter = kstat_lookup(kc, (char *)"link", -1, (char *)name); if (ksAdapter == 0) { char szModule[KSTAT_STRLEN]; uint32_t uInstance = getInstance(name, szModule); - ksAdapter = kstat_lookup(kc, szModule, uInstance, "phys"); + ksAdapter = kstat_lookup(kc, szModule, uInstance, (char *)"phys"); if (ksAdapter == 0) - ksAdapter = kstat_lookup(kc, szModule, uInstance, name); + ksAdapter = kstat_lookup(kc, szModule, uInstance, (char*)name); } if (ksAdapter == 0) LogRel(("Failed to get network statistics for %s\n", name)); @@ -103,15 +104,21 @@ static uint64_t kstatGet(const char *name) if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"ifspeed")) == 0) LogRel(("kstat_data_lookup(ifspeed) -> %d, name=%s\n", errno, name)); else - uSpeed = kn->value.ul; + uSpeed = kn->value.ul / 1000000; /* bits -> Mbits */ } kstat_close(kc); + LogFlow(("kstatGet(%s) -> %u Mbit/s\n", name, uSpeed)); return uSpeed; } static void queryIfaceSpeed(PNETIFINFO pInfo) { - pInfo->uSpeedMbits = kstatGet(pInfo->szShortName) / 1000000; /* bits -> Mbits */ + /* Don't query interface speed for inactive interfaces (see @bugref{6345}). */ + if (pInfo->enmStatus == NETIF_S_UP) + pInfo->uSpeedMbits = kstatGet(pInfo->szShortName); + else + pInfo->uSpeedMbits = 0; + LogFlow(("queryIfaceSpeed(%s) -> %u\n", pInfo->szShortName, pInfo->uSpeedMbits)); } static void vboxSolarisAddHostIface(char *pszIface, int Instance, void *pvHostNetworkInterfaceList) @@ -192,12 +199,12 @@ static void vboxSolarisAddHostIface(char *pszIface, int Instance, void *pvHostNe * Try to get IP V4 address and netmask as well as Ethernet address. */ NETIFINFO Info; - memset(&Info, 0, sizeof(Info)); + RT_ZERO(Info); int Sock = socket(PF_INET, SOCK_DGRAM, IPPROTO_IP); if (Sock > 0) { struct lifreq IfReq; - strcpy(IfReq.lifr_name, szNICInstance); + RTStrCopy(IfReq.lifr_name, sizeof(IfReq.lifr_name), szNICInstance); if (ioctl(Sock, SIOCGLIFADDR, &IfReq) >= 0) { memcpy(Info.IPAddress.au8, &((struct sockaddr_in *)&IfReq.lifr_addr)->sin_addr.s_addr, @@ -236,7 +243,7 @@ static void vboxSolarisAddHostIface(char *pszIface, int Instance, void *pvHostNe if (Sock > 0) { struct lifreq IfReq; - strcpy(IfReq.lifr_name, szNICInstance); + RTStrCopy(IfReq.lifr_name, sizeof(IfReq.lifr_name), szNICInstance); if (ioctl(Sock, SIOCGLIFADDR, &IfReq) >= 0) { memcpy(Info.IPv6Address.au8, ((struct sockaddr_in6 *)&IfReq.lifr_addr)->sin6_addr.s6_addr, @@ -269,7 +276,7 @@ static void vboxSolarisAddHostIface(char *pszIface, int Instance, void *pvHostNe strncpy(Info.szShortName, szNICInstance, sizeof(Info.szShortName) - 1); HostNetworkInterfaceType_T enmType; - if (strncmp("vboxnet", szNICInstance, 7)) + if (strncmp(szNICInstance, RT_STR_TUPLE("vboxnet"))) enmType = HostNetworkInterfaceType_Bridged; else enmType = HostNetworkInterfaceType_HostOnly; @@ -285,15 +292,15 @@ static boolean_t vboxSolarisAddLinkHostIface(const char *pszIface, void *pvHostN /* * Skip IPSEC interfaces. It's at IP level. */ - if (!strncmp(pszIface, "ip.tun", 6)) + if (!strncmp(pszIface, RT_STR_TUPLE("ip.tun"))) return _B_FALSE; /* * Skip our own dynamic VNICs but don't skip VNIC templates. * These names originate from VBoxNetFltBow-solaris.c, hardcoded here for now. */ - if ( strncmp(pszIface, "vboxvnic_template", 17) - && !strncmp(pszIface, "vboxvnic", 8)) + if ( strncmp(pszIface, RT_STR_TUPLE("vboxvnic_template")) + && !strncmp(pszIface, RT_STR_TUPLE("vboxvnic"))) return _B_FALSE; /* @@ -406,59 +413,65 @@ int NetIfList(std::list <ComObjPtr<HostNetworkInterface> > &list) if (Sock > 0) { struct lifnum IfNum; - memset(&IfNum, 0, sizeof(IfNum)); + RT_ZERO(IfNum); IfNum.lifn_family = AF_INET; int rc = ioctl(Sock, SIOCGLIFNUM, &IfNum); if (!rc) { - struct lifreq Ifaces[24]; - struct lifconf IfConfig; - memset(&IfConfig, 0, sizeof(IfConfig)); - IfConfig.lifc_family = AF_INET; - IfConfig.lifc_len = sizeof(Ifaces); - IfConfig.lifc_buf = (caddr_t)&(Ifaces[0]); - rc = ioctl(Sock, SIOCGLIFCONF, &IfConfig); - if (!rc) + int cIfaces = RT_MIN(1024, IfNum.lifn_count); /* sane limit */ + int cbIfaces = cIfaces * sizeof(struct lifreq); + struct lifreq *Ifaces = (struct lifreq *)RTMemTmpAlloc(cbIfaces); + if (Ifaces) { - for (int i = 0; i < IfNum.lifn_count; i++) + struct lifconf IfConfig; + RT_ZERO(IfConfig); + IfConfig.lifc_family = AF_INET; + IfConfig.lifc_len = cbIfaces; + IfConfig.lifc_buf = (caddr_t)Ifaces; + rc = ioctl(Sock, SIOCGLIFCONF, &IfConfig); + if (!rc) { - /* - * Skip loopback interfaces. - */ - if (!strncmp(Ifaces[i].lifr_name, "lo", 2)) - continue; - -#if 0 - rc = ioctl(Sock, SIOCGLIFADDR, &(Ifaces[i])); - if (rc >= 0) + for (int i = 0; i < cIfaces; i++) { - memcpy(Info.IPAddress.au8, ((struct sockaddr *)&Ifaces[i].lifr_addr)->sa_data, - sizeof(Info.IPAddress.au8)); - // SIOCGLIFNETMASK - struct arpreq ArpReq; - memcpy(&ArpReq.arp_pa, &Ifaces[i].lifr_addr, sizeof(struct sockaddr_in)); - /* - * We might fail if the interface has not been assigned an IP address. - * That doesn't matter; as long as it's plumbed we can pick it up. - * But, if it has not acquired an IP address we cannot obtain it's MAC - * address this way, so we just use all zeros there. + * Skip loopback interfaces. */ - rc = ioctl(Sock, SIOCGARP, &ArpReq); + if (!strncmp(Ifaces[i].lifr_name, RT_STR_TUPLE("lo"))) + continue; + +#if 0 + rc = ioctl(Sock, SIOCGLIFADDR, &(Ifaces[i])); if (rc >= 0) - memcpy(&Info.MACAddress, ArpReq.arp_ha.sa_data, sizeof(Info.MACAddress)); + { + memcpy(Info.IPAddress.au8, ((struct sockaddr *)&Ifaces[i].lifr_addr)->sa_data, + sizeof(Info.IPAddress.au8)); + // SIOCGLIFNETMASK + struct arpreq ArpReq; + memcpy(&ArpReq.arp_pa, &Ifaces[i].lifr_addr, sizeof(struct sockaddr_in)); + + /* + * We might fail if the interface has not been assigned an IP address. + * That doesn't matter; as long as it's plumbed we can pick it up. + * But, if it has not acquired an IP address we cannot obtain it's MAC + * address this way, so we just use all zeros there. + */ + rc = ioctl(Sock, SIOCGARP, &ArpReq); + if (rc >= 0) + memcpy(&Info.MACAddress, ArpReq.arp_ha.sa_data, sizeof(Info.MACAddress)); + + char szNICDesc[LIFNAMSIZ + 256]; + char *pszIface = Ifaces[i].lifr_name; + strcpy(szNICDesc, pszIface); + + vboxSolarisAddLinkHostIface(pszIface, &list); + } +#endif - char szNICDesc[LIFNAMSIZ + 256]; char *pszIface = Ifaces[i].lifr_name; - strcpy(szNICDesc, pszIface); - vboxSolarisAddLinkHostIface(pszIface, &list); } -#endif - - char *pszIface = Ifaces[i].lifr_name; - vboxSolarisAddLinkHostIface(pszIface, &list); } + RTMemTmpFree(Ifaces); } } close(Sock); @@ -486,3 +499,17 @@ int NetIfGetConfigByName(PNETIFINFO pInfo) return VERR_NOT_IMPLEMENTED; } +/** + * Retrieve the physical link speed in megabits per second. If the interface is + * not up or otherwise unavailable the zero speed is returned. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param puMbits Where to store the link speed. + */ +int NetIfGetLinkSpeed(const char *pcszIfName, uint32_t *puMbits) +{ + *puMbits = kstatGet(pcszIfName); + return VINF_SUCCESS; +} diff --git a/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp b/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp index 7abeae04..ed935116 100644 --- a/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp +++ b/src/VBox/Main/src-server/solaris/PerformanceSolaris.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -26,33 +26,85 @@ #include <unistd.h> #include <sys/sysinfo.h> #include <sys/time.h> +#include <sys/types.h> +#include <sys/statvfs.h> #include <iprt/ctype.h> #include <iprt/err.h> #include <iprt/string.h> #include <iprt/alloc.h> #include <iprt/param.h> -#include <VBox/log.h> +#include <iprt/path.h> +#include <iprt/system.h> + +#include "Logging.h" #include "Performance.h" +#include <dlfcn.h> + +#include <libzfs.h> +#include <libnvpair.h> + +#include <map> + namespace pm { + typedef libzfs_handle_t *(*PFNZFSINIT)(void); + typedef void (*PFNZFSFINI)(libzfs_handle_t *); + typedef zfs_handle_t *(*PFNZFSOPEN)(libzfs_handle_t *, const char *, int); + typedef void (*PFNZFSCLOSE)(zfs_handle_t *); + typedef uint64_t (*PFNZFSPROPGETINT)(zfs_handle_t *, zfs_prop_t); + typedef zpool_handle_t *(*PFNZPOOLOPEN)(libzfs_handle_t *, const char *); + typedef void (*PFNZPOOLCLOSE)(zpool_handle_t *); + typedef nvlist_t *(*PFNZPOOLGETCONFIG)(zpool_handle_t *, nvlist_t **); + typedef char *(*PFNZPOOLVDEVNAME)(libzfs_handle_t *, zpool_handle_t *, nvlist_t *, boolean_t); + + typedef std::map<RTCString,RTCString> FsMap; + class CollectorSolaris : public CollectorHAL { public: CollectorSolaris(); virtual ~CollectorSolaris(); virtual int getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available); + virtual int getHostFilesystemUsage(const char *name, ULONG *total, ULONG *used, ULONG *available); + virtual int getHostDiskSize(const char *name, uint64_t *size); virtual int getProcessMemoryUsage(RTPROCESS process, ULONG *used); virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle); virtual int getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx); + virtual int getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms); virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total); + + virtual int getDiskListByFs(const char *name, DiskList& listUsage, DiskList& listLoad); private: static uint32_t getInstance(const char *pszIfaceName, char *pszDevName); - kstat_ctl_t *mKC; - kstat_t *mSysPages; - kstat_t *mZFSCache; + uint64_t getZfsTotal(uint64_t cbTotal, const char *szFsType, const char *szFsName); + void updateFilesystemMap(void); + RTCString physToInstName(const char *pcszPhysName); + RTCString pathToInstName(const char *pcszDevPathName); + uint64_t wrapCorrection(uint32_t cur, uint64_t prev, const char *name); + uint64_t wrapDetection(uint64_t cur, uint64_t prev, const char *name); + + kstat_ctl_t *mKC; + kstat_t *mSysPages; + kstat_t *mZFSCache; + + void *mZfsSo; + libzfs_handle_t *mZfsLib; + PFNZFSINIT mZfsInit; + PFNZFSFINI mZfsFini; + PFNZFSOPEN mZfsOpen; + PFNZFSCLOSE mZfsClose; + PFNZFSPROPGETINT mZfsPropGetInt; + PFNZPOOLOPEN mZpoolOpen; + PFNZPOOLCLOSE mZpoolClose; + PFNZPOOLGETCONFIG mZpoolGetConfig; + PFNZPOOLVDEVNAME mZpoolVdevName; + + FsMap mFsMap; + uint32_t mCpus; + ULONG totalRAM; }; CollectorHAL *createHAL() @@ -66,7 +118,9 @@ CollectorHAL *createHAL() CollectorSolaris::CollectorSolaris() : mKC(0), mSysPages(0), - mZFSCache(0) + mZFSCache(0), + mZfsLib(0), + mCpus(0) { if ((mKC = kstat_open()) == 0) { @@ -84,12 +138,55 @@ CollectorSolaris::CollectorSolaris() { Log(("kstat_lookup(system_pages) -> %d\n", errno)); } + + /* Try to load libzfs dynamically, it may be missing. */ + mZfsSo = dlopen("libzfs.so", RTLD_LAZY); + if (mZfsSo) + { + mZfsInit = (PFNZFSINIT)(uintptr_t)dlsym(mZfsSo, "libzfs_init"); + mZfsFini = (PFNZFSFINI)(uintptr_t)dlsym(mZfsSo, "libzfs_fini"); + mZfsOpen = (PFNZFSOPEN)(uintptr_t)dlsym(mZfsSo, "zfs_open"); + mZfsClose = (PFNZFSCLOSE)(uintptr_t)dlsym(mZfsSo, "zfs_close"); + mZfsPropGetInt = (PFNZFSPROPGETINT)(uintptr_t)dlsym(mZfsSo, "zfs_prop_get_int"); + mZpoolOpen = (PFNZPOOLOPEN)(uintptr_t)dlsym(mZfsSo, "zpool_open"); + mZpoolClose = (PFNZPOOLCLOSE)(uintptr_t)dlsym(mZfsSo, "zpool_close"); + mZpoolGetConfig = (PFNZPOOLGETCONFIG)(uintptr_t)dlsym(mZfsSo, "zpool_get_config"); + mZpoolVdevName = (PFNZPOOLVDEVNAME)(uintptr_t)dlsym(mZfsSo, "zpool_vdev_name"); + + if ( mZfsInit + && mZfsOpen + && mZfsClose + && mZfsPropGetInt + && mZpoolOpen + && mZpoolClose + && mZpoolGetConfig + && mZpoolVdevName) + mZfsLib = mZfsInit(); + else + LogRel(("Incompatible libzfs? libzfs_init=%p zfs_open=%p zfs_close=%p zfs_prop_get_int=%p\n", + mZfsInit, mZfsOpen, mZfsClose, mZfsPropGetInt)); + } + + updateFilesystemMap(); + /* Notice that mCpus member will be initialized by HostCpuLoadRaw::init() */ + + uint64_t cb; + int rc = RTSystemQueryTotalRam(&cb); + if (RT_FAILURE(rc)) + totalRAM = 0; + else + totalRAM = (ULONG)(cb / 1024); } CollectorSolaris::~CollectorSolaris() { if (mKC) kstat_close(mKC); + /* Not calling libzfs_fini() causes file descriptor leaks (#6788). */ + if (mZfsFini && mZfsLib) + mZfsFini(mZfsLib); + if (mZfsSo) + dlclose(mZfsSo); } int CollectorSolaris::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle) @@ -123,6 +220,8 @@ int CollectorSolaris::getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64 Log(("no cpu stats found!\n")); return VERR_INTERNAL_ERROR; } + else + mCpus = cpus; if (user) *user = tmpUser; if (kernel) *kernel = tmpKernel; @@ -148,8 +247,18 @@ int CollectorSolaris::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, ui { //Assert((pid_t)process == pstatus.pr_pid); //Log(("user=%u kernel=%u total=%u\n", prusage.pr_utime.tv_sec, prusage.pr_stime.tv_sec, prusage.pr_tstamp.tv_sec)); - *user = (uint64_t)prusage.pr_utime.tv_sec * 1000000000 + prusage.pr_utime.tv_nsec; - *kernel = (uint64_t)prusage.pr_stime.tv_sec * 1000000000 + prusage.pr_stime.tv_nsec; + /* + * The CPU time spent must be adjusted by the number of cores for compatibility with + * other platforms (see @bugref{6345}). + */ + Assert(mCpus); + if (mCpus) + { + *user = ((uint64_t)prusage.pr_utime.tv_sec * 1000000000 + prusage.pr_utime.tv_nsec) / mCpus; + *kernel = ((uint64_t)prusage.pr_stime.tv_sec * 1000000000 + prusage.pr_stime.tv_nsec) / mCpus; + } + else + *user = *kernel = 0; *total = (uint64_t)prusage.pr_tstamp.tv_sec * 1000000000 + prusage.pr_tstamp.tv_nsec; //Log(("user=%llu kernel=%llu total=%llu\n", *user, *kernel, *total)); } @@ -171,61 +280,15 @@ int CollectorSolaris::getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, ui int CollectorSolaris::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) { - int rc = VINF_SUCCESS; - - kstat_named_t *kn; - - if (mKC == 0 || mSysPages == 0) - return VERR_INTERNAL_ERROR; - - if (kstat_read(mKC, mSysPages, 0) == -1) + AssertReturn(totalRAM, VERR_INTERNAL_ERROR); + uint64_t cb; + int rc = RTSystemQueryAvailableRam(&cb); + if (RT_SUCCESS(rc)) { - Log(("kstat_read(sys_pages) -> %d\n", errno)); - return VERR_INTERNAL_ERROR; + *total = totalRAM; + *available = cb / 1024; + *used = *total - *available; } - if ((kn = (kstat_named_t *)kstat_data_lookup(mSysPages, (char *)"freemem")) == 0) - { - Log(("kstat_data_lookup(freemem) -> %d\n", errno)); - return VERR_INTERNAL_ERROR; - } - *available = kn->value.ul * (PAGE_SIZE/1024); - - if (kstat_read(mKC, mZFSCache, 0) != -1) - { - if (mZFSCache) - { - if ((kn = (kstat_named_t *)kstat_data_lookup(mZFSCache, (char *)"size"))) - { - ulong_t ulSize = kn->value.ul; - - if ((kn = (kstat_named_t *)kstat_data_lookup(mZFSCache, (char *)"c_min"))) - { - /* - * Account for ZFS minimum arc cache size limit. - * "c_min" is the target minimum size of the ZFS cache, and not the hard limit. It's possible - * for "size" to shrink below "c_min" (e.g: during boot & high memory consumption). - */ - ulong_t ulMin = kn->value.ul; - *available += ulSize > ulMin ? (ulSize - ulMin) / 1024 : 0; - } - else - Log(("kstat_data_lookup(c_min) ->%d\n", errno)); - } - else - Log(("kstat_data_lookup(size) -> %d\n", errno)); - } - else - Log(("mZFSCache missing.\n")); - } - - if ((kn = (kstat_named_t *)kstat_data_lookup(mSysPages, (char *)"physmem")) == 0) - { - Log(("kstat_data_lookup(physmem) -> %d\n", errno)); - return VERR_INTERNAL_ERROR; - } - *total = kn->value.ul * (PAGE_SIZE/1024); - *used = *total - *available; - return rc; } @@ -285,21 +348,53 @@ uint32_t CollectorSolaris::getInstance(const char *pszIfaceName, char *pszDevNam return uInstance; } +uint64_t CollectorSolaris::wrapCorrection(uint32_t cur, uint64_t prev, const char *name) +{ + NOREF(name); + uint64_t corrected = (prev & 0xffffffff00000000) + cur; + if (cur < (prev & 0xffffffff)) + { + /* wrap has occurred */ + corrected += 0x100000000; + LogFlowThisFunc(("Corrected wrap on %s (%u < %u), returned %llu.\n", + name, cur, (uint32_t)prev, corrected)); + } + return corrected; +} + +uint64_t CollectorSolaris::wrapDetection(uint64_t cur, uint64_t prev, const char *name) +{ + static bool fNotSeen = true; + + if (fNotSeen && cur < prev) + { + fNotSeen = false; + LogRel(("Detected wrap on %s (%llu < %llu).\n", name, cur, prev)); + } + return cur; +} + +/* + * WARNING! This function expects the previous values of rx and tx counter to + * be passed in as well as returnes new values in the same parameters. This is + * needed to provide a workaround for 32-bit counter wrapping. + */ int CollectorSolaris::getRawHostNetworkLoad(const char *name, uint64_t *rx, uint64_t *tx) { + static bool g_fNotReported = true; AssertReturn(strlen(name) < KSTAT_STRLEN, VERR_INVALID_PARAMETER); LogFlowThisFunc(("m=%s i=%d n=%s\n", "link", -1, name)); - kstat_t *ksAdapter = kstat_lookup(mKC, "link", -1, (char *)name); + kstat_t *ksAdapter = kstat_lookup(mKC, (char *)"link", -1, (char *)name); if (ksAdapter == 0) { char szModule[KSTAT_STRLEN]; uint32_t uInstance = getInstance(name, szModule); LogFlowThisFunc(("m=%s i=%u n=%s\n", szModule, uInstance, "phys")); - ksAdapter = kstat_lookup(mKC, szModule, uInstance, "phys"); + ksAdapter = kstat_lookup(mKC, szModule, uInstance, (char *)"phys"); if (ksAdapter == 0) { LogFlowThisFunc(("m=%s i=%u n=%s\n", szModule, uInstance, name)); - ksAdapter = kstat_lookup(mKC, szModule, uInstance, name); + ksAdapter = kstat_lookup(mKC, szModule, uInstance, (char *)name); if (ksAdapter == 0) { LogRel(("Failed to get network statistics for %s\n", name)); @@ -313,24 +408,311 @@ int CollectorSolaris::getRawHostNetworkLoad(const char *name, uint64_t *rx, uint return VERR_INTERNAL_ERROR; } kstat_named_t *kn; - if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"rbytes")) == 0) + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"rbytes64")) == 0) { - LogRel(("kstat_data_lookup(rbytes) -> %d, name=%s\n", errno, name)); - return VERR_INTERNAL_ERROR; + if (g_fNotReported) + { + g_fNotReported = false; + LogRel(("Failed to locate rbytes64, falling back to 32-bit counters...\n")); + } + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"rbytes")) == 0) + { + LogRel(("kstat_data_lookup(rbytes) -> %d, name=%s\n", errno, name)); + return VERR_INTERNAL_ERROR; + } + *rx = wrapCorrection(kn->value.ul, *rx, "rbytes"); } - *rx = kn->value.ul; - if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"obytes")) == 0) + else + *rx = wrapDetection(kn->value.ull, *rx, "rbytes64"); + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"obytes64")) == 0) { - LogRel(("kstat_data_lookup(obytes) -> %d\n", errno)); - return VERR_INTERNAL_ERROR; + if (g_fNotReported) + { + g_fNotReported = false; + LogRel(("Failed to locate obytes64, falling back to 32-bit counters...\n")); + } + if ((kn = (kstat_named_t *)kstat_data_lookup(ksAdapter, (char *)"obytes")) == 0) + { + LogRel(("kstat_data_lookup(obytes) -> %d\n", errno)); + return VERR_INTERNAL_ERROR; + } + *tx = wrapCorrection(kn->value.ul, *tx, "obytes"); + } + else + *tx = wrapDetection(kn->value.ull, *tx, "obytes64"); + return VINF_SUCCESS; +} + +int CollectorSolaris::getRawHostDiskLoad(const char *name, uint64_t *disk_ms, uint64_t *total_ms) +{ + int rc = VINF_SUCCESS; + AssertReturn(strlen(name) < KSTAT_STRLEN, VERR_INVALID_PARAMETER); + LogFlowThisFunc(("n=%s\n", name)); + kstat_t *ksDisk = kstat_lookup(mKC, NULL, -1, (char *)name); + if (ksDisk != 0) + { + if (kstat_read(mKC, ksDisk, 0) == -1) + { + LogRel(("kstat_read(%s) -> %d\n", name, errno)); + rc = VERR_INTERNAL_ERROR; + } + else + { + kstat_io_t *ksIo = KSTAT_IO_PTR(ksDisk); + /* + * We do not care for wrap possibility here, although we may + * reconsider in about 300 years (9223372036854775807 ns). + */ + *disk_ms = ksIo->rtime / 1000000; + *total_ms = ksDisk->ks_snaptime / 1000000; + } } - *tx = kn->value.ul; + else + { + LogRel(("kstat_lookup(%s) -> %d\n", name, errno)); + rc = VERR_INTERNAL_ERROR; + } + + return rc; +} + +uint64_t CollectorSolaris::getZfsTotal(uint64_t cbTotal, const char *szFsType, const char *szFsName) +{ + if (strcmp(szFsType, "zfs")) + return cbTotal; + FsMap::iterator it = mFsMap.find(szFsName); + if (it == mFsMap.end()) + return cbTotal; + + char *pszDataset = strdup(it->second.c_str()); + char *pszEnd = pszDataset + strlen(pszDataset); + uint64_t uAvail = 0; + while (pszEnd) + { + zfs_handle_t *hDataset; + + *pszEnd = 0; + hDataset = mZfsOpen(mZfsLib, pszDataset, ZFS_TYPE_DATASET); + if (!hDataset) + break; + + if (uAvail == 0) + { + uAvail = mZfsPropGetInt(hDataset, ZFS_PROP_REFQUOTA); + if (uAvail == 0) + uAvail = UINT64_MAX; + } + + uint64_t uQuota = mZfsPropGetInt(hDataset, ZFS_PROP_QUOTA); + if (uQuota && uAvail > uQuota) + uAvail = uQuota; + + pszEnd = strrchr(pszDataset, '/'); + if (!pszEnd) + { + uint64_t uPoolSize = mZfsPropGetInt(hDataset, ZFS_PROP_USED) + + mZfsPropGetInt(hDataset, ZFS_PROP_AVAILABLE); + if (uAvail > uPoolSize) + uAvail = uPoolSize; + } + mZfsClose(hDataset); + } + free(pszDataset); + + return uAvail ? uAvail : cbTotal; +} + +int CollectorSolaris::getHostFilesystemUsage(const char *path, ULONG *total, ULONG *used, ULONG *available) +{ + struct statvfs64 stats; + + if (statvfs64(path, &stats) == -1) + { + LogRel(("Failed to collect %s filesystem usage: errno=%d.\n", path, errno)); + return VERR_ACCESS_DENIED; + } + uint64_t cbBlock = stats.f_frsize ? stats.f_frsize : stats.f_bsize; + *total = (ULONG)(getZfsTotal(cbBlock * stats.f_blocks, stats.f_basetype, path) / _1M); + LogFlowThisFunc(("f_blocks=%llu.\n", stats.f_blocks)); + *used = (ULONG)(cbBlock * (stats.f_blocks - stats.f_bfree) / _1M); + *available = (ULONG)(cbBlock * stats.f_bavail / _1M); + return VINF_SUCCESS; } -int getDiskListByFs(const char *name, DiskList& list) +int CollectorSolaris::getHostDiskSize(const char *name, uint64_t *size) { - return VERR_NOT_IMPLEMENTED; + int rc = VINF_SUCCESS; + AssertReturn(strlen(name) + 5 < KSTAT_STRLEN, VERR_INVALID_PARAMETER); + LogFlowThisFunc(("n=%s\n", name)); + char szName[KSTAT_STRLEN]; + strcpy(szName, name); + strcat(szName, ",err"); + kstat_t *ksDisk = kstat_lookup(mKC, NULL, -1, szName); + if (ksDisk != 0) + { + if (kstat_read(mKC, ksDisk, 0) == -1) + { + LogRel(("kstat_read(%s) -> %d\n", name, errno)); + rc = VERR_INTERNAL_ERROR; + } + else + { + kstat_named_t *kn; + if ((kn = (kstat_named_t *)kstat_data_lookup(ksDisk, (char *)"Size")) == 0) + { + LogRel(("kstat_data_lookup(rbytes) -> %d, name=%s\n", errno, name)); + return VERR_INTERNAL_ERROR; + } + *size = kn->value.ull; + } + } + else + { + LogRel(("kstat_lookup(%s) -> %d\n", szName, errno)); + rc = VERR_INTERNAL_ERROR; + } + + + return rc; +} + +RTCString CollectorSolaris::physToInstName(const char *pcszPhysName) +{ + FILE *fp = fopen("/etc/path_to_inst", "r"); + if (!fp) + return RTCString(); + + RTCString strInstName; + size_t cbName = strlen(pcszPhysName); + char szBuf[RTPATH_MAX]; + while (fgets(szBuf, sizeof(szBuf), fp)) + { + if (szBuf[0] == '"' && strncmp(szBuf + 1, pcszPhysName, cbName) == 0) + { + char *pszDriver, *pszInstance; + pszDriver = strrchr(szBuf, '"'); + if (pszDriver) + { + *pszDriver = '\0'; + pszDriver = strrchr(szBuf, '"'); + if (pszDriver) + { + *pszDriver++ = '\0'; + pszInstance = strrchr(szBuf, ' '); + if (pszInstance) + { + *pszInstance = '\0'; + pszInstance = strrchr(szBuf, ' '); + if (pszInstance) + { + *pszInstance++ = '\0'; + strInstName = pszDriver; + strInstName += pszInstance; + break; + } + } + } + } + } + } + fclose(fp); + + return strInstName; +} + +RTCString CollectorSolaris::pathToInstName(const char *pcszDevPathName) +{ + char szLink[RTPATH_MAX]; + if (readlink(pcszDevPathName, szLink, sizeof(szLink)) != -1) + { + char *pszStart, *pszEnd; + pszStart = strstr(szLink, "/devices/"); + pszEnd = strrchr(szLink, ':'); + if (pszStart && pszEnd) + { + pszStart += 8; // Skip "/devices" + *pszEnd = '\0'; // Trim partition + return physToInstName(pszStart); + } + } + + return RTCString(pcszDevPathName); +} + +int CollectorSolaris::getDiskListByFs(const char *name, DiskList& listUsage, DiskList& listLoad) +{ + FsMap::iterator it = mFsMap.find(name); + if (it == mFsMap.end()) + return VERR_INVALID_PARAMETER; + + RTCString strName = it->second.substr(0, it->second.find("/")); + if (mZpoolOpen && mZpoolClose && mZpoolGetConfig && !strName.isEmpty()) + { + zpool_handle_t *zh = mZpoolOpen(mZfsLib, strName.c_str()); + if (zh) + { + unsigned int cChildren = 0; + nvlist_t **nvChildren = NULL; + nvlist_t *nvRoot = NULL; + nvlist_t *nvConfig = mZpoolGetConfig(zh, NULL); + if ( !nvlist_lookup_nvlist(nvConfig, ZPOOL_CONFIG_VDEV_TREE, &nvRoot) + && !nvlist_lookup_nvlist_array(nvRoot, ZPOOL_CONFIG_CHILDREN, &nvChildren, &cChildren)) + { + for (unsigned int i = 0; i < cChildren; ++i) + { + uint64_t fHole = 0; + uint64_t fLog = 0; + + nvlist_lookup_uint64(nvChildren[i], ZPOOL_CONFIG_IS_HOLE, &fHole); + nvlist_lookup_uint64(nvChildren[i], ZPOOL_CONFIG_IS_LOG, &fLog); + + if (!fHole && !fLog) + { + char *pszChildName = mZpoolVdevName(mZfsLib, zh, nvChildren[i], _B_FALSE); + Assert(pszChildName); + RTCString strDevPath("/dev/dsk/"); + strDevPath += pszChildName; + char szLink[RTPATH_MAX]; + if (readlink(strDevPath.c_str(), szLink, sizeof(szLink)) != -1) + { + char *pszStart, *pszEnd; + pszStart = strstr(szLink, "/devices/"); + pszEnd = strrchr(szLink, ':'); + if (pszStart && pszEnd) + { + pszStart += 8; // Skip "/devices" + *pszEnd = '\0'; // Trim partition + listUsage.push_back(physToInstName(pszStart)); + } + } + free(pszChildName); + } + } + } + mZpoolClose(zh); + } + } + else + listUsage.push_back(pathToInstName(it->second.c_str())); + listLoad = listUsage; + return VINF_SUCCESS; +} + +void CollectorSolaris::updateFilesystemMap(void) +{ + FILE *fp = fopen("/etc/mnttab", "r"); + if (fp) + { + struct mnttab Entry; + int rc = 0; + resetmnttab(fp); + while ((rc = getmntent(fp, &Entry)) == 0) + mFsMap[Entry.mnt_mountp] = Entry.mnt_special; + fclose(fp); + if (rc != -1) + LogRel(("Error while reading mnttab: %d\n", rc)); + } } } diff --git a/src/VBox/Main/src-server/solaris/USBProxyServiceSolaris.cpp b/src/VBox/Main/src-server/solaris/USBProxyServiceSolaris.cpp index 8261d990..0d6d85e8 100644 --- a/src/VBox/Main/src-server/solaris/USBProxyServiceSolaris.cpp +++ b/src/VBox/Main/src-server/solaris/USBProxyServiceSolaris.cpp @@ -173,7 +173,7 @@ static int solarisWalkDeviceNode(di_node_t Node, void *pvArg) char *pszCompatNames = NULL; int cCompatNames = di_compatible_names(Node, &pszCompatNames); for (int i = 0; i < cCompatNames; i++, pszCompatNames += strlen(pszCompatNames) + 1) - if (!strncmp(pszCompatNames, "usb", 3)) + if (!strncmp(pszCompatNames, RT_STR_TUPLE("usb"))) { fUSBDevice = true; break; @@ -304,7 +304,7 @@ static int solarisWalkDeviceNode(di_node_t Node, void *pvArg) pList->pTail = pList->pHead = pCur; rc = DI_WALK_CONTINUE; - } while(0); + } while (0); di_devfs_path_free(pszDevicePath); if (!fValidDevice) @@ -322,7 +322,7 @@ static USBDEVICESTATE solarisDetermineUSBDeviceState(PUSBDEVICE pDevice, di_node if (!pszDriverName) return USBDEVICESTATE_UNUSED; - if (!strncmp(pszDriverName, VBOXUSB_DRIVER_NAME, sizeof(VBOXUSB_DRIVER_NAME) - 1)) + if (!strncmp(pszDriverName, RT_STR_TUPLE(VBOXUSB_DRIVER_NAME))) return USBDEVICESTATE_HELD_BY_PROXY; NOREF(pDevice); diff --git a/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp b/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp new file mode 100644 index 00000000..1bd72233 --- /dev/null +++ b/src/VBox/Main/src-server/win/HostDnsServiceWin.cpp @@ -0,0 +1,233 @@ +/* -*- indent-tabs-mode: nil; -*- */ +#include <VBox/com/string.h> +#include <VBox/com/ptr.h> + + +#include <iprt/assert.h> +#include <iprt/err.h> + +#include <Windows.h> + +#include <string> +#include <vector> +#include "../HostDnsService.h" + +struct HostDnsServiceWin::Data +{ + HostDnsServiceWin::Data(){} + HKEY hKeyTcpipParameters; +#define DATA_DNS_UPDATE_EVENT 0 +#define DATA_SHUTDOWN_EVENT 1 +#define DATA_MAX_EVENT 2 + HANDLE haDataEvent[DATA_MAX_EVENT]; +}; + +static inline int registerNotification(const HKEY& hKey, HANDLE& hEvent) +{ + LONG lrc = RegNotifyChangeKeyValue(hKey, + TRUE, + REG_NOTIFY_CHANGE_LAST_SET, + hEvent, + TRUE); + AssertMsgReturn(lrc == ERROR_SUCCESS, + ("Failed to register event on the key. Please debug me!"), + VERR_INTERNAL_ERROR); + + return VINF_SUCCESS; +} + +HostDnsServiceWin::HostDnsServiceWin():HostDnsMonitor(true), m(NULL) +{ + m = new Data(); + + m->haDataEvent[DATA_DNS_UPDATE_EVENT] = CreateEvent(NULL, + TRUE, FALSE, NULL); + AssertReleaseMsg(m->haDataEvent[DATA_DNS_UPDATE_EVENT], + ("Failed to create event for DNS event (%d)\n", GetLastError())); + + m->haDataEvent[DATA_SHUTDOWN_EVENT] = CreateEvent(NULL, + TRUE, FALSE, NULL); + AssertReleaseMsg(m->haDataEvent[DATA_SHUTDOWN_EVENT], + ("Failed to create event for Shutdown signal (%d)\n", GetLastError())); + + LONG lrc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, + TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), + 0, KEY_READ|KEY_NOTIFY, &m->hKeyTcpipParameters); + AssertReleaseMsg(lrc == ERROR_SUCCESS, + ("Failed to open Registry Key for read and update notifications (%d)\n", + GetLastError())); +} + + +HostDnsServiceWin::~HostDnsServiceWin() +{ + if (m && !m->hKeyTcpipParameters) + { + RegCloseKey(m->hKeyTcpipParameters); + m->hKeyTcpipParameters = 0; + + CloseHandle(m->haDataEvent[DATA_DNS_UPDATE_EVENT]); + CloseHandle(m->haDataEvent[DATA_SHUTDOWN_EVENT]); + + delete m; + + m = NULL; + } +} + + +HRESULT HostDnsServiceWin::init() +{ + HRESULT hrc = HostDnsMonitor::init(); + AssertComRCReturn(hrc, hrc); + + return updateInfo(); +} + + +void HostDnsServiceWin::monitorThreadShutdown() +{ + SetEvent(m->haDataEvent[DATA_SHUTDOWN_EVENT]); +} + + +int HostDnsServiceWin::monitorWorker() +{ + registerNotification(m->hKeyTcpipParameters, + m->haDataEvent[DATA_DNS_UPDATE_EVENT]); + + monitorThreadInitializationDone(); + + DWORD dwRc; + while (true) + { + dwRc = WaitForMultipleObjects(DATA_MAX_EVENT, + m->haDataEvent, + FALSE, + INFINITE); + AssertMsgReturn(dwRc != WAIT_FAILED, + ("WaitForMultipleObjects failed (%d) to wait! Please debug", + GetLastError()), VERR_INTERNAL_ERROR); + + if ((dwRc - WAIT_OBJECT_0) == DATA_DNS_UPDATE_EVENT) + { + updateInfo(); + notifyAll(); + ResetEvent(m->haDataEvent[DATA_DNS_UPDATE_EVENT]); + registerNotification(m->hKeyTcpipParameters, + m->haDataEvent[DATA_DNS_UPDATE_EVENT]); + + } + else if ((dwRc - WAIT_OBJECT_0) == DATA_SHUTDOWN_EVENT) + { + break; + } + else + { + AssertMsgFailedReturn( + ("WaitForMultipleObjects returns out of bound index %d. Please debug!", + dwRc), + VERR_INTERNAL_ERROR); + } + } + return VINF_SUCCESS; +} + + +HRESULT HostDnsServiceWin::updateInfo() +{ + HRESULT hrc; + DWORD regIndex; + BYTE abDomain[256]; + BYTE abNameServers[256]; + BYTE abSearchList[256]; + + RT_ZERO(abDomain); + RT_ZERO(abNameServers); + RT_ZERO(abSearchList); + + regIndex = 0; + do { + CHAR keyName[256]; + DWORD cbKeyName = sizeof(keyName); + DWORD keyType = 0; + BYTE keyData[1024]; + DWORD cbKeyData = sizeof(keyData); + + hrc = RegEnumValueA(m->hKeyTcpipParameters, regIndex, keyName, &cbKeyName, 0, + &keyType, keyData, &cbKeyData); + if ( hrc == ERROR_SUCCESS + || hrc == ERROR_MORE_DATA) + { + if ( RTStrICmp("Domain", keyName) == 0 + && cbKeyData > 1 + && cbKeyData < sizeof(abDomain)) + memcpy(abDomain, keyData, cbKeyData); + + else if ( RTStrICmp("DhcpDomain", keyName) == 0 + && cbKeyData > 1 + && abDomain[0] == 0 + && cbKeyData < sizeof(abDomain)) + memcpy(abDomain, keyData, cbKeyData); + + else if ( RTStrICmp("NameServer", keyName) == 0 + && cbKeyData > 1 + && cbKeyData < sizeof(abNameServers)) + memcpy(abNameServers, keyData, cbKeyData); + + else if ( RTStrICmp("DhcpNameServer", keyName) == 0 + && cbKeyData > 1 + && abNameServers[0] == 0 + && cbKeyData < sizeof(abNameServers)) + memcpy(abNameServers, keyData, cbKeyData); + + else if ( RTStrICmp("SearchList", keyName) == 0 + && cbKeyData > 1 + && cbKeyData < sizeof(abSearchList)) + memcpy(abSearchList, keyData, cbKeyData); + } + regIndex++; + } while (hrc != ERROR_NO_MORE_ITEMS); + + /* OK, now parse and update DNS structures. */ + /* domain name */ + HostDnsInformation info; + info.domain = (char*)abDomain; + + /* server list */ + strList2List(info.servers, (char *)abNameServers); + /* search list */ + strList2List(info.searchList, (char *)abSearchList); + + HostDnsMonitor::setInfo(info); + + return S_OK; +} + + +void HostDnsServiceWin::strList2List(std::vector<std::string>& lst, char *strLst) +{ + char *next, *current; + char address[512]; + + AssertPtrReturnVoid(strLst); + + if (strlen(strLst) == 0) + return; + + current = strLst; + do { + RT_ZERO(address); + next = RTStrStr(current, " "); + + if (next) + strncpy(address, current, RT_MIN(sizeof(address)-1, next - current)); + else + strcpy(address, current); + + lst.push_back(std::string(address)); + + current = next + 1; + } while(next); + +} diff --git a/src/VBox/Main/src-server/win/HostPowerWin.cpp b/src/VBox/Main/src-server/win/HostPowerWin.cpp index ae806fd7..34826f9d 100644 --- a/src/VBox/Main/src-server/win/HostPowerWin.cpp +++ b/src/VBox/Main/src-server/win/HostPowerWin.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -31,12 +31,12 @@ extern "C" { static WCHAR gachWindowClassName[] = L"VBoxPowerNotifyClass"; -HostPowerServiceWin::HostPowerServiceWin(VirtualBox *aVirtualBox) : HostPowerService(aVirtualBox) +HostPowerServiceWin::HostPowerServiceWin(VirtualBox *aVirtualBox) : HostPowerService(aVirtualBox), mThread(NIL_RTTHREAD) { mHwnd = 0; - int rc = RTThreadCreate (&mThread, HostPowerServiceWin::NotificationThread, this, 65536, - RTTHREADTYPE_GUI, RTTHREADFLAGS_WAITABLE, "MainPower"); + int rc = RTThreadCreate(&mThread, HostPowerServiceWin::NotificationThread, this, 65536, + RTTHREADTYPE_GUI, RTTHREADFLAGS_WAITABLE, "MainPower"); if (RT_FAILURE(rc)) { @@ -55,12 +55,14 @@ HostPowerServiceWin::~HostPowerServiceWin() SetWindowLongPtr(mHwnd, 0, 0); /* Send the quit message and wait for it be processed. */ SendMessage(mHwnd, WM_QUIT, 0, 0); + RTThreadWait(mThread, 5000, NULL); + mThread = NIL_RTTHREAD; } } -DECLCALLBACK(int) HostPowerServiceWin::NotificationThread (RTTHREAD ThreadSelf, void *pInstance) +DECLCALLBACK(int) HostPowerServiceWin::NotificationThread(RTTHREAD ThreadSelf, void *pInstance) { HostPowerServiceWin *pPowerObj = (HostPowerServiceWin *)pInstance; HWND hwnd = 0; @@ -68,7 +70,7 @@ DECLCALLBACK(int) HostPowerServiceWin::NotificationThread (RTTHREAD ThreadSelf, /* Create a window and make it a power event notification handler. */ int rc = VINF_SUCCESS; - HINSTANCE hInstance = (HINSTANCE)GetModuleHandle (NULL); + HINSTANCE hInstance = (HINSTANCE)GetModuleHandle(NULL); /* Register the Window Class. */ WNDCLASS wc; @@ -94,10 +96,10 @@ DECLCALLBACK(int) HostPowerServiceWin::NotificationThread (RTTHREAD ThreadSelf, else { /* Create the window. */ - hwnd = pPowerObj->mHwnd = CreateWindowEx (WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST, - gachWindowClassName, gachWindowClassName, - WS_POPUPWINDOW, - -200, -200, 100, 100, NULL, NULL, hInstance, NULL); + hwnd = pPowerObj->mHwnd = CreateWindowEx(WS_EX_TOOLWINDOW | WS_EX_TRANSPARENT | WS_EX_TOPMOST, + gachWindowClassName, gachWindowClassName, + WS_POPUPWINDOW, + -200, -200, 100, 100, NULL, NULL, hInstance, NULL); if (hwnd == NULL) { @@ -121,11 +123,11 @@ DECLCALLBACK(int) HostPowerServiceWin::NotificationThread (RTTHREAD ThreadSelf, Log(("HostPowerServiceWin::NotificationThread: exit thread\n")); if (hwnd) - DestroyWindow (hwnd); + DestroyWindow(hwnd); if (atomWindowClass != 0) { - UnregisterClass (gachWindowClassName, hInstance); + UnregisterClass(gachWindowClassName, hInstance); atomWindowClass = 0; } @@ -146,11 +148,11 @@ LRESULT CALLBACK HostPowerServiceWin::WndProc(HWND hwnd, UINT msg, WPARAM wParam switch(wParam) { case PBT_APMSUSPEND: - pPowerObj->notify(HostPowerEvent_Suspend); + pPowerObj->notify(Reason_HostSuspend); break; case PBT_APMRESUMEAUTOMATIC: - pPowerObj->notify(HostPowerEvent_Resume); + pPowerObj->notify(Reason_HostResume); break; case PBT_APMPOWERSTATUSCHANGE: @@ -177,27 +179,27 @@ LRESULT CALLBACK HostPowerServiceWin::WndProc(HWND hwnd, UINT msg, WPARAM wParam if ( rc == 0 /* STATUS_SUCCESS */ && BatteryState.EstimatedTime < 60*5) { - pPowerObj->notify(HostPowerEvent_BatteryLow); + pPowerObj->notify(Reason_HostBatteryLow); } } else /* If the machine has less than 5% battery left (and is not connected to the AC), then we should save the state. */ if (SystemPowerStatus.BatteryFlag == 4 /* critical battery status; less than 5% */) { - pPowerObj->notify(HostPowerEvent_BatteryLow); + pPowerObj->notify(Reason_HostBatteryLow); } } } break; } default: - return DefWindowProc (hwnd, msg, wParam, lParam); + return DefWindowProc(hwnd, msg, wParam, lParam); } } return TRUE; } default: - return DefWindowProc (hwnd, msg, wParam, lParam); + return DefWindowProc(hwnd, msg, wParam, lParam); } } diff --git a/src/VBox/Main/src-server/win/NetIf-win.cpp b/src/VBox/Main/src-server/win/NetIf-win.cpp index cce820da..3b86dabd 100644 --- a/src/VBox/Main/src-server/win/NetIf-win.cpp +++ b/src/VBox/Main/src-server/win/NetIf-win.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2008-2010 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -954,7 +954,7 @@ static int vboxNetWinAddComponent(std::list<ComObjPtr<HostNetworkInterface> > * if (hr == S_OK) { NETIFINFO Info; - memset(&Info, 0, sizeof(Info)); + RT_ZERO(Info); Info.Uuid = *(Guid(IfGuid).raw()); rc = collectNetIfInfo(name, Guid(IfGuid), &Info, iDefaultInterface); if (RT_FAILURE(rc)) @@ -1082,6 +1082,33 @@ int NetIfGetConfigByName(PNETIFINFO) return VERR_NOT_IMPLEMENTED; } +/** + * Obtain the current state of the interface. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param penmState Where to store the retrieved state. + */ +int NetIfGetState(const char *pcszIfName, NETIFSTATUS *penmState) +{ + return VERR_NOT_IMPLEMENTED; +} + +/** + * Retrieve the physical link speed in megabits per second. If the interface is + * not up or otherwise unavailable the zero speed is returned. + * + * @returns VBox status code. + * + * @param pcszIfName Interface name. + * @param puMbits Where to store the link speed. + */ +int NetIfGetLinkSpeed(const char * /*pcszIfName*/, uint32_t * /*puMbits*/) +{ + return VERR_NOT_IMPLEMENTED; +} + int NetIfCreateHostOnlyNetworkInterface(VirtualBox *pVBox, IHostNetworkInterface **aHostNetworkInterface, IProgress **aProgress, @@ -1485,7 +1512,7 @@ int NetIfList(std::list<ComObjPtr<HostNetworkInterface> > &list) { hr = pBp->EnumBindingInterfaces(&pEnumBi); Assert(hr == S_OK); - if ( hr == S_OK ) + if (hr == S_OK) { hr = pEnumBi->Reset(); Assert(hr == S_OK); @@ -1493,7 +1520,7 @@ int NetIfList(std::list<ComObjPtr<HostNetworkInterface> > &list) { while ((hr = pEnumBi->Next(1, &pBi, NULL)) == S_OK) { - hr = pBi->GetLowerComponent( &pMpNcc ); + hr = pBi->GetLowerComponent(&pMpNcc); Assert(hr == S_OK); if (hr == S_OK) { @@ -1527,7 +1554,7 @@ int NetIfList(std::list<ComObjPtr<HostNetworkInterface> > &list) } else { - LogRel(("failed to get the sun_VBoxNetFlt component, error (0x%x)", hr)); + LogRel(("failed to get the sun_VBoxNetFlt component, error (0x%x)\n", hr)); } VBoxNetCfgWinReleaseINetCfg(pNc, FALSE); diff --git a/src/VBox/Main/src-server/win/PerformanceWin.cpp b/src/VBox/Main/src-server/win/PerformanceWin.cpp index 48643c6d..74c2bf13 100644 --- a/src/VBox/Main/src-server/win/PerformanceWin.cpp +++ b/src/VBox/Main/src-server/win/PerformanceWin.cpp @@ -6,7 +6,7 @@ */ /* - * Copyright (C) 2008 Oracle Corporation + * Copyright (C) 2008-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -33,8 +33,10 @@ extern "C" { } #include <iprt/err.h> +#include <iprt/ldr.h> #include <iprt/mp.h> #include <iprt/mem.h> +#include <iprt/system.h> #include <map> @@ -61,6 +63,7 @@ public: virtual int getRawHostCpuLoad(uint64_t *user, uint64_t *kernel, uint64_t *idle); virtual int getRawProcessCpuLoad(RTPROCESS process, uint64_t *user, uint64_t *kernel, uint64_t *total); + private: struct VMProcessStats { @@ -72,21 +75,20 @@ private: typedef std::map<RTPROCESS, VMProcessStats> VMProcessMap; - VMProcessMap mProcessStats; + VMProcessMap mProcessStats; - typedef BOOL (WINAPI *PFNGST)( - LPFILETIME lpIdleTime, - LPFILETIME lpKernelTime, - LPFILETIME lpUserTime); - typedef NTSTATUS (WINAPI *PFNNQSI)( - SYSTEM_INFORMATION_CLASS SystemInformationClass, - PVOID SystemInformation, - ULONG SystemInformationLength, - PULONG ReturnLength); + typedef BOOL (WINAPI *PFNGST)(LPFILETIME lpIdleTime, + LPFILETIME lpKernelTime, + LPFILETIME lpUserTime); + typedef NTSTATUS (WINAPI *PFNNQSI)(SYSTEM_INFORMATION_CLASS SystemInformationClass, + PVOID SystemInformation, + ULONG SystemInformationLength, + PULONG ReturnLength); PFNGST mpfnGetSystemTimes; PFNNQSI mpfnNtQuerySystemInformation; - HMODULE mhNtDll; + + ULONG totalRAM; }; CollectorHAL *createHAL() @@ -94,35 +96,29 @@ CollectorHAL *createHAL() return new CollectorWin(); } -CollectorWin::CollectorWin() : CollectorHAL(), mhNtDll(0) +CollectorWin::CollectorWin() : CollectorHAL(), mpfnNtQuerySystemInformation(NULL) { - mpfnGetSystemTimes = (PFNGST)GetProcAddress( - GetModuleHandle(TEXT("kernel32.dll")), - "GetSystemTimes"); + /* Note! Both kernel32.dll and ntdll.dll can be assumed to always be present. */ + mpfnGetSystemTimes = (PFNGST)RTLdrGetSystemSymbol("kernel32.dll", "GetSystemTimes"); if (!mpfnGetSystemTimes) { /* Fall back to deprecated NtQuerySystemInformation */ - if (!(mhNtDll = LoadLibrary(TEXT("ntdll.dll")))) - { - LogRel(("Failed to load NTDLL.DLL with error 0x%x. GetSystemTimes() is" - " not available either. CPU and VM metrics will not be collected.\n", - GetLastError())); - mpfnNtQuerySystemInformation = 0; - } - else if (!(mpfnNtQuerySystemInformation = (PFNNQSI)GetProcAddress(mhNtDll, - "NtQuerySystemInformation"))) - { - LogRel(("Neither GetSystemTimes() nor NtQuerySystemInformation() is" - " not available. CPU and VM metrics will not be collected.\n")); - mpfnNtQuerySystemInformation = 0; - } + mpfnNtQuerySystemInformation = (PFNNQSI)RTLdrGetSystemSymbol("ntdll.dll", "NtQuerySystemInformation"); + if (!mpfnNtQuerySystemInformation) + LogRel(("Warning! Neither GetSystemTimes() nor NtQuerySystemInformation() is not available.\n" + " CPU and VM metrics will not be collected! (lasterr %u)\n", GetLastError())); } + + uint64_t cb; + int rc = RTSystemQueryTotalRam(&cb); + if (RT_FAILURE(rc)) + totalRAM = 0; + else + totalRAM = (ULONG)(cb / 1024); } CollectorWin::~CollectorWin() { - if (mhNtDll) - FreeLibrary(mhNtDll); } #define FILETTIME_TO_100NS(ft) (((uint64_t)ft.dwHighDateTime << 32) + ft.dwLowDateTime) @@ -278,7 +274,7 @@ int CollectorWin::getHostCpuMHz(ULONG *mhz) return VERR_NO_MEMORY; LONG ns = CallNtPowerInformation(ProcessorInformation, NULL, 0, ppi, - nProcessors * sizeof(PROCESSOR_POWER_INFORMATION)); + nProcessors * sizeof(PROCESSOR_POWER_INFORMATION)); if (ns) { Log(("CallNtPowerInformation() -> %x\n", ns)); @@ -300,19 +296,16 @@ int CollectorWin::getHostCpuMHz(ULONG *mhz) int CollectorWin::getHostMemoryUsage(ULONG *total, ULONG *used, ULONG *available) { - MEMORYSTATUSEX mstat; - - mstat.dwLength = sizeof(mstat); - if (GlobalMemoryStatusEx(&mstat)) + AssertReturn(totalRAM, VERR_INTERNAL_ERROR); + uint64_t cb; + int rc = RTSystemQueryAvailableRam(&cb); + if (RT_SUCCESS(rc)) { - *total = (ULONG)( mstat.ullTotalPhys / 1024 ); - *available = (ULONG)( mstat.ullAvailPhys / 1024 ); + *total = totalRAM; + *available = (ULONG)(cb / 1024); *used = *total - *available; } - else - return RTErrConvertFromWin32(GetLastError()); - - return VINF_SUCCESS; + return rc; } int CollectorWin::getProcessCpuLoad(RTPROCESS process, ULONG *user, ULONG *kernel) @@ -348,9 +341,4 @@ int CollectorWin::getProcessMemoryUsage(RTPROCESS process, ULONG *used) return VINF_SUCCESS; } -int getDiskListByFs(const char *name, DiskList& list) -{ - return VERR_NOT_IMPLEMENTED; -} - } diff --git a/src/VBox/Main/src-server/win/svchlp.cpp b/src/VBox/Main/src-server/win/svchlp.cpp index 2191f3fa..a3c07ff6 100644 --- a/src/VBox/Main/src-server/win/svchlp.cpp +++ b/src/VBox/Main/src-server/win/svchlp.cpp @@ -3,7 +3,7 @@ */ /* - * Copyright (C) 2006-2010 Oracle Corporation + * Copyright (C) 2006-2012 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/win/svchlp.h b/src/VBox/Main/src-server/win/svchlp.h index 0e8b8b80..a5df8dba 100644 --- a/src/VBox/Main/src-server/win/svchlp.h +++ b/src/VBox/Main/src-server/win/svchlp.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/win/svcmain.cpp b/src/VBox/Main/src-server/win/svcmain.cpp index 111c2dd4..5e2c251b 100644 --- a/src/VBox/Main/src-server/win/svcmain.cpp +++ b/src/VBox/Main/src-server/win/svcmain.cpp @@ -327,19 +327,26 @@ int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE /*hPrevInstance*/, LPSTR /*lpC * registering/unregistering or calling the helper functionality. */ if (fRun) { + /** @todo Merge this code with server.cpp (use Logging.cpp?). */ + char szLogFile[RTPATH_MAX]; if (!pszLogFile) { - char szLogFile[RTPATH_MAX]; vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile)); if (RT_SUCCESS(vrc)) vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log"); - if (RT_SUCCESS(vrc)) - pszLogFile = RTStrDup(szLogFile); } + else + { + if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile)) + vrc = VERR_NO_MEMORY; + } + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc); + char szError[RTPATH_MAX + 128]; - vrc = com::VBoxLogRelCreate("COM Server", pszLogFile, + vrc = com::VBoxLogRelCreate("COM Server", szLogFile, RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG, - "all", "VBOXSVC_RELEASE_LOG", + VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG", RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize, szError, sizeof(szError)); diff --git a/src/VBox/Main/src-server/xpcom/server.cpp b/src/VBox/Main/src-server/xpcom/server.cpp index 6c48fcba..24ff15a8 100644 --- a/src/VBox/Main/src-server/xpcom/server.cpp +++ b/src/VBox/Main/src-server/xpcom/server.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2004-2012 Oracle Corporation + * Copyright (C) 2004-2013 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -20,10 +20,6 @@ #include <nsIComponentRegistrar.h> -#ifdef XPCOM_GLUE -# include <nsXPCOMGlue.h> -#endif - #include <nsEventQueueUtils.h> #include <nsGenericFactory.h> @@ -66,7 +62,7 @@ #include "AudioAdapterImpl.h" #include "BandwidthControlImpl.h" #include "BandwidthGroupImpl.h" -#include "DHCPServerRunner.h" +#include "NetworkServiceRunner.h" #include "DHCPServerImpl.h" #include "GuestOSTypeImpl.h" #include "HostImpl.h" @@ -77,7 +73,6 @@ #include "NATEngineImpl.h" #include "NetworkAdapterImpl.h" #include "ParallelPortImpl.h" -#include "ProgressCombinedImpl.h" #include "ProgressProxyImpl.h" #include "SerialPortImpl.h" #include "SharedFolderImpl.h" @@ -85,6 +80,7 @@ #include "StorageControllerImpl.h" #include "SystemPropertiesImpl.h" #include "USBControllerImpl.h" +#include "USBDeviceFiltersImpl.h" #include "VFSExplorerImpl.h" #include "VirtualBoxImpl.h" #include "VRDEServerImpl.h" @@ -96,6 +92,8 @@ #ifdef VBOX_WITH_EXTPACK # include "ExtPackManagerImpl.h" #endif +# include "NATNetworkImpl.h" + /* implement nsISupports parts of our objects with support for nsIClassInfo */ @@ -126,18 +124,12 @@ NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Snapshot, ISnapshot) NS_DECL_CLASSINFO(Medium) NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Medium, IMedium) -NS_DECL_CLASSINFO(MediumFormat) -NS_IMPL_THREADSAFE_ISUPPORTS1_CI(MediumFormat, IMediumFormat) - NS_DECL_CLASSINFO(MediumAttachment) NS_IMPL_THREADSAFE_ISUPPORTS1_CI(MediumAttachment, IMediumAttachment) NS_DECL_CLASSINFO(Progress) NS_IMPL_THREADSAFE_ISUPPORTS1_CI(Progress, IProgress) -NS_DECL_CLASSINFO(CombinedProgress) -NS_IMPL_THREADSAFE_ISUPPORTS1_CI(CombinedProgress, IProgress) - NS_DECL_CLASSINFO(ProgressProxy) NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ProgressProxy, IProgress) @@ -156,6 +148,9 @@ NS_IMPL_THREADSAFE_ISUPPORTS1_CI(HostNetworkInterface, IHostNetworkInterface) NS_DECL_CLASSINFO(DHCPServer) NS_IMPL_THREADSAFE_ISUPPORTS1_CI(DHCPServer, IDHCPServer) +NS_DECL_CLASSINFO(NATNetwork) +NS_IMPL_THREADSAFE_ISUPPORTS1_CI(NATNetwork, INATNetwork) + NS_DECL_CLASSINFO(GuestOSType) NS_IMPL_THREADSAFE_ISUPPORTS1_CI(GuestOSType, IGuestOSType) @@ -175,6 +170,9 @@ NS_IMPL_THREADSAFE_ISUPPORTS1_CI(ParallelPort, IParallelPort) NS_DECL_CLASSINFO(USBController) NS_IMPL_THREADSAFE_ISUPPORTS1_CI(USBController, IUSBController) +NS_DECL_CLASSINFO(USBDeviceFilters) +NS_IMPL_THREADSAFE_ISUPPORTS1_CI(USBDeviceFilters, IUSBDeviceFilters) + NS_DECL_CLASSINFO(StorageController) NS_IMPL_THREADSAFE_ISUPPORTS1_CI(StorageController, IStorageController) @@ -881,23 +879,26 @@ int main(int argc, char **argv) nsresult rc; + /** @todo Merge this code with svcmain.cpp (use Logging.cpp?). */ + char szLogFile[RTPATH_MAX]; if (!pszLogFile) { - char szLogFile[RTPATH_MAX] = ""; vrc = com::GetVBoxUserHomeDirectory(szLogFile, sizeof(szLogFile)); - if (vrc == VERR_ACCESS_DENIED) - return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to open global settings directory '%s'", szLogFile); if (RT_SUCCESS(vrc)) vrc = RTPathAppend(szLogFile, sizeof(szLogFile), "VBoxSVC.log"); - if (RT_SUCCESS(vrc)) - pszLogFile = RTStrDup(szLogFile); - if (RT_FAILURE(vrc)) - return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to determine release log file (%Rrc)", vrc); } + else + { + if (!RTStrPrintf(szLogFile, sizeof(szLogFile), "%s", pszLogFile)) + vrc = VERR_NO_MEMORY; + } + if (RT_FAILURE(vrc)) + return RTMsgErrorExit(RTEXITCODE_FAILURE, "failed to create logging file name, rc=%Rrc", vrc); + char szError[RTPATH_MAX + 128]; - vrc = com::VBoxLogRelCreate("XPCOM Server", pszLogFile, + vrc = com::VBoxLogRelCreate("XPCOM Server", szLogFile, RTLOGFLAGS_PREFIX_THREAD | RTLOGFLAGS_PREFIX_TIME_PROG, - "all", "VBOXSVC_RELEASE_LOG", + VBOXSVC_LOG_DEFAULT, "VBOXSVC_RELEASE_LOG", RTLOGDEST_FILE, UINT32_MAX /* cMaxEntriesPerGroup */, cHistory, uHistoryFileTime, uHistoryFileSize, szError, sizeof(szError)); diff --git a/src/VBox/Main/src-server/xpcom/server.h b/src/VBox/Main/src-server/xpcom/server.h index f14977af..4fbaa231 100644 --- a/src/VBox/Main/src-server/xpcom/server.h +++ b/src/VBox/Main/src-server/xpcom/server.h @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2007 Oracle Corporation + * Copyright (C) 2006-2010 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; diff --git a/src/VBox/Main/src-server/xpcom/server_module.cpp b/src/VBox/Main/src-server/xpcom/server_module.cpp index 0647016c..11cf239e 100644 --- a/src/VBox/Main/src-server/xpcom/server_module.cpp +++ b/src/VBox/Main/src-server/xpcom/server_module.cpp @@ -4,7 +4,7 @@ */ /* - * Copyright (C) 2006-2011 Oracle Corporation + * Copyright (C) 2006-2014 Oracle Corporation * * This file is part of VirtualBox Open Source Edition (OSE), as * available from http://www.virtualbox.org. This file is free software; @@ -53,10 +53,9 @@ #include <iprt/path.h> #include <iprt/process.h> #include <iprt/env.h> +#include <iprt/string.h> #include <iprt/thread.h> -#include <string.h> - #if defined(RT_OS_SOLARIS) # include <sys/systeminfo.h> #endif @@ -92,8 +91,8 @@ static bool IsVBoxSVCPathSet = false; * in sync with macros used for VirtualBox in server.cpp for the same purpose. */ -NS_DECL_CLASSINFO (VirtualBox) -NS_IMPL_CI_INTERFACE_GETTER1 (VirtualBox, IVirtualBox) +NS_DECL_CLASSINFO(VirtualBox) +NS_IMPL_CI_INTERFACE_GETTER1(VirtualBox, IVirtualBox) static nsresult vboxsvcSpawnDaemon(void) { @@ -137,7 +136,7 @@ static nsresult vboxsvcSpawnDaemon(void) writable = nsnull; char msg[10]; - memset(msg, '\0', sizeof(msg)); + RT_ZERO(msg); if ( PR_Read(readable, msg, sizeof(msg)-1) != 5 || strcmp(msg, "READY")) { @@ -168,8 +167,8 @@ end: * VirtualBox component defined on the server. */ static NS_IMETHODIMP -VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, - void **aResult) +VirtualBoxConstructor(nsISupports *aOuter, REFNSIID aIID, + void **aResult) { LogFlowFuncEnter(); @@ -189,25 +188,25 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, { /* Get the directory containing XPCOM components -- the VBoxSVC * executable is expected in the parent directory. */ - nsCOMPtr <nsIProperties> dirServ = do_GetService (NS_DIRECTORY_SERVICE_CONTRACTID, &rc); + nsCOMPtr<nsIProperties> dirServ = do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rc); if (NS_SUCCEEDED(rc)) { - nsCOMPtr <nsIFile> componentDir; - rc = dirServ->Get (NS_XPCOM_COMPONENT_DIR, - NS_GET_IID (nsIFile), getter_AddRefs (componentDir)); + nsCOMPtr<nsIFile> componentDir; + rc = dirServ->Get(NS_XPCOM_COMPONENT_DIR, + NS_GET_IID(nsIFile), getter_AddRefs(componentDir)); if (NS_SUCCEEDED(rc)) { nsCAutoString path; - componentDir->GetNativePath (path); + componentDir->GetNativePath(path); - LogFlowFunc (("component directory = \"%s\"\n", path.get())); - AssertBreakStmt (path.Length() + strlen (VBoxSVC_exe) < RTPATH_MAX, - rc = NS_ERROR_FAILURE); + LogFlowFunc(("component directory = \"%s\"\n", path.get())); + AssertBreakStmt(path.Length() + strlen(VBoxSVC_exe) < RTPATH_MAX, + rc = NS_ERROR_FAILURE); #if defined(RT_OS_SOLARIS) && defined(VBOX_WITH_HARDENING) char achKernArch[128]; - int cbKernArch = sysinfo (SI_ARCHITECTURE_K, achKernArch, sizeof(achKernArch)); + int cbKernArch = sysinfo(SI_ARCHITECTURE_K, achKernArch, sizeof(achKernArch)); if (cbKernArch > 0) { sprintf(VBoxSVCPath, "/opt/VirtualBox/%s%s", achKernArch, VBoxSVC_exe); @@ -216,9 +215,9 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, else rc = NS_ERROR_UNEXPECTED; #else - strcpy (VBoxSVCPath, path.get()); - RTPathStripFilename (VBoxSVCPath); - strcat (VBoxSVCPath, VBoxSVC_exe); + strcpy(VBoxSVCPath, path.get()); + RTPathStripFilename(VBoxSVCPath); + strcat(VBoxSVCPath, VBoxSVC_exe); IsVBoxSVCPathSet = true; #endif @@ -228,7 +227,7 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, break; } - nsCOMPtr <ipcIService> ipcServ = do_GetService (IPC_SERVICE_CONTRACTID, &rc); + nsCOMPtr<ipcIService> ipcServ = do_GetService(IPC_SERVICE_CONTRACTID, &rc); if (NS_FAILED(rc)) break; @@ -239,13 +238,13 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, do { - LogFlowFunc (("Resolving server name \"%s\"...\n", VBOXSVC_IPC_NAME)); + LogFlowFunc(("Resolving server name \"%s\"...\n", VBOXSVC_IPC_NAME)); PRUint32 serverID = 0; - rc = ipcServ->ResolveClientName (VBOXSVC_IPC_NAME, &serverID); + rc = ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID); if (NS_FAILED(rc)) { - LogFlowFunc (("Starting server \"%s\"...\n", VBoxSVCPath)); + LogFlowFunc(("Starting server \"%s\"...\n", VBoxSVCPath)); startedOnce = true; @@ -256,8 +255,8 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, /* wait for the server process to establish a connection */ do { - RTThreadSleep (VBoxSVC_WaitSlice); - rc = ipcServ->ResolveClientName (VBOXSVC_IPC_NAME, &serverID); + RTThreadSleep(VBoxSVC_WaitSlice); + rc = ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID); if (NS_SUCCEEDED(rc)) break; if (timeLeft <= VBoxSVC_WaitSlice) @@ -276,20 +275,20 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, } } - LogFlowFunc (("Connecting to server (ID=%d)...\n", serverID)); + LogFlowFunc(("Connecting to server (ID=%d)...\n", serverID)); - nsCOMPtr <ipcIDConnectService> dconServ = - do_GetService (IPC_DCONNECTSERVICE_CONTRACTID, &rc); + nsCOMPtr<ipcIDConnectService> dconServ = + do_GetService(IPC_DCONNECTSERVICE_CONTRACTID, &rc); if (NS_FAILED(rc)) break; - rc = dconServ->CreateInstance (serverID, - CLSID_VirtualBox, - aIID, aResult); + rc = dconServ->CreateInstance(serverID, + CLSID_VirtualBox, + aIID, aResult); if (NS_SUCCEEDED(rc)) break; - LogFlowFunc (("Failed to connect (rc=%Rhrc (%#08x))\n", rc, rc)); + LogFlowFunc(("Failed to connect (rc=%Rhrc (%#08x))\n", rc, rc)); /* It's possible that the server gets shut down after we * successfully resolve the server name but before it @@ -298,12 +297,11 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, if (!startedOnce) { nsresult rc2 = - ipcServ->ResolveClientName (VBOXSVC_IPC_NAME, &serverID); + ipcServ->ResolveClientName(VBOXSVC_IPC_NAME, &serverID); if (NS_SUCCEEDED(rc2)) break; - LogFlowFunc (("Server seems to have terminated before " - "receiving our request. Will try again.\n")); + LogFlowFunc(("Server seems to have terminated before receiving our request. Will try again.\n")); } else break; @@ -312,7 +310,7 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, } while (0); - LogFlowFunc (("rc=%Rhrc (%#08x), vrc=%Rrc\n", rc, rc, vrc)); + LogFlowFunc(("rc=%Rhrc (%#08x), vrc=%Rrc\n", rc, rc, vrc)); LogFlowFuncLeave(); return rc; @@ -331,19 +329,19 @@ VirtualBoxConstructor (nsISupports *aOuter, REFNSIID aIID, * @return */ static NS_IMETHODIMP -VirtualBoxRegistration (nsIComponentManager *aCompMgr, - nsIFile *aPath, - const char *aLoaderStr, - const char *aType, - const nsModuleComponentInfo *aInfo) +VirtualBoxRegistration(nsIComponentManager *aCompMgr, + nsIFile *aPath, + const char *aLoaderStr, + const char *aType, + const nsModuleComponentInfo *aInfo) { nsCAutoString modulePath; - aPath->GetNativePath (modulePath); + aPath->GetNativePath(modulePath); nsCAutoString moduleTarget; - aPath->GetNativeTarget (moduleTarget); + aPath->GetNativeTarget(moduleTarget); - LogFlowFunc (("aPath=%s, aTarget=%s, aLoaderStr=%s, aType=%s\n", - modulePath.get(), moduleTarget.get(), aLoaderStr, aType)); + LogFlowFunc(("aPath=%s, aTarget=%s, aLoaderStr=%s, aType=%s\n", + modulePath.get(), moduleTarget.get(), aLoaderStr, aType)); nsresult rc = NS_OK; @@ -372,4 +370,4 @@ static const nsModuleComponentInfo components[] = } }; -NS_IMPL_NSGETMODULE (VirtualBox_Server_Module, components) +NS_IMPL_NSGETMODULE(VirtualBox_Server_Module, components) |