Settings.cpp revision 45893a4ff027d3d8e70ee4546c05718ba2eab3d0
/** @file
* Settings File Manipulation API.
*
* Two classes, MainConfigFile and MachineConfigFile, represent the VirtualBox.xml and
* machine XML files. They share a common ancestor class, ConfigFileBase, which shares
* functionality such as talking to the XML back-end classes and settings version management.
*
* Rules for introducing new settings: If an element or attribute is introduced that was not
* present before VirtualBox 3.1, then settings version checks need to be introduced. The
* settings version for VirtualBox 3.1 is 1.9; see the SettingsVersion enumeration in
* src/VBox/Main/idl/VirtualBox.xidl for details about which version was used when.
*
* The settings versions checks are necessary because VirtualBox 3.1 no longer automatically
* converts XML settings files but only if necessary, that is, if settings are present that
* the old format does not support. If we write an element or attribute to a settings file
* of an older version, then an old VirtualBox (before 3.1) will attempt to validate it
* with XML schema, and that will certainly fail.
*
* So, to introduce a new setting:
*
* 1) Make sure the constructor of corresponding settings structure has a proper default.
*
* 2) In the settings reader method, try to read the setting; if it's there, great, if not,
* the default value will have been set by the constructor.
*
* 3) In the settings writer method, write the setting _only_ if the current settings
* version (stored in m->sv) is high enough. That is, for VirtualBox 3.1, write it
* only if (m->sv >= SettingsVersion_v1_9).
*
* 4) In MachineConfigFile::bumpSettingsVersionIfNeeded(), check if the new setting has
* a non-default value (i.e. that differs from the constructor). If so, bump the
* settings version to the current version so the settings writer (3) can write out
* the non-default value properly.
*
* So far a corresponding method for MainConfigFile has not been necessary since there
* have been no incompatible changes yet.
*/
/*
* Copyright (C) 2007-2009 Sun Microsystems, Inc.
*
* This file is part of VirtualBox Open Source Edition (OSE), as
* available from http://www.virtualbox.org. This file is free software;
* 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.
*
* Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
* Clara, CA 95054 USA or visit http://www.sun.com if you need
* additional information or have any questions.
*/
#include "VBox/settings.h"
// generated header
#include "SchemaDefs.h"
#include "Logging.h"
using namespace com;
using namespace settings;
////////////////////////////////////////////////////////////////////////////////
//
// Defines
//
////////////////////////////////////////////////////////////////////////////////
/** VirtualBox XML settings namespace */
/** VirtualBox XML settings version number substring ("x.y") */
#define VBOX_XML_VERSION "1.9"
/** VirtualBox XML settings version platform substring */
#if defined (RT_OS_DARWIN)
# define VBOX_XML_PLATFORM "macosx"
#elif defined (RT_OS_FREEBSD)
# define VBOX_XML_PLATFORM "freebsd"
#elif defined (RT_OS_LINUX)
# define VBOX_XML_PLATFORM "linux"
#elif defined (RT_OS_NETBSD)
# define VBOX_XML_PLATFORM "netbsd"
#elif defined (RT_OS_OPENBSD)
# define VBOX_XML_PLATFORM "openbsd"
# define VBOX_XML_PLATFORM "os2"
#elif defined (RT_OS_SOLARIS)
# define VBOX_XML_PLATFORM "solaris"
#elif defined (RT_OS_WINDOWS)
# define VBOX_XML_PLATFORM "windows"
#else
#endif
/** VirtualBox XML settings full version string ("x.y-platform") */
////////////////////////////////////////////////////////////////////////////////
//
// Internal data
//
////////////////////////////////////////////////////////////////////////////////
/**
* Opaque data structore for ConfigFileBase (only declared
* in header, defined only here).
*/
struct ConfigFileBase::Data
{
Data()
{}
~Data()
{
cleanup();
}
bool fFileExists;
// or SettingsVersion_Null if none
void cleanup()
{
if (pDoc)
{
delete pDoc;
}
if (pParser)
{
delete pParser;
}
}
};
/**
* Private exception class (not in the header file) that makes
* throwing xml::LogicError instances easier. That class is public
* and should be caught by client code.
*/
{
public:
const char *pcszFormat, ...)
: xml::LogicError()
{
if (pNode)
}
};
////////////////////////////////////////////////////////////////////////////////
//
// ConfigFileBase
//
////////////////////////////////////////////////////////////////////////////////
/**
* Constructor. Allocates the XML internals.
* @param strFilename
*/
: m(new Data)
{
m->fFileExists = false;
if (pstrFilename)
{
m->strFilename = *pstrFilename;
*m->pDoc);
m->fFileExists = true;
throw ConfigFileError(this, NULL, N_("Root element in VirtualBox settings files must be \"VirtualBox\"."));
LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
// parse settings version; allow future versions but fail if file is older than 1.6
m->sv = SettingsVersion_Null;
{
char c;
while ( (c = *pcsz)
&& RT_C_IS_DIGIT(c)
)
{
++pcsz;
}
if (*pcsz++ == '.')
{
while ( (c = *pcsz)
&& RT_C_IS_DIGIT(c)
)
{
++pcsz;
}
}
if (ulMajor == 1)
{
if (ulMinor == 3)
m->sv = SettingsVersion_v1_3;
else if (ulMinor == 4)
m->sv = SettingsVersion_v1_4;
else if (ulMinor == 5)
m->sv = SettingsVersion_v1_5;
else if (ulMinor == 6)
m->sv = SettingsVersion_v1_6;
else if (ulMinor == 7)
m->sv = SettingsVersion_v1_7;
else if (ulMinor == 8)
m->sv = SettingsVersion_v1_8;
else if (ulMinor == 9)
m->sv = SettingsVersion_v1_9;
else if (ulMinor > 9)
m->sv = SettingsVersion_Future;
}
else if (ulMajor > 1)
m->sv = SettingsVersion_Future;
}
if (m->sv == SettingsVersion_Null)
throw ConfigFileError(this, m->pelmRoot, N_("Cannot handle settings version '%s'"), m->strSettingsVersionFull.c_str());
// remember the settings version we read in case it gets upgraded later,
// so we know when to make backups
}
else
{
m->sv = SettingsVersion_v1_9;
}
}
/**
* Clean up.
*/
{
if (m)
{
delete m;
m = NULL;
}
}
/**
* Helper function that parses a UUID in string form into
* a com::Guid item. Since that uses an IPRT function which
* does not accept "{}" characters around the UUID string,
* we handle that here. Throws on errors.
* @param guid
* @param strUUID
*/
{
// {5f102a55-a51b-48e3-b45a-b28d33469488}
// 01234567890123456789012345678901234567
// 1 2 3
if ( (strUUID[0] == '{')
)
else
}
/**
* Parses the given string in str and attempts to treat it as an ISO
* @param timestamp
* @param str
*/
{
// yyyy-mm-ddThh:mm:ss
// "2009-07-10T11:54:03Z"
// 01234567890123456789
// 1
{
// timezone must either be unspecified or 'Z' for UTC
if ( (pcsz[19])
)
throw ConfigFileError(this, NULL, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
)
{
int rc;
// could theoretically be negative but let's assume that nobody
// created virtual machines before the Christian era
)
{
0,
0,
0,
if (RTTimeNormalize(&time))
return;
}
throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
}
throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
}
}
/**
* Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
* @param stamp
* @return
*/
{
return Utf8StrFmt("%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
}
/**
* Helper to create a string for a GUID.
* @param guid
* @return
*/
{
return str;
}
/**
* Helper method to read in an ExtraData subtree and stores its contents
* in the given map of extradata items. Used for both main and machine
* extradata (MainConfigFile and MachineConfigFile).
* @param elmExtraData
* @param map
*/
{
{
{
// <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
)
else
throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing"));
}
}
}
/**
* Reads <USBDeviceFilter> entries from under the given elmDeviceFilters node and
* stores them in the given linklist. This is in ConfigFileBase because it's used
* from both MainConfigFile (for host filters) and MachineConfigFile (for machine
* filters).
* @param elmDeviceFilters
* @param ll
*/
{
{
)
{
// the next 2 are irrelevant for host USB objects
// action is only used with host USB objects
{
if (strAction == "Ignore")
else if (strAction == "Hold")
else
throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str());
}
}
}
}
/**
* Creates a new stub xml::Document in the m->pDoc member with the
* root "VirtualBox" element set up. This is used by both
* MainConfigFile and MachineConfigFile at the beginning of writing
* out their XML.
*
* Before calling this, it is the responsibility of the caller to
* set the "sv" member to the required settings version that is to
* be written. For newly created files, the settings version will be
* the latest (1.9); for files read in from disk earlier, it will be
* the settings version indicated in the file. However, this method
* will silently make sure that the settings version is always
* at least 1.7 and change it if necessary, since there is no write
* support for earlier settings versions.
*/
void ConfigFileBase::createStubDocument()
{
const char *pcszVersion = NULL;
switch (m->sv)
{
case SettingsVersion_v1_8:
pcszVersion = "1.8";
break;
case SettingsVersion_v1_9:
case SettingsVersion_Future: // can be set if this code runs on XML files that were created by a future version of VBox;
// in that case, downgrade to current version when writing since we can't write future versions...
pcszVersion = "1.9";
m->sv = SettingsVersion_v1_9;
break;
default:
// silently upgrade if this is less than 1.7 because that's the oldest we can write
pcszVersion = "1.7";
m->sv = SettingsVersion_v1_7;
break;
}
VBOX_XML_PLATFORM)); // e.g. "linux"
// since this gets called before the XML document is actually written out
// do this, this is where we must check whether we're upgrading the settings
// version and need to make a backup, so the user can go back to an earlier
// VirtualBox version and recover his old settings files.
)
{
// compose new filename: strip off trailing ".xml"
// and append something likd "-1.3-linux.xml"
0); // no RTFILEMOVE_FLAGS_REPLACE
// do this only once
m->svRead = SettingsVersion_Null;
}
}
/**
* Creates an <ExtraData> node under the given parent element with
* <ExtraDataItem> childern according to the contents of the given
* map.
* This is in ConfigFileBase because it's used in both MainConfigFile
* MachineConfigFile, which both can have extradata.
*
* @param elmParent
* @param me
*/
const ExtraDataItemsMap &me)
{
{
++it)
{
}
}
}
/**
* Creates <DeviceFilter> nodes under the given parent element according to
* the contents of the given USBDeviceFiltersList. This is in ConfigFileBase
* because it's used in both MainConfigFile (for host filters) and
* MachineConfigFile (for machine filters).
*
* If fHostMode is true, this means that we're supposed to write filters
* for the IHost interface (respect "action", omit "strRemote" and
* "ulMaskedInterfaces" in struct USBDeviceFilter).
*
* @param elmParent
* @param ll
* @param fHostMode
*/
const USBDeviceFiltersList &ll,
bool fHostMode)
{
++it)
{
if (fHostMode)
{
const char *pcsz =
: /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
}
else
{
if (flt.ulMaskedInterfaces)
}
}
}
/**
* Cleans up memory allocated by the internal XML parser. To be called by
* descendant classes when they're done analyzing the DOM tree to discard it.
*/
void ConfigFileBase::clearDocument()
{
m->cleanup();
}
/**
* Returns true only if the underlying config file exists on disk;
* either because the file has been loaded from disk, or it's been written
* to disk, or both.
* @return
*/
bool ConfigFileBase::fileExists()
{
return m->fFileExists;
}
////////////////////////////////////////////////////////////////////////////////
//
// MainConfigFile
//
////////////////////////////////////////////////////////////////////////////////
/**
* Reads one <MachineEntry> from the main VirtualBox.xml file.
* @param elmMachineRegistry
*/
{
// <MachineEntry uuid="{ xxx }" src=" xxx "/>
{
{
)
{
}
else
throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing"));
}
}
}
/**
* Reads a media registry entry from the main VirtualBox.xml file.
*
* Whereas the current media registry code is fairly straightforward, it was quite a mess
* with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements
* in the media registry were much more inconsistent, and different elements were used
* depending on the type of device and image.
*
* @param t
* @param elmMedium
* @param llMedia
*/
// child HardDisk node or DiffHardDisk node for pre-1.4
{
// <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName());
bool fNeedsLocation = true;
if (t == HardDisk)
{
if (m->sv < SettingsVersion_v1_4)
{
// here the system is:
// <HardDisk uuid="{....}" type="normal">
// </HardDisk>
fNeedsLocation = false;
bool fNeedsFilePath = true;
{
fNeedsFilePath = false;
// string for the location and also have several disk properties for these, whereas this used
// to be hidden in several sub-elements before 1.4, so compose a location string and set up
// the properties:
{
}
{
}
{
if (strServerAndPort.length())
}
{
}
{
}
}
{
fNeedsFilePath = false;
fNeedsLocation = true;
// also requires @format attribute, which will be queried below
}
else
throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName());
if (fNeedsFilePath)
throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName());
}
throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName());
med.fAutoReset = false;
{
// pre-1.4 used lower case, so make this case-insensitive
if (strType == "NORMAL")
else if (strType == "IMMUTABLE")
else if (strType == "WRITETHROUGH")
else
throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable or Writethrough"));
}
}
else if (m->sv < SettingsVersion_v1_4)
{
// DVD and floppy images before 1.4 had "src" attribute instead of "location"
throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName());
fNeedsLocation = false;
}
if (fNeedsLocation)
// current files and 1.4 CustomHardDisk elements must have a location attribute
throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName());
// recurse to handle children
{
if ( t == HardDisk
|| ( (m->sv < SettingsVersion_v1_4)
)
)
)
// recurse with this element and push the child onto our current children list
readMedium(t,
{
)
else
throw ConfigFileError(this, pelmHDChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
}
}
}
/**
* Reads in the entire <MediaRegistry> chunk. For pre-1.4 files, this gets called
* with the <DiskRegistry> chunk instead.
* @param elmMediaRegistry
*/
{
{
t = HardDisk;
t = DVDImage;
t = FloppyImage;
else
continue;
{
if ( t == HardDisk
)
readMedium(t,
llHardDisks); // list to append hard disk data to: the root list
else if ( t == DVDImage
)
readMedium(t,
llDvdImages); // list to append dvd images to: the root list
else if ( t == FloppyImage
)
readMedium(t,
llFloppyImages); // list to append floppy images to: the root list
}
}
}
/**
* Reads in the <DHCPServers> chunk.
* @param elmDHCPServers
*/
{
{
{
)
else
throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing"));
}
}
}
/**
* Constructor.
*
* If pstrFilename is != NULL, this reads the given settings file into the member
* variables and various substructures and lists. Otherwise, the member variables
* are initialized with default values.
*
* Throws variants of xml::Error for I/O, XML and logical content errors, which
* the caller should catch; if this constructor does not throw, then the member
* variables contain meaningful values (either from the file or defaults).
*
* @param strFilename
*/
{
if (pstrFilename)
{
// the ConfigFileBase constructor has loaded the XML file, so now
// we need only analyze what is in there
{
{
{
{
pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
if (!pelmGlobalChild->getAttributeValue("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder))
// pre-1.4 used @defaultVDIFolder instead
pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
}
|| ( (m->sv < SettingsVersion_v1_4)
)
)
{
{
}
}
}
} // end if (pelmRootChild->nameEquals("Global"))
}
}
// DHCP servers were introduced with settings version 1.7; if we're loading
// from an older version OR this is a fresh install, then add one DHCP server
// with default settings
if ( (!llDhcpServers.size())
&& ( (!pstrFilename) // empty VirtualBox.xml file
)
)
{
#ifdef RT_OS_WINDOWS
"HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
#else
"HostInterfaceNetworking-vboxnet0";
#endif
}
}
/**
* Creates a single <HardDisk> element for the given Medium structure
* and recurses to write the child hard disks underneath. Called from
* MainConfigFile::write().
*
* @param elmMedium
* @param m
* @param level
*/
const Medium &m,
{
if (m.fAutoReset)
if (m.strDescription.length())
++it)
{
}
// only for base hard disks, save the type
if (level == 0)
{
const char *pcszType =
/*m.hdType == MediumType_Writethrough ?*/ "Writethrough";
}
++it)
{
// recurse for children
*it, // settings::Medium
++level); // recursion level
}
}
/**
* Called from the IVirtualBox interface to write out VirtualBox.xml. This
* builds an XML DOM tree and writes it out to disk.
*/
{
m->strFilename = strFilename;
++it)
{
// <MachineEntry uuid="{5f102a55-a51b-48e3-b45a-b28d33469488}" src="/mnt/innotek-unix/vbox-machines/Windows 5.1 XP 1 (Office 2003)/Windows 5.1 XP 1 (Office 2003).xml"/>
}
++it)
{
}
++it)
{
if (m.strDescription.length())
}
++it)
{
if (m.strDescription.length())
}
++it)
{
const DHCPServer &d = *it;
}
pelmSysProps->setAttribute("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
true); // fHostMode
// now go write the XML
m->fFileExists = true;
}
// use a define for the platform-dependent default value of
// hwvirt exclusivity, since we'll need to check that value
// in bumpSettingsVersionIfNeeded()
#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
#define HWVIRTEXCLUSIVEDEFAULT false
#else
#define HWVIRTEXCLUSIVEDEFAULT true
#endif
/**
* Hardware struct constructor.
*/
: strVersion("2"),
fHardwareVirt(true),
fNestedPaging(false),
fVPID(false),
fSyntheticCpu(false),
fPAE(false),
cCPUs(1),
ulVRAMSizeMB(8),
cMonitors(1),
fAccelerate3D(false),
fAccelerate2DVideo(false),
{
}
/**
* Called from MachineConfigFile::readHardware() to cpuid information.
* @param elmCpuid
* @param ll
*/
{
{
}
}
/**
* Called from MachineConfigFile::readHardware() to network information.
* @param elmNetwork
* @param ll
*/
{
{
{
if (strTemp == "Am79C970A")
else if (strTemp == "Am79C973")
else if (strTemp == "82540EM")
else if (strTemp == "82543GC")
else if (strTemp == "82545EM")
else if (strTemp == "virtio")
else
throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
}
{
}
)
{
}
{
throw ConfigFileError(this, pelmAdapterChild, N_("Required InternalNetwork/@name element is missing"));
}
{
throw ConfigFileError(this, pelmAdapterChild, N_("Required HostOnlyInterface/@name element is missing"));
}
// else: default is NetworkAttachmentType_Null
}
}
/**
* Called from MachineConfigFile::readHardware() to read serial port information.
* @param elmUART
* @param ll
*/
{
{
// slot must be unique
++it)
throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
if (strPortMode == "RawFile")
else if (strPortMode == "HostPipe")
else if (strPortMode == "HostDevice")
else if (strPortMode == "Disconnected")
else
throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
}
}
/**
* Called from MachineConfigFile::readHardware() to read parallel port information.
* @param elmLPT
* @param ll
*/
{
{
// slot must be unique
++it)
throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
}
}
/**
* Called from MachineConfigFile::readHardware() to read guest property information.
* @param elmGuestProperties
* @param hw
*/
{
{
}
}
/**
* Helper function to read attributes that are common to <SATAController> (pre-1.7)
* and <StorageController>.
* @param elmStorageController
* @param strg
*/
void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
{
}
/**
* Reads in a <Hardware> block and stores it in the given structure. Used
* both directly from readMachine and from readSnapshot, since snapshots
* have their own hardware sections.
*
* For legacy pre-1.7 settings we also need a storage structure because
* the IDE and SATA controllers used to be defined under <Hardware>.
*
* @param elmHardware
* @param hw
*/
{
// defaults to 2 and is only written if != 2
{
{
{
// pre-1.5 variant; not sure if this actually exists in the wild anywhere
}
{
}
}
{
{
if ( (strFirmwareType == "BIOS")
)
else if ( (strFirmwareType == "EFI")
)
else
throw ConfigFileError(this,
strFirmwareType.c_str());
}
}
{
{
if ( ulPos < 1
)
throw ConfigFileError(this,
// XML is 1-based but internal data is 0-based
--ulPos;
throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
if (strDevice == "None")
else if (strDevice == "Floppy")
else if (strDevice == "DVD")
else if (strDevice == "HardDisk")
else if (strDevice == "Network")
else
throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
}
}
{
}
{
{
// settings before 1.3 used lower case so make sure this is case-insensitive
if (strAuthType == "NULL")
else if (strAuthType == "GUEST")
else if (strAuthType == "EXTERNAL")
else
throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
}
}
{
{
}
{
{
// settings before 1.3 used lower case so make sure this is case-insensitive
if (strBootMenuMode == "DISABLED")
else if (strBootMenuMode == "MENUONLY")
else if (strBootMenuMode == "MESSAGEANDMENU")
else
throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
}
}
// legacy BIOS/IDEController (pre 1.7)
if ( (m->sv < SettingsVersion_v1_7)
)
{
{
if (strType == "PIIX3")
else if (strType == "PIIX4")
else if (strType == "ICH6")
else
throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
}
}
}
{
}
else if ( (m->sv < SettingsVersion_v1_7)
)
{
bool f;
&& (f)
)
{
}
}
)
)
{
{
if (strTemp == "SB16")
else if (strTemp == "AC97")
else
throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
}
{
// settings before 1.3 used lower case so make sure this is case-insensitive
if (strTemp == "NULL")
else if (strTemp == "WINMM")
else if (strTemp == "SOLAUDIO")
else if (strTemp == "ALSA")
else if (strTemp == "PULSE")
else if (strTemp == "OSS")
else if (strTemp == "COREAUDIO")
else if (strTemp == "MMPM")
else
throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
}
}
{
{
}
}
{
{
if (strTemp == "Disabled")
else if (strTemp == "HostToGuest")
else if (strTemp == "GuestToHost")
else if (strTemp == "Bidirectional")
else
throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipbord/@mode attribute"), strTemp.c_str());
}
}
{
}
}
throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
}
/**
* This gets called instead of readStorageControllers() for legacy pre-1.7 settings
* files which have a <HardDiskAttachments> node and storage controller settings
* hidden in the <Hardware> settings. We set the StorageControllers fields just the
* same, just from different sources.
* @param elmHardware <Hardware> XML node.
* @param elmHardDiskAttachments <HardDiskAttachments> XML node.
* @param strg
*/
void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
{
++it)
{
StorageController &s = *it;
if (s.storageBus == StorageBus_IDE)
pIDEController = &s;
else if (s.storageBus == StorageBus_SATA)
pSATAController = &s;
}
{
throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
// pre-1.7 'channel' is now port
throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
// pre-1.7 'device' is still device
throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
if (strBus == "IDE")
{
if (!pIDEController)
throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
}
else if (strBus == "SATA")
{
if (!pSATAController)
throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
}
else
throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
}
}
/**
* Reads in a <StorageControllers> block and stores it in the given Storage structure.
* Used both directly from readMachine and from readSnapshot, since snapshots
* have their own storage controllers sections.
*
* This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
* for earlier versions.
*
* @param elmStorageControllers
*/
{
{
throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
// canonicalize storage controller names for configs in the switchover
// period.
if (m->sv <= SettingsVersion_v1_9)
{
}
// default from constructor is 0
throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
if (strType == "AHCI")
{
}
else if (strType == "LsiLogic")
{
}
else if (strType == "BusLogic")
{
}
else if (strType == "PIIX3")
{
}
else if (strType == "PIIX4")
{
}
else if (strType == "ICH6")
{
}
else if ( (m->sv >= SettingsVersion_v1_9)
&& (strType == "I82078")
)
{
}
else
throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
{
if (strTemp == "HardDisk")
else if (m->sv >= SettingsVersion_v1_9)
{
// starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
if (strTemp == "DVD")
{
}
else if (strTemp == "Floppy")
}
{
// all types can have images attached, but for HardDisk it's required
{
else
{
// DVDs and floppies can also have <HostDrive> instead of <Image>
throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
}
}
else
{
throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
}
}
}
}
}
/**
* This gets called for legacy pre-1.9 settings files after having parsed the
* <Hardware> and <StorageControllers> sections to parse <Hardware> once more
* for the <DVDDrive> and <FloppyDrive> sections.
*
* Before settings version 1.9, DVD and floppy drives were specified separately
* under <Hardware>; we then need this extra loop to make sure the storage
* controller structs are already set up so we can add stuff to them.
*
* @param elmHardware
* @param strg
*/
{
{
{
// create a DVD "attached device" and attach it to the existing IDE controller
// legacy DVD drive is always secondary master (port 1, device 0)
)
// find the IDE controller and attach the DVD drive
bool fFound = false;
++it)
{
{
fFound = true;
break;
}
}
if (!fFound)
throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
// shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
// which should have gotten parsed in <StorageControllers> before this got called
}
{
bool fEnabled;
&& (fEnabled)
)
{
// create a new floppy controller and attach a floppy "attached device"
)
// store attachment with controller
// store controller with storage
}
}
}
}
/**
* Called initially for the <Snapshot> element under <Machine>, if present,
* to store the snapshot's data into the given Snapshot structure (which is
* then the one in the Machine struct). This might then recurse if
* a <Snapshots> (plural) element is found in the snapshot, which should
* contain a list of child snapshots; such lists are maintained in the
* Snapshot structure.
*
* @param elmSnapshot
* @param snap
*/
{
// earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
// parse Hardware before the other elements because other things depend on it
{
else if ( (m->sv < SettingsVersion_v1_7)
)
else if ( (m->sv >= SettingsVersion_v1_7)
)
{
{
{
}
}
}
}
if (m->sv < SettingsVersion_v1_9)
// go through Hardware once more to repair the settings controller structures
// with data from old DVDDrive and FloppyDrive elements
}
{
}
/**
* Called from the constructor to actually read in the <Machine> element
* of a machine config file.
* @param elmMachine
*/
{
)
{
fNameSync = true;
if (m->sv < SettingsVersion_v1_5)
fCurrentStateModified = true;
// constructor has called RTTimeNow(&timeLastStateChange) before
#if 1 /** @todo Teleportation: Obsolete. Remove in a couple of days. */
fTeleporterEnabled = false;
uTeleporterPort = 0;
strTeleporterAddress = "";
strTeleporterPassword = "";
#endif
// parse Hardware before the other elements because other things depend on it
{
else if ( (m->sv < SettingsVersion_v1_7)
)
else if ( (m->sv >= SettingsVersion_v1_7)
)
{
// this will recurse into child snapshots, if necessary
}
{
fTeleporterEnabled = false;
uTeleporterPort = 0;
strTeleporterAddress = "";
strTeleporterPassword = "";
}
}
if (m->sv < SettingsVersion_v1_9)
// go through Hardware once more to repair the settings controller structures
// with data from old DVDDrive and FloppyDrive elements
}
else
throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
}
////////////////////////////////////////////////////////////////////////////////
//
// MachineConfigFile
//
////////////////////////////////////////////////////////////////////////////////
/**
* Constructor.
*
* If pstrFilename is != NULL, this reads the given settings file into the member
* variables and various substructures and lists. Otherwise, the member variables
* are initialized with default values.
*
* Throws variants of xml::Error for I/O, XML and logical content errors, which
* the caller should catch; if this constructor does not throw, then the member
* variables contain meaningful values (either from the file or defaults).
*
* @param strFilename
*/
fNameSync(true),
fTeleporterEnabled(false),
uTeleporterPort(0),
fCurrentStateModified(true),
fAborted(false)
{
if (pstrFilename)
{
// the ConfigFileBase constructor has loaded the XML file, so now
// we need only analyze what is in there
{
}
// clean up memory allocated by XML engine
}
}
/**
* Creates a <Hardware> node under elmParent and then writes out the XML
* keys under that. Called for both the <Machine> node and for snapshots.
* @param elmParent
* @param st
*/
{
if ( (m->sv >= SettingsVersion_v1_9)
)
if (m->sv >= SettingsVersion_v1_9)
if (hw.fNestedPaging)
if (hw.fSyntheticCpu)
++it)
{
}
if ( (m->sv >= SettingsVersion_v1_9)
)
{
}
++it)
{
const char *pcszDevice;
switch (type)
{
}
i + 1); // XML is 1-based but internal data is 0-based
}
if (m->sv >= SettingsVersion_v1_8)
const char *pcszAuthType;
{
}
const char *pcszBootMenu;
{
}
if (m->sv < SettingsVersion_v1_9)
{
// settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
// run thru the storage controllers to see if we have a DVD or floppy drives
++it)
{
// in old settings format, the DVD drive could only have been under the IDE controller
{
++it2)
{
{
if (cDVDs > 0)
throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
++cDVDs;
}
}
}
{
if (cFloppiesHere > 1)
throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
if (cFloppiesHere)
{
}
}
}
if (cFloppies == 0)
else if (cFloppies > 1)
throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
}
false); // fHostMode
++it)
{
if (nic.fTraceEnabled)
{
}
const char *pcszType;
{
}
{
break;
break;
break;
break;
default: /*case NetworkAttachmentType_Null:*/
break;
}
}
++it)
{
const char *pcszHostMode;
{
}
{
case PortMode_HostPipe:
/* no break */
case PortMode_HostDevice:
case PortMode_RawFile:
break;
default:
break;
}
}
++it)
{
}
pelmAudio->setAttribute("controller", (hw.audioAdapter.controllerType == AudioControllerType_SB16) ? "SB16" : "AC97");
const char *pcszDriver;
{
}
++it)
{
}
const char *pcszClip;
switch (hw.clipboardMode)
{
}
++it)
{
}
}
/**
* Creates a <StorageControllers> node under elmParent and then writes out the XML
* keys under that. Called for both the <Machine> node and for snapshots.
* @param elmParent
* @param st
*/
{
++it)
{
if ( (m->sv < SettingsVersion_v1_9)
)
// floppy controller already got written into <Hardware>/<FloppyController> in writeHardware()
// for pre-1.9 settings
continue;
//
if (m->sv < SettingsVersion_v1_8)
{
// pre-1.8 settings use shorter controller names, they are
// expanded when reading the settings
if (name == "IDE Controller")
name = "IDE";
else if (name == "SATA Controller")
name = "SATA";
}
const char *pcszType;
switch (sc.controllerType)
{
}
if (m->sv >= SettingsVersion_v1_9)
if (sc.ulInstance)
{
}
++it2)
{
// For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
// so we shouldn't write them here; we only get here for DVDs though because we ruled out
// the floppy controller at the top of the loop
&& m->sv < SettingsVersion_v1_9
)
continue;
switch (att.deviceType)
{
case DeviceType_HardDisk:
pcszType = "HardDisk";
break;
case DeviceType_DVD:
pcszType = "DVD";
if (att.fPassThrough)
break;
case DeviceType_Floppy:
pcszType = "Floppy";
break;
}
else if ( (m->sv >= SettingsVersion_v1_9)
)
}
}
}
/**
* Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
* for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
* <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
* @param elmParent
* @param snap
*/
{
{
++it)
{
}
}
}
/**
* Called from write() before calling ConfigFileBase::createStubDocument().
* This adjusts the settings version in m->sv if incompatible settings require
* a settings bump, whereas otherwise we try to preserve the settings version
* to avoid breaking compatibility with older versions.
*/
{
// "accelerate 2d video" requires settings version 1.8
if ( (m->sv < SettingsVersion_v1_8)
)
m->sv = SettingsVersion_v1_8;
// all the following require settings version 1.9
if ( (m->sv < SettingsVersion_v1_9)
|| !strTeleporterAddress.isEmpty()
|| !strTeleporterPassword.isEmpty()
)
)
m->sv = SettingsVersion_v1_9;
// settings version 1.9 is also required if there is not exactly one DVD
// or more than one floppy drive present or the DVD is not at the secondary
// master; this check is a bit more complicated
if (m->sv < SettingsVersion_v1_9)
{
// need to run thru all the storage controllers to figure this out
&& m->sv < SettingsVersion_v1_9;
++it)
{
++it2)
{
{
m->sv = SettingsVersion_v1_9;
break;
}
{
)
{
m->sv = SettingsVersion_v1_9;
break;
}
++cDVDs;
}
++cFloppies;
}
}
// VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
// so any deviation from that will require settings version 1.9
if ( (m->sv < SettingsVersion_v1_9)
&& ( (cDVDs != 1)
|| (cFloppies > 1)
)
)
m->sv = SettingsVersion_v1_9;
}
}
/**
* Called from Main code to write a machine config file to disk. This builds a DOM tree from
* the member variables and then writes the XML file; it throws xml::Error instances on errors,
* in particular if the file cannot be written.
*/
{
try
{
// createStubDocument() sets the settings version to at least 1.7; however,
// we might need to enfore a later settings version if incompatible settings
// are present:
m->strFilename = strFilename;
if (!fNameSync)
if (strDescription.length())
if (strStateFile.length())
if (!uuidCurrentSnapshot.isEmpty())
if (strSnapshotFolder.length())
if (!fCurrentStateModified)
if (fAborted)
if ( m->sv >= SettingsVersion_v1_9
&& ( fTeleporterEnabled
|| !strTeleporterAddress.isEmpty()
|| !strTeleporterPassword.isEmpty()
)
)
{
}
if (llFirstSnapshot.size())
// now go write the XML
m->fFileExists = true;
}
catch (...)
{
throw;
}
}