VirtualBox

source: vbox/trunk/src/VBox/Main/xml/Settings.cpp@ 34587

最後變更 在這個檔案從34587是 34587,由 vboxsync 提交於 14 年 前

Main: Bandwidth groups for disks (and later network)

This introduces two new interfaces. The first one named IBandwidthGroup
represents one I/O limit and can be assigned to several mediums which
share this limit (which works only for harddisk images with the disabled
host cache).
The second one IBandwdithControl manages the groups and can create new ones
and destroy them if not required anymore.

VBoxManage: commands to access the bandwidth groups

Syntax:
VBoxManage storageattach <uuid|vmname>

...
--bandwidthgroup <name>

--bandwidthgroup assigns the specified device to the given group.

VBoxManage bandwidthctl <uuid|vmname>

--name <name>
--add disk|network
--limit <megabytes per second>
--delete

The --name parameter gives the name of the bandwidth group.
--add creates a new group of the given type (only disk is implemented so far)

with the given name.

--limit sets the limit to the given amount of MB/s

Note that limit can be changed while the VM is running. The VM
will immediately pick up the new limit for the given group name.

--delete deletes the group with the given name if it isn't used anymore.

Trying to delete a still used group will result in an error.

Example:

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 20
Creates a group named Test having a 20 MB/s limit.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup Limit
Adds a new disk to the SATA controller and assigns the bandwidth group Limit to it.

VBoxManage storageattach "Test VM" --storagectl "SATA Controller" --port 0 --device 0 --type hdd --medium test.vdi --bandwidthgroup none
Removes the bandwidth limit from the disk.

VBoxManage bandwidthctl "Test VM" --name Limit --add disk --limit 10
Changes the limit of bandwidth group Limit to 10 MB/s. If the VM is running the limit will be picked up
immediately.

  • 屬性 svn:eol-style 設為 native
  • 屬性 svn:keywords 設為 Date Revision Author Id
檔案大小: 190.8 KB
 
1/* $Id: Settings.cpp 34587 2010-12-01 20:30:02Z vboxsync $ */
2/** @file
3 * Settings File Manipulation API.
4 *
5 * Two classes, MainConfigFile and MachineConfigFile, represent the VirtualBox.xml and
6 * machine XML files. They share a common ancestor class, ConfigFileBase, which shares
7 * functionality such as talking to the XML back-end classes and settings version management.
8 *
9 * The code can read all VirtualBox settings files version 1.3 and higher. That version was
10 * written by VirtualBox 2.0. It can write settings version 1.7 (used by VirtualBox 2.2 and
11 * 3.0) and 1.9 (used by VirtualBox 3.1) and newer ones obviously.
12 *
13 * The settings versions enum is defined in src/VBox/Main/idl/VirtualBox.xidl. To introduce
14 * a new settings version (should be necessary at most once per VirtualBox major release,
15 * if at all), add a new SettingsVersion value to that enum and grep for the previously
16 * highest value to see which code in here needs adjusting.
17 *
18 * Certainly ConfigFileBase::ConfigFileBase() will. Change VBOX_XML_VERSION below as well.
19 *
20 * Once a new settings version has been added, these are the rules for introducing a new
21 * setting: If an XML element or attribute or value is introduced that was not present in
22 * previous versions, then settings version checks need to be introduced. See the
23 * SettingsVersion enumeration in src/VBox/Main/idl/VirtualBox.xidl for details about which
24 * version was used when.
25 *
26 * The settings versions checks are necessary because since version 3.1, VirtualBox no longer
27 * automatically converts XML settings files but only if necessary, that is, if settings are
28 * present that the old format does not support. If we write an element or attribute to a
29 * settings file of an older version, then an old VirtualBox (before 3.1) will attempt to
30 * validate it with XML schema, and that will certainly fail.
31 *
32 * So, to introduce a new setting:
33 *
34 * 1) Make sure the constructor of corresponding settings structure has a proper default.
35 *
36 * 2) In the settings reader method, try to read the setting; if it's there, great, if not,
37 * the default value will have been set by the constructor. The rule is to be tolerant
38 * here.
39 *
40 * 3) In MachineConfigFile::bumpSettingsVersionIfNeeded(), check if the new setting has
41 * a non-default value (i.e. that differs from the constructor). If so, bump the
42 * settings version to the current version so the settings writer (4) can write out
43 * the non-default value properly.
44 *
45 * So far a corresponding method for MainConfigFile has not been necessary since there
46 * have been no incompatible changes yet.
47 *
48 * 4) In the settings writer method, write the setting _only_ if the current settings
49 * version (stored in m->sv) is high enough. That is, for VirtualBox 4.0, write it
50 * only if (m->sv >= SettingsVersion_v1_11).
51 */
52
53/*
54 * Copyright (C) 2007-2010 Oracle Corporation
55 *
56 * This file is part of VirtualBox Open Source Edition (OSE), as
57 * available from http://www.alldomusa.eu.org. This file is free software;
58 * you can redistribute it and/or modify it under the terms of the GNU
59 * General Public License (GPL) as published by the Free Software
60 * Foundation, in version 2 as it comes in the "COPYING" file of the
61 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
62 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
63 */
64
65#include "VBox/com/string.h"
66#include "VBox/settings.h"
67#include <iprt/cpp/xml.h>
68#include <iprt/stream.h>
69#include <iprt/ctype.h>
70#include <iprt/file.h>
71#include <iprt/process.h>
72#include <iprt/ldr.h>
73#include <iprt/cpp/lock.h>
74
75// generated header
76#include "SchemaDefs.h"
77
78#include "Logging.h"
79
80using namespace com;
81using namespace settings;
82
83////////////////////////////////////////////////////////////////////////////////
84//
85// Defines
86//
87////////////////////////////////////////////////////////////////////////////////
88
89/** VirtualBox XML settings namespace */
90#define VBOX_XML_NAMESPACE "http://www.innotek.de/VirtualBox-settings"
91
92/** VirtualBox XML settings version number substring ("x.y") */
93#define VBOX_XML_VERSION "1.11"
94
95/** VirtualBox XML settings version platform substring */
96#if defined (RT_OS_DARWIN)
97# define VBOX_XML_PLATFORM "macosx"
98#elif defined (RT_OS_FREEBSD)
99# define VBOX_XML_PLATFORM "freebsd"
100#elif defined (RT_OS_LINUX)
101# define VBOX_XML_PLATFORM "linux"
102#elif defined (RT_OS_NETBSD)
103# define VBOX_XML_PLATFORM "netbsd"
104#elif defined (RT_OS_OPENBSD)
105# define VBOX_XML_PLATFORM "openbsd"
106#elif defined (RT_OS_OS2)
107# define VBOX_XML_PLATFORM "os2"
108#elif defined (RT_OS_SOLARIS)
109# define VBOX_XML_PLATFORM "solaris"
110#elif defined (RT_OS_WINDOWS)
111# define VBOX_XML_PLATFORM "windows"
112#else
113# error Unsupported platform!
114#endif
115
116/** VirtualBox XML settings full version string ("x.y-platform") */
117#define VBOX_XML_VERSION_FULL VBOX_XML_VERSION "-" VBOX_XML_PLATFORM
118
119////////////////////////////////////////////////////////////////////////////////
120//
121// Internal data
122//
123////////////////////////////////////////////////////////////////////////////////
124
125/**
126 * Opaque data structore for ConfigFileBase (only declared
127 * in header, defined only here).
128 */
129
130struct ConfigFileBase::Data
131{
132 Data()
133 : pDoc(NULL),
134 pelmRoot(NULL),
135 sv(SettingsVersion_Null),
136 svRead(SettingsVersion_Null)
137 {}
138
139 ~Data()
140 {
141 cleanup();
142 }
143
144 iprt::MiniString strFilename;
145 bool fFileExists;
146
147 xml::Document *pDoc;
148 xml::ElementNode *pelmRoot;
149
150 com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux"
151 SettingsVersion_T sv; // e.g. SettingsVersion_v1_7
152
153 SettingsVersion_T svRead; // settings version that the original file had when it was read,
154 // or SettingsVersion_Null if none
155
156 void copyFrom(const Data &d)
157 {
158 strFilename = d.strFilename;
159 fFileExists = d.fFileExists;
160 strSettingsVersionFull = d.strSettingsVersionFull;
161 sv = d.sv;
162 svRead = d.svRead;
163 }
164
165 void cleanup()
166 {
167 if (pDoc)
168 {
169 delete pDoc;
170 pDoc = NULL;
171 pelmRoot = NULL;
172 }
173 }
174};
175
176/**
177 * Private exception class (not in the header file) that makes
178 * throwing xml::LogicError instances easier. That class is public
179 * and should be caught by client code.
180 */
181class settings::ConfigFileError : public xml::LogicError
182{
183public:
184 ConfigFileError(const ConfigFileBase *file,
185 const xml::Node *pNode,
186 const char *pcszFormat, ...)
187 : xml::LogicError()
188 {
189 va_list args;
190 va_start(args, pcszFormat);
191 Utf8Str strWhat(pcszFormat, args);
192 va_end(args);
193
194 Utf8Str strLine;
195 if (pNode)
196 strLine = Utf8StrFmt(" (line %RU32)", pNode->getLineNumber());
197
198 const char *pcsz = strLine.c_str();
199 Utf8StrFmt str(N_("Error in %s%s -- %s"),
200 file->m->strFilename.c_str(),
201 (pcsz) ? pcsz : "",
202 strWhat.c_str());
203
204 setWhat(str.c_str());
205 }
206};
207
208////////////////////////////////////////////////////////////////////////////////
209//
210// MediaRegistry
211//
212////////////////////////////////////////////////////////////////////////////////
213
214bool Medium::operator==(const Medium &m) const
215{
216 return (uuid == m.uuid)
217 && (strLocation == m.strLocation)
218 && (strDescription == m.strDescription)
219 && (strFormat == m.strFormat)
220 && (fAutoReset == m.fAutoReset)
221 && (properties == m.properties)
222 && (hdType == m.hdType)
223 && (llChildren== m.llChildren); // this is deep and recurses
224}
225
226bool MediaRegistry::operator==(const MediaRegistry &m) const
227{
228 return llHardDisks == m.llHardDisks
229 && llDvdImages == m.llDvdImages
230 && llFloppyImages == m.llFloppyImages;
231}
232
233////////////////////////////////////////////////////////////////////////////////
234//
235// ConfigFileBase
236//
237////////////////////////////////////////////////////////////////////////////////
238
239/**
240 * Constructor. Allocates the XML internals, parses the XML file if
241 * pstrFilename is != NULL and reads the settings version from it.
242 * @param strFilename
243 */
244ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
245 : m(new Data)
246{
247 Utf8Str strMajor;
248 Utf8Str strMinor;
249
250 m->fFileExists = false;
251
252 if (pstrFilename)
253 {
254 // reading existing settings file:
255 m->strFilename = *pstrFilename;
256
257 xml::XmlFileParser parser;
258 m->pDoc = new xml::Document;
259 parser.read(*pstrFilename,
260 *m->pDoc);
261
262 m->fFileExists = true;
263
264 m->pelmRoot = m->pDoc->getRootElement();
265 if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
266 throw ConfigFileError(this, NULL, N_("Root element in VirtualBox settings files must be \"VirtualBox\"."));
267
268 if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
269 throw ConfigFileError(this, m->pelmRoot, N_("Required VirtualBox/@version attribute is missing"));
270
271 LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
272
273 // parse settings version; allow future versions but fail if file is older than 1.6
274 m->sv = SettingsVersion_Null;
275 if (m->strSettingsVersionFull.length() > 3)
276 {
277 const char *pcsz = m->strSettingsVersionFull.c_str();
278 char c;
279
280 while ( (c = *pcsz)
281 && RT_C_IS_DIGIT(c)
282 )
283 {
284 strMajor.append(c);
285 ++pcsz;
286 }
287
288 if (*pcsz++ == '.')
289 {
290 while ( (c = *pcsz)
291 && RT_C_IS_DIGIT(c)
292 )
293 {
294 strMinor.append(c);
295 ++pcsz;
296 }
297 }
298
299 uint32_t ulMajor = RTStrToUInt32(strMajor.c_str());
300 uint32_t ulMinor = RTStrToUInt32(strMinor.c_str());
301
302 if (ulMajor == 1)
303 {
304 if (ulMinor == 3)
305 m->sv = SettingsVersion_v1_3;
306 else if (ulMinor == 4)
307 m->sv = SettingsVersion_v1_4;
308 else if (ulMinor == 5)
309 m->sv = SettingsVersion_v1_5;
310 else if (ulMinor == 6)
311 m->sv = SettingsVersion_v1_6;
312 else if (ulMinor == 7)
313 m->sv = SettingsVersion_v1_7;
314 else if (ulMinor == 8)
315 m->sv = SettingsVersion_v1_8;
316 else if (ulMinor == 9)
317 m->sv = SettingsVersion_v1_9;
318 else if (ulMinor == 10)
319 m->sv = SettingsVersion_v1_10;
320 else if (ulMinor == 11)
321 m->sv = SettingsVersion_v1_11;
322 else if (ulMinor > 11)
323 m->sv = SettingsVersion_Future;
324 }
325 else if (ulMajor > 1)
326 m->sv = SettingsVersion_Future;
327
328 LogRel(("Parsed settings version %d.%d to enum value %d\n", ulMajor, ulMinor, m->sv));
329 }
330
331 if (m->sv == SettingsVersion_Null)
332 throw ConfigFileError(this, m->pelmRoot, N_("Cannot handle settings version '%s'"), m->strSettingsVersionFull.c_str());
333
334 // remember the settings version we read in case it gets upgraded later,
335 // so we know when to make backups
336 m->svRead = m->sv;
337 }
338 else
339 {
340 // creating new settings file:
341 m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
342 m->sv = SettingsVersion_v1_11;
343 }
344}
345
346/**
347 * Clean up.
348 */
349ConfigFileBase::~ConfigFileBase()
350{
351 if (m)
352 {
353 delete m;
354 m = NULL;
355 }
356}
357
358/**
359 * Helper function that parses a UUID in string form into
360 * a com::Guid item. Accepts UUIDs both with and without
361 * "{}" brackets. Throws on errors.
362 * @param guid
363 * @param strUUID
364 */
365void ConfigFileBase::parseUUID(Guid &guid,
366 const Utf8Str &strUUID) const
367{
368 guid = strUUID.c_str();
369 if (guid.isEmpty())
370 throw ConfigFileError(this, NULL, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
371}
372
373/**
374 * Parses the given string in str and attempts to treat it as an ISO
375 * date/time stamp to put into timestamp. Throws on errors.
376 * @param timestamp
377 * @param str
378 */
379void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
380 const com::Utf8Str &str) const
381{
382 const char *pcsz = str.c_str();
383 // yyyy-mm-ddThh:mm:ss
384 // "2009-07-10T11:54:03Z"
385 // 01234567890123456789
386 // 1
387 if (str.length() > 19)
388 {
389 // timezone must either be unspecified or 'Z' for UTC
390 if ( (pcsz[19])
391 && (pcsz[19] != 'Z')
392 )
393 throw ConfigFileError(this, NULL, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
394
395 int32_t yyyy;
396 uint32_t mm, dd, hh, min, secs;
397 if ( (pcsz[4] == '-')
398 && (pcsz[7] == '-')
399 && (pcsz[10] == 'T')
400 && (pcsz[13] == ':')
401 && (pcsz[16] == ':')
402 )
403 {
404 int rc;
405 if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
406 // could theoretically be negative but let's assume that nobody
407 // created virtual machines before the Christian era
408 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
409 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
410 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
411 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
412 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
413 )
414 {
415 RTTIME time =
416 {
417 yyyy,
418 (uint8_t)mm,
419 0,
420 0,
421 (uint8_t)dd,
422 (uint8_t)hh,
423 (uint8_t)min,
424 (uint8_t)secs,
425 0,
426 RTTIME_FLAGS_TYPE_UTC,
427 0
428 };
429 if (RTTimeNormalize(&time))
430 if (RTTimeImplode(&timestamp, &time))
431 return;
432 }
433
434 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
435 }
436
437 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
438 }
439}
440
441/**
442 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
443 * @param stamp
444 * @return
445 */
446com::Utf8Str ConfigFileBase::makeString(const RTTIMESPEC &stamp)
447{
448 RTTIME time;
449 if (!RTTimeExplode(&time, &stamp))
450 throw ConfigFileError(this, NULL, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
451
452 return Utf8StrFmt("%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
453 time.i32Year,
454 (uint16_t)time.u8Month,
455 (uint16_t)time.u8MonthDay,
456 (uint16_t)time.u8Hour,
457 (uint16_t)time.u8Minute,
458 (uint16_t)time.u8Second);
459}
460
461/**
462 * Helper method to read in an ExtraData subtree and stores its contents
463 * in the given map of extradata items. Used for both main and machine
464 * extradata (MainConfigFile and MachineConfigFile).
465 * @param elmExtraData
466 * @param map
467 */
468void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
469 StringsMap &map)
470{
471 xml::NodesLoop nlLevel4(elmExtraData);
472 const xml::ElementNode *pelmExtraDataItem;
473 while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
474 {
475 if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
476 {
477 // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
478 Utf8Str strName, strValue;
479 if ( ((pelmExtraDataItem->getAttributeValue("name", strName)))
480 && ((pelmExtraDataItem->getAttributeValue("value", strValue)))
481 )
482 map[strName] = strValue;
483 else
484 throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing"));
485 }
486 }
487}
488
489/**
490 * Reads <USBDeviceFilter> entries from under the given elmDeviceFilters node and
491 * stores them in the given linklist. This is in ConfigFileBase because it's used
492 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
493 * filters).
494 * @param elmDeviceFilters
495 * @param ll
496 */
497void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
498 USBDeviceFiltersList &ll)
499{
500 xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
501 const xml::ElementNode *pelmLevel4Child;
502 while ((pelmLevel4Child = nl1.forAllNodes()))
503 {
504 USBDeviceFilter flt;
505 flt.action = USBDeviceFilterAction_Ignore;
506 Utf8Str strAction;
507 if ( (pelmLevel4Child->getAttributeValue("name", flt.strName))
508 && (pelmLevel4Child->getAttributeValue("active", flt.fActive))
509 )
510 {
511 if (!pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId))
512 pelmLevel4Child->getAttributeValue("vendorid", flt.strVendorId); // used before 1.3
513 if (!pelmLevel4Child->getAttributeValue("productId", flt.strProductId))
514 pelmLevel4Child->getAttributeValue("productid", flt.strProductId); // used before 1.3
515 pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
516 pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
517 pelmLevel4Child->getAttributeValue("product", flt.strProduct);
518 if (!pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber))
519 pelmLevel4Child->getAttributeValue("serialnumber", flt.strSerialNumber); // used before 1.3
520 pelmLevel4Child->getAttributeValue("port", flt.strPort);
521
522 // the next 2 are irrelevant for host USB objects
523 pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
524 pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
525
526 // action is only used with host USB objects
527 if (pelmLevel4Child->getAttributeValue("action", strAction))
528 {
529 if (strAction == "Ignore")
530 flt.action = USBDeviceFilterAction_Ignore;
531 else if (strAction == "Hold")
532 flt.action = USBDeviceFilterAction_Hold;
533 else
534 throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str());
535 }
536
537 ll.push_back(flt);
538 }
539 }
540}
541
542/**
543 * Reads a media registry entry from the main VirtualBox.xml file.
544 *
545 * Whereas the current media registry code is fairly straightforward, it was quite a mess
546 * with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements
547 * in the media registry were much more inconsistent, and different elements were used
548 * depending on the type of device and image.
549 *
550 * @param t
551 * @param elmMedium
552 * @param llMedia
553 */
554void ConfigFileBase::readMedium(MediaType t,
555 const xml::ElementNode &elmMedium, // HardDisk node if root; if recursing,
556 // child HardDisk node or DiffHardDisk node for pre-1.4
557 MediaList &llMedia) // list to append medium to (root disk or child list)
558{
559 // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
560 settings::Medium med;
561 Utf8Str strUUID;
562 if (!(elmMedium.getAttributeValue("uuid", strUUID)))
563 throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName());
564
565 parseUUID(med.uuid, strUUID);
566
567 bool fNeedsLocation = true;
568
569 if (t == HardDisk)
570 {
571 if (m->sv < SettingsVersion_v1_4)
572 {
573 // here the system is:
574 // <HardDisk uuid="{....}" type="normal">
575 // <VirtualDiskImage filePath="/path/to/xxx.vdi"/>
576 // </HardDisk>
577
578 fNeedsLocation = false;
579 bool fNeedsFilePath = true;
580 const xml::ElementNode *pelmImage;
581 if ((pelmImage = elmMedium.findChildElement("VirtualDiskImage")))
582 med.strFormat = "VDI";
583 else if ((pelmImage = elmMedium.findChildElement("VMDKImage")))
584 med.strFormat = "VMDK";
585 else if ((pelmImage = elmMedium.findChildElement("VHDImage")))
586 med.strFormat = "VHD";
587 else if ((pelmImage = elmMedium.findChildElement("ISCSIHardDisk")))
588 {
589 med.strFormat = "iSCSI";
590
591 fNeedsFilePath = false;
592 // location is special here: current settings specify an "iscsi://user@server:port/target/lun"
593 // string for the location and also have several disk properties for these, whereas this used
594 // to be hidden in several sub-elements before 1.4, so compose a location string and set up
595 // the properties:
596 med.strLocation = "iscsi://";
597 Utf8Str strUser, strServer, strPort, strTarget, strLun;
598 if (pelmImage->getAttributeValue("userName", strUser))
599 {
600 med.strLocation.append(strUser);
601 med.strLocation.append("@");
602 }
603 Utf8Str strServerAndPort;
604 if (pelmImage->getAttributeValue("server", strServer))
605 {
606 strServerAndPort = strServer;
607 }
608 if (pelmImage->getAttributeValue("port", strPort))
609 {
610 if (strServerAndPort.length())
611 strServerAndPort.append(":");
612 strServerAndPort.append(strPort);
613 }
614 med.strLocation.append(strServerAndPort);
615 if (pelmImage->getAttributeValue("target", strTarget))
616 {
617 med.strLocation.append("/");
618 med.strLocation.append(strTarget);
619 }
620 if (pelmImage->getAttributeValue("lun", strLun))
621 {
622 med.strLocation.append("/");
623 med.strLocation.append(strLun);
624 }
625
626 if (strServer.length() && strPort.length())
627 med.properties["TargetAddress"] = strServerAndPort;
628 if (strTarget.length())
629 med.properties["TargetName"] = strTarget;
630 if (strUser.length())
631 med.properties["InitiatorUsername"] = strUser;
632 Utf8Str strPassword;
633 if (pelmImage->getAttributeValue("password", strPassword))
634 med.properties["InitiatorSecret"] = strPassword;
635 if (strLun.length())
636 med.properties["LUN"] = strLun;
637 }
638 else if ((pelmImage = elmMedium.findChildElement("CustomHardDisk")))
639 {
640 fNeedsFilePath = false;
641 fNeedsLocation = true;
642 // also requires @format attribute, which will be queried below
643 }
644 else
645 throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName());
646
647 if (fNeedsFilePath)
648 if (!(pelmImage->getAttributeValue("filePath", med.strLocation)))
649 throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName());
650 }
651
652 if (med.strFormat.isEmpty()) // not set with 1.4 format above, or 1.4 Custom format?
653 if (!(elmMedium.getAttributeValue("format", med.strFormat)))
654 throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName());
655
656 if (!(elmMedium.getAttributeValue("autoReset", med.fAutoReset)))
657 med.fAutoReset = false;
658
659 Utf8Str strType;
660 if ((elmMedium.getAttributeValue("type", strType)))
661 {
662 // pre-1.4 used lower case, so make this case-insensitive
663 strType.toUpper();
664 if (strType == "NORMAL")
665 med.hdType = MediumType_Normal;
666 else if (strType == "IMMUTABLE")
667 med.hdType = MediumType_Immutable;
668 else if (strType == "WRITETHROUGH")
669 med.hdType = MediumType_Writethrough;
670 else if (strType == "SHAREABLE")
671 med.hdType = MediumType_Shareable;
672 else if (strType == "READONLY")
673 med.hdType = MediumType_Readonly;
674 else
675 throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable or Writethrough"));
676 }
677 }
678 else
679 {
680 if (m->sv < SettingsVersion_v1_4)
681 {
682 // DVD and floppy images before 1.4 had "src" attribute instead of "location"
683 if (!(elmMedium.getAttributeValue("src", med.strLocation)))
684 throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName());
685
686 fNeedsLocation = false;
687 }
688
689 if (!(elmMedium.getAttributeValue("format", med.strFormat)))
690 {
691 // DVD and floppy images before 1.11 had no format attribute. assign the default.
692 med.strFormat = "RAW";
693 }
694 }
695
696 if (fNeedsLocation)
697 // current files and 1.4 CustomHardDisk elements must have a location attribute
698 if (!(elmMedium.getAttributeValue("location", med.strLocation)))
699 throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName());
700
701 elmMedium.getAttributeValue("Description", med.strDescription); // optional
702
703 // recurse to handle children
704 xml::NodesLoop nl2(elmMedium);
705 const xml::ElementNode *pelmHDChild;
706 while ((pelmHDChild = nl2.forAllNodes()))
707 {
708 if ( t == HardDisk
709 && ( pelmHDChild->nameEquals("HardDisk")
710 || ( (m->sv < SettingsVersion_v1_4)
711 && (pelmHDChild->nameEquals("DiffHardDisk"))
712 )
713 )
714 )
715 // recurse with this element and push the child onto our current children list
716 readMedium(t,
717 *pelmHDChild,
718 med.llChildren);
719 else if (pelmHDChild->nameEquals("Property"))
720 {
721 Utf8Str strPropName, strPropValue;
722 if ( (pelmHDChild->getAttributeValue("name", strPropName))
723 && (pelmHDChild->getAttributeValue("value", strPropValue))
724 )
725 med.properties[strPropName] = strPropValue;
726 else
727 throw ConfigFileError(this, pelmHDChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
728 }
729 }
730
731 llMedia.push_back(med);
732}
733
734/**
735 * Reads in the entire <MediaRegistry> chunk and stores its media in the lists
736 * of the given MediaRegistry structure.
737 *
738 * This is used in both MainConfigFile and MachineConfigFile since starting with
739 * VirtualBox 4.0, we can have media registries in both.
740 *
741 * For pre-1.4 files, this gets called with the <DiskRegistry> chunk instead.
742 *
743 * @param elmMediaRegistry
744 */
745void ConfigFileBase::readMediaRegistry(const xml::ElementNode &elmMediaRegistry,
746 MediaRegistry &mr)
747{
748 xml::NodesLoop nl1(elmMediaRegistry);
749 const xml::ElementNode *pelmChild1;
750 while ((pelmChild1 = nl1.forAllNodes()))
751 {
752 MediaType t = Error;
753 if (pelmChild1->nameEquals("HardDisks"))
754 t = HardDisk;
755 else if (pelmChild1->nameEquals("DVDImages"))
756 t = DVDImage;
757 else if (pelmChild1->nameEquals("FloppyImages"))
758 t = FloppyImage;
759 else
760 continue;
761
762 xml::NodesLoop nl2(*pelmChild1);
763 const xml::ElementNode *pelmMedium;
764 while ((pelmMedium = nl2.forAllNodes()))
765 {
766 if ( t == HardDisk
767 && (pelmMedium->nameEquals("HardDisk"))
768 )
769 readMedium(t,
770 *pelmMedium,
771 mr.llHardDisks); // list to append hard disk data to: the root list
772 else if ( t == DVDImage
773 && (pelmMedium->nameEquals("Image"))
774 )
775 readMedium(t,
776 *pelmMedium,
777 mr.llDvdImages); // list to append dvd images to: the root list
778 else if ( t == FloppyImage
779 && (pelmMedium->nameEquals("Image"))
780 )
781 readMedium(t,
782 *pelmMedium,
783 mr.llFloppyImages); // list to append floppy images to: the root list
784 }
785 }
786}
787
788/**
789 * Adds a "version" attribute to the given XML element with the
790 * VirtualBox settings version (e.g. "1.10-linux"). Used by
791 * the XML format for the root element and by the OVF export
792 * for the vbox:Machine element.
793 * @param elm
794 */
795void ConfigFileBase::setVersionAttribute(xml::ElementNode &elm)
796{
797 const char *pcszVersion = NULL;
798 switch (m->sv)
799 {
800 case SettingsVersion_v1_8:
801 pcszVersion = "1.8";
802 break;
803
804 case SettingsVersion_v1_9:
805 pcszVersion = "1.9";
806 break;
807
808 case SettingsVersion_v1_10:
809 pcszVersion = "1.10";
810 break;
811
812 case SettingsVersion_v1_11:
813 pcszVersion = "1.11";
814 break;
815
816 case SettingsVersion_Future:
817 // can be set if this code runs on XML files that were created by a future version of VBox;
818 // in that case, downgrade to current version when writing since we can't write future versions...
819 pcszVersion = "1.11";
820 m->sv = SettingsVersion_v1_10;
821 break;
822
823 default:
824 // silently upgrade if this is less than 1.7 because that's the oldest we can write
825 pcszVersion = "1.7";
826 m->sv = SettingsVersion_v1_7;
827 break;
828 }
829
830 elm.setAttribute("version", Utf8StrFmt("%s-%s",
831 pcszVersion,
832 VBOX_XML_PLATFORM)); // e.g. "linux"
833}
834
835/**
836 * Creates a new stub xml::Document in the m->pDoc member with the
837 * root "VirtualBox" element set up. This is used by both
838 * MainConfigFile and MachineConfigFile at the beginning of writing
839 * out their XML.
840 *
841 * Before calling this, it is the responsibility of the caller to
842 * set the "sv" member to the required settings version that is to
843 * be written. For newly created files, the settings version will be
844 * the latest (1.11); for files read in from disk earlier, it will be
845 * the settings version indicated in the file. However, this method
846 * will silently make sure that the settings version is always
847 * at least 1.7 and change it if necessary, since there is no write
848 * support for earlier settings versions.
849 */
850void ConfigFileBase::createStubDocument()
851{
852 Assert(m->pDoc == NULL);
853 m->pDoc = new xml::Document;
854
855 m->pelmRoot = m->pDoc->createRootElement("VirtualBox");
856 m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
857
858 // add settings version attribute to root element
859 setVersionAttribute(*m->pelmRoot);
860
861 // since this gets called before the XML document is actually written out,
862 // this is where we must check whether we're upgrading the settings version
863 // and need to make a backup, so the user can go back to an earlier
864 // VirtualBox version and recover his old settings files.
865 if ( (m->svRead != SettingsVersion_Null) // old file exists?
866 && (m->svRead < m->sv) // we're upgrading?
867 )
868 {
869 // compose new filename: strip off trailing ".xml"/".vbox"
870 Utf8Str strFilenameNew;
871 Utf8Str strExt = ".xml";
872 if (m->strFilename.endsWith(".xml"))
873 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 4);
874 else if (m->strFilename.endsWith(".vbox"))
875 {
876 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 5);
877 strExt = ".vbox";
878 }
879
880 // and append something like "-1.3-linux.xml"
881 strFilenameNew.append("-");
882 strFilenameNew.append(m->strSettingsVersionFull); // e.g. "1.3-linux"
883 strFilenameNew.append(strExt); // .xml for main config, .vbox for machine config
884
885 RTFileMove(m->strFilename.c_str(),
886 strFilenameNew.c_str(),
887 0); // no RTFILEMOVE_FLAGS_REPLACE
888
889 // do this only once
890 m->svRead = SettingsVersion_Null;
891 }
892}
893
894/**
895 * Creates an <ExtraData> node under the given parent element with
896 * <ExtraDataItem> childern according to the contents of the given
897 * map.
898 *
899 * This is in ConfigFileBase because it's used in both MainConfigFile
900 * and MachineConfigFile, which both can have extradata.
901 *
902 * @param elmParent
903 * @param me
904 */
905void ConfigFileBase::buildExtraData(xml::ElementNode &elmParent,
906 const StringsMap &me)
907{
908 if (me.size())
909 {
910 xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
911 for (StringsMap::const_iterator it = me.begin();
912 it != me.end();
913 ++it)
914 {
915 const Utf8Str &strName = it->first;
916 const Utf8Str &strValue = it->second;
917 xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
918 pelmThis->setAttribute("name", strName);
919 pelmThis->setAttribute("value", strValue);
920 }
921 }
922}
923
924/**
925 * Creates <DeviceFilter> nodes under the given parent element according to
926 * the contents of the given USBDeviceFiltersList. This is in ConfigFileBase
927 * because it's used in both MainConfigFile (for host filters) and
928 * MachineConfigFile (for machine filters).
929 *
930 * If fHostMode is true, this means that we're supposed to write filters
931 * for the IHost interface (respect "action", omit "strRemote" and
932 * "ulMaskedInterfaces" in struct USBDeviceFilter).
933 *
934 * @param elmParent
935 * @param ll
936 * @param fHostMode
937 */
938void ConfigFileBase::buildUSBDeviceFilters(xml::ElementNode &elmParent,
939 const USBDeviceFiltersList &ll,
940 bool fHostMode)
941{
942 for (USBDeviceFiltersList::const_iterator it = ll.begin();
943 it != ll.end();
944 ++it)
945 {
946 const USBDeviceFilter &flt = *it;
947 xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
948 pelmFilter->setAttribute("name", flt.strName);
949 pelmFilter->setAttribute("active", flt.fActive);
950 if (flt.strVendorId.length())
951 pelmFilter->setAttribute("vendorId", flt.strVendorId);
952 if (flt.strProductId.length())
953 pelmFilter->setAttribute("productId", flt.strProductId);
954 if (flt.strRevision.length())
955 pelmFilter->setAttribute("revision", flt.strRevision);
956 if (flt.strManufacturer.length())
957 pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
958 if (flt.strProduct.length())
959 pelmFilter->setAttribute("product", flt.strProduct);
960 if (flt.strSerialNumber.length())
961 pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
962 if (flt.strPort.length())
963 pelmFilter->setAttribute("port", flt.strPort);
964
965 if (fHostMode)
966 {
967 const char *pcsz =
968 (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
969 : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
970 pelmFilter->setAttribute("action", pcsz);
971 }
972 else
973 {
974 if (flt.strRemote.length())
975 pelmFilter->setAttribute("remote", flt.strRemote);
976 if (flt.ulMaskedInterfaces)
977 pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
978 }
979 }
980}
981
982/**
983 * Creates a single <HardDisk> element for the given Medium structure
984 * and recurses to write the child hard disks underneath. Called from
985 * MainConfigFile::write().
986 *
987 * @param elmMedium
988 * @param m
989 * @param level
990 */
991void ConfigFileBase::buildMedium(xml::ElementNode &elmMedium,
992 DeviceType_T devType,
993 const Medium &mdm,
994 uint32_t level) // 0 for "root" call, incremented with each recursion
995{
996 xml::ElementNode *pelmMedium;
997
998 if (devType == DeviceType_HardDisk)
999 pelmMedium = elmMedium.createChild("HardDisk");
1000 else
1001 pelmMedium = elmMedium.createChild("Image");
1002
1003 pelmMedium->setAttribute("uuid", mdm.uuid.toStringCurly());
1004 pelmMedium->setAttribute("location", mdm.strLocation);
1005 pelmMedium->setAttribute("format", mdm.strFormat);
1006 if (mdm.fAutoReset)
1007 pelmMedium->setAttribute("autoReset", mdm.fAutoReset);
1008 if (mdm.strDescription.length())
1009 pelmMedium->setAttribute("Description", mdm.strDescription);
1010
1011 for (StringsMap::const_iterator it = mdm.properties.begin();
1012 it != mdm.properties.end();
1013 ++it)
1014 {
1015 xml::ElementNode *pelmProp = pelmMedium->createChild("Property");
1016 pelmProp->setAttribute("name", it->first);
1017 pelmProp->setAttribute("value", it->second);
1018 }
1019
1020 // only for base hard disks, save the type
1021 if (level == 0)
1022 {
1023 const char *pcszType =
1024 mdm.hdType == MediumType_Normal ? "Normal" :
1025 mdm.hdType == MediumType_Immutable ? "Immutable" :
1026 mdm.hdType == MediumType_Writethrough ? "Writethrough" :
1027 mdm.hdType == MediumType_Shareable ? "Shareable" :
1028 mdm.hdType == MediumType_Readonly ? "Readonly" : "INVALID";
1029 pelmMedium->setAttribute("type", pcszType);
1030 }
1031
1032 for (MediaList::const_iterator it = mdm.llChildren.begin();
1033 it != mdm.llChildren.end();
1034 ++it)
1035 {
1036 // recurse for children
1037 buildMedium(*pelmMedium, // parent
1038 devType, // device type
1039 *it, // settings::Medium
1040 ++level); // recursion level
1041 }
1042}
1043
1044/**
1045 * Creates a <MediaRegistry> node under the given parent and writes out all
1046 * hard disks and DVD and floppy images from the lists in the given MediaRegistry
1047 * structure under it.
1048 *
1049 * This is used in both MainConfigFile and MachineConfigFile since starting with
1050 * VirtualBox 4.0, we can have media registries in both.
1051 *
1052 * @param elmParent
1053 * @param mr
1054 */
1055void ConfigFileBase::buildMediaRegistry(xml::ElementNode &elmParent,
1056 const MediaRegistry &mr)
1057{
1058 xml::ElementNode *pelmMediaRegistry = elmParent.createChild("MediaRegistry");
1059
1060 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
1061 for (MediaList::const_iterator it = mr.llHardDisks.begin();
1062 it != mr.llHardDisks.end();
1063 ++it)
1064 {
1065 buildMedium(*pelmHardDisks, DeviceType_HardDisk, *it, 0);
1066 }
1067
1068 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
1069 for (MediaList::const_iterator it = mr.llDvdImages.begin();
1070 it != mr.llDvdImages.end();
1071 ++it)
1072 {
1073 buildMedium(*pelmDVDImages, DeviceType_DVD, *it, 0);
1074 }
1075
1076 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
1077 for (MediaList::const_iterator it = mr.llFloppyImages.begin();
1078 it != mr.llFloppyImages.end();
1079 ++it)
1080 {
1081 buildMedium(*pelmFloppyImages, DeviceType_Floppy, *it, 0);
1082 }
1083}
1084
1085/**
1086 * Cleans up memory allocated by the internal XML parser. To be called by
1087 * descendant classes when they're done analyzing the DOM tree to discard it.
1088 */
1089void ConfigFileBase::clearDocument()
1090{
1091 m->cleanup();
1092}
1093
1094/**
1095 * Returns true only if the underlying config file exists on disk;
1096 * either because the file has been loaded from disk, or it's been written
1097 * to disk, or both.
1098 * @return
1099 */
1100bool ConfigFileBase::fileExists()
1101{
1102 return m->fFileExists;
1103}
1104
1105/**
1106 * Copies the base variables from another instance. Used by Machine::saveSettings
1107 * so that the settings version does not get lost when a copy of the Machine settings
1108 * file is made to see if settings have actually changed.
1109 * @param b
1110 */
1111void ConfigFileBase::copyBaseFrom(const ConfigFileBase &b)
1112{
1113 m->copyFrom(*b.m);
1114}
1115
1116////////////////////////////////////////////////////////////////////////////////
1117//
1118// Structures shared between Machine XML and VirtualBox.xml
1119//
1120////////////////////////////////////////////////////////////////////////////////
1121
1122/**
1123 * Comparison operator. This gets called from MachineConfigFile::operator==,
1124 * which in turn gets called from Machine::saveSettings to figure out whether
1125 * machine settings have really changed and thus need to be written out to disk.
1126 */
1127bool USBDeviceFilter::operator==(const USBDeviceFilter &u) const
1128{
1129 return ( (this == &u)
1130 || ( (strName == u.strName)
1131 && (fActive == u.fActive)
1132 && (strVendorId == u.strVendorId)
1133 && (strProductId == u.strProductId)
1134 && (strRevision == u.strRevision)
1135 && (strManufacturer == u.strManufacturer)
1136 && (strProduct == u.strProduct)
1137 && (strSerialNumber == u.strSerialNumber)
1138 && (strPort == u.strPort)
1139 && (action == u.action)
1140 && (strRemote == u.strRemote)
1141 && (ulMaskedInterfaces == u.ulMaskedInterfaces)
1142 )
1143 );
1144}
1145
1146////////////////////////////////////////////////////////////////////////////////
1147//
1148// MainConfigFile
1149//
1150////////////////////////////////////////////////////////////////////////////////
1151
1152/**
1153 * Reads one <MachineEntry> from the main VirtualBox.xml file.
1154 * @param elmMachineRegistry
1155 */
1156void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
1157{
1158 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
1159 xml::NodesLoop nl1(elmMachineRegistry);
1160 const xml::ElementNode *pelmChild1;
1161 while ((pelmChild1 = nl1.forAllNodes()))
1162 {
1163 if (pelmChild1->nameEquals("MachineEntry"))
1164 {
1165 MachineRegistryEntry mre;
1166 Utf8Str strUUID;
1167 if ( ((pelmChild1->getAttributeValue("uuid", strUUID)))
1168 && ((pelmChild1->getAttributeValue("src", mre.strSettingsFile)))
1169 )
1170 {
1171 parseUUID(mre.uuid, strUUID);
1172 llMachines.push_back(mre);
1173 }
1174 else
1175 throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing"));
1176 }
1177 }
1178}
1179
1180/**
1181 * Reads in the <DHCPServers> chunk.
1182 * @param elmDHCPServers
1183 */
1184void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
1185{
1186 xml::NodesLoop nl1(elmDHCPServers);
1187 const xml::ElementNode *pelmServer;
1188 while ((pelmServer = nl1.forAllNodes()))
1189 {
1190 if (pelmServer->nameEquals("DHCPServer"))
1191 {
1192 DHCPServer srv;
1193 if ( (pelmServer->getAttributeValue("networkName", srv.strNetworkName))
1194 && (pelmServer->getAttributeValue("IPAddress", srv.strIPAddress))
1195 && (pelmServer->getAttributeValue("networkMask", srv.strIPNetworkMask))
1196 && (pelmServer->getAttributeValue("lowerIP", srv.strIPLower))
1197 && (pelmServer->getAttributeValue("upperIP", srv.strIPUpper))
1198 && (pelmServer->getAttributeValue("enabled", srv.fEnabled))
1199 )
1200 llDhcpServers.push_back(srv);
1201 else
1202 throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing"));
1203 }
1204 }
1205}
1206
1207/**
1208 * Constructor.
1209 *
1210 * If pstrFilename is != NULL, this reads the given settings file into the member
1211 * variables and various substructures and lists. Otherwise, the member variables
1212 * are initialized with default values.
1213 *
1214 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1215 * the caller should catch; if this constructor does not throw, then the member
1216 * variables contain meaningful values (either from the file or defaults).
1217 *
1218 * @param strFilename
1219 */
1220MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
1221 : ConfigFileBase(pstrFilename)
1222{
1223 if (pstrFilename)
1224 {
1225 // the ConfigFileBase constructor has loaded the XML file, so now
1226 // we need only analyze what is in there
1227 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1228 const xml::ElementNode *pelmRootChild;
1229 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1230 {
1231 if (pelmRootChild->nameEquals("Global"))
1232 {
1233 xml::NodesLoop nlGlobalChildren(*pelmRootChild);
1234 const xml::ElementNode *pelmGlobalChild;
1235 while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
1236 {
1237 if (pelmGlobalChild->nameEquals("SystemProperties"))
1238 {
1239 pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1240 pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1241 if (!pelmGlobalChild->getAttributeValue("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary))
1242 // pre-1.11 used @remoteDisplayAuthLibrary instead
1243 pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strVRDEAuthLibrary);
1244 pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1245 pelmGlobalChild->getAttributeValue("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
1246 pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.ulLogHistoryCount);
1247 }
1248 else if (pelmGlobalChild->nameEquals("ExtraData"))
1249 readExtraData(*pelmGlobalChild, mapExtraDataItems);
1250 else if (pelmGlobalChild->nameEquals("MachineRegistry"))
1251 readMachineRegistry(*pelmGlobalChild);
1252 else if ( (pelmGlobalChild->nameEquals("MediaRegistry"))
1253 || ( (m->sv < SettingsVersion_v1_4)
1254 && (pelmGlobalChild->nameEquals("DiskRegistry"))
1255 )
1256 )
1257 readMediaRegistry(*pelmGlobalChild, mediaRegistry);
1258 else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
1259 {
1260 xml::NodesLoop nlLevel4(*pelmGlobalChild);
1261 const xml::ElementNode *pelmLevel4Child;
1262 while ((pelmLevel4Child = nlLevel4.forAllNodes()))
1263 {
1264 if (pelmLevel4Child->nameEquals("DHCPServers"))
1265 readDHCPServers(*pelmLevel4Child);
1266 }
1267 }
1268 else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
1269 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
1270 }
1271 } // end if (pelmRootChild->nameEquals("Global"))
1272 }
1273
1274 clearDocument();
1275 }
1276
1277 // DHCP servers were introduced with settings version 1.7; if we're loading
1278 // from an older version OR this is a fresh install, then add one DHCP server
1279 // with default settings
1280 if ( (!llDhcpServers.size())
1281 && ( (!pstrFilename) // empty VirtualBox.xml file
1282 || (m->sv < SettingsVersion_v1_7) // upgrading from before 1.7
1283 )
1284 )
1285 {
1286 DHCPServer srv;
1287 srv.strNetworkName =
1288#ifdef RT_OS_WINDOWS
1289 "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
1290#else
1291 "HostInterfaceNetworking-vboxnet0";
1292#endif
1293 srv.strIPAddress = "192.168.56.100";
1294 srv.strIPNetworkMask = "255.255.255.0";
1295 srv.strIPLower = "192.168.56.101";
1296 srv.strIPUpper = "192.168.56.254";
1297 srv.fEnabled = true;
1298 llDhcpServers.push_back(srv);
1299 }
1300}
1301
1302/**
1303 * Called from the IVirtualBox interface to write out VirtualBox.xml. This
1304 * builds an XML DOM tree and writes it out to disk.
1305 */
1306void MainConfigFile::write(const com::Utf8Str strFilename)
1307{
1308 m->strFilename = strFilename;
1309 createStubDocument();
1310
1311 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
1312
1313 buildExtraData(*pelmGlobal, mapExtraDataItems);
1314
1315 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
1316 for (MachinesRegistry::const_iterator it = llMachines.begin();
1317 it != llMachines.end();
1318 ++it)
1319 {
1320 // <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"/>
1321 const MachineRegistryEntry &mre = *it;
1322 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
1323 pelmMachineEntry->setAttribute("uuid", mre.uuid.toStringCurly());
1324 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
1325 }
1326
1327 buildMediaRegistry(*pelmGlobal, mediaRegistry);
1328
1329 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
1330 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
1331 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
1332 it != llDhcpServers.end();
1333 ++it)
1334 {
1335 const DHCPServer &d = *it;
1336 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
1337 pelmThis->setAttribute("networkName", d.strNetworkName);
1338 pelmThis->setAttribute("IPAddress", d.strIPAddress);
1339 pelmThis->setAttribute("networkMask", d.strIPNetworkMask);
1340 pelmThis->setAttribute("lowerIP", d.strIPLower);
1341 pelmThis->setAttribute("upperIP", d.strIPUpper);
1342 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
1343 }
1344
1345 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
1346 if (systemProperties.strDefaultMachineFolder.length())
1347 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1348 if (systemProperties.strDefaultHardDiskFormat.length())
1349 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1350 if (systemProperties.strVRDEAuthLibrary.length())
1351 pelmSysProps->setAttribute("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary);
1352 if (systemProperties.strWebServiceAuthLibrary.length())
1353 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1354 if (systemProperties.strDefaultVRDEExtPack.length())
1355 pelmSysProps->setAttribute("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
1356 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.ulLogHistoryCount);
1357
1358 buildUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
1359 host.llUSBDeviceFilters,
1360 true); // fHostMode
1361
1362 // now go write the XML
1363 xml::XmlFileWriter writer(*m->pDoc);
1364 writer.write(m->strFilename.c_str(), true /*fSafe*/);
1365
1366 m->fFileExists = true;
1367
1368 clearDocument();
1369}
1370
1371////////////////////////////////////////////////////////////////////////////////
1372//
1373// Machine XML structures
1374//
1375////////////////////////////////////////////////////////////////////////////////
1376
1377/**
1378 * Comparison operator. This gets called from MachineConfigFile::operator==,
1379 * which in turn gets called from Machine::saveSettings to figure out whether
1380 * machine settings have really changed and thus need to be written out to disk.
1381 */
1382bool VRDESettings::operator==(const VRDESettings& v) const
1383{
1384 return ( (this == &v)
1385 || ( (fEnabled == v.fEnabled)
1386 && (authType == v.authType)
1387 && (ulAuthTimeout == v.ulAuthTimeout)
1388 && (strAuthLibrary == v.strAuthLibrary)
1389 && (fAllowMultiConnection == v.fAllowMultiConnection)
1390 && (fReuseSingleConnection == v.fReuseSingleConnection)
1391 && (fVideoChannel == v.fVideoChannel)
1392 && (ulVideoChannelQuality == v.ulVideoChannelQuality)
1393 && (strVrdeExtPack == v.strVrdeExtPack)
1394 && (mapProperties == v.mapProperties)
1395 )
1396 );
1397}
1398
1399/**
1400 * Comparison operator. This gets called from MachineConfigFile::operator==,
1401 * which in turn gets called from Machine::saveSettings to figure out whether
1402 * machine settings have really changed and thus need to be written out to disk.
1403 */
1404bool BIOSSettings::operator==(const BIOSSettings &d) const
1405{
1406 return ( (this == &d)
1407 || ( fACPIEnabled == d.fACPIEnabled
1408 && fIOAPICEnabled == d.fIOAPICEnabled
1409 && fLogoFadeIn == d.fLogoFadeIn
1410 && fLogoFadeOut == d.fLogoFadeOut
1411 && ulLogoDisplayTime == d.ulLogoDisplayTime
1412 && strLogoImagePath == d.strLogoImagePath
1413 && biosBootMenuMode == d.biosBootMenuMode
1414 && fPXEDebugEnabled == d.fPXEDebugEnabled
1415 && llTimeOffset == d.llTimeOffset)
1416 );
1417}
1418
1419/**
1420 * Comparison operator. This gets called from MachineConfigFile::operator==,
1421 * which in turn gets called from Machine::saveSettings to figure out whether
1422 * machine settings have really changed and thus need to be written out to disk.
1423 */
1424bool USBController::operator==(const USBController &u) const
1425{
1426 return ( (this == &u)
1427 || ( (fEnabled == u.fEnabled)
1428 && (fEnabledEHCI == u.fEnabledEHCI)
1429 && (llDeviceFilters == u.llDeviceFilters)
1430 )
1431 );
1432}
1433
1434/**
1435 * Comparison operator. This gets called from MachineConfigFile::operator==,
1436 * which in turn gets called from Machine::saveSettings to figure out whether
1437 * machine settings have really changed and thus need to be written out to disk.
1438 */
1439bool NetworkAdapter::operator==(const NetworkAdapter &n) const
1440{
1441 return ( (this == &n)
1442 || ( (ulSlot == n.ulSlot)
1443 && (type == n.type)
1444 && (fEnabled == n.fEnabled)
1445 && (strMACAddress == n.strMACAddress)
1446 && (fCableConnected == n.fCableConnected)
1447 && (ulLineSpeed == n.ulLineSpeed)
1448 && (fTraceEnabled == n.fTraceEnabled)
1449 && (strTraceFile == n.strTraceFile)
1450 && (mode == n.mode)
1451 && (nat == n.nat)
1452 && (strName == n.strName)
1453 && (ulBootPriority == n.ulBootPriority)
1454 && (fHasDisabledNAT == n.fHasDisabledNAT)
1455 )
1456 );
1457}
1458
1459/**
1460 * Comparison operator. This gets called from MachineConfigFile::operator==,
1461 * which in turn gets called from Machine::saveSettings to figure out whether
1462 * machine settings have really changed and thus need to be written out to disk.
1463 */
1464bool SerialPort::operator==(const SerialPort &s) const
1465{
1466 return ( (this == &s)
1467 || ( (ulSlot == s.ulSlot)
1468 && (fEnabled == s.fEnabled)
1469 && (ulIOBase == s.ulIOBase)
1470 && (ulIRQ == s.ulIRQ)
1471 && (portMode == s.portMode)
1472 && (strPath == s.strPath)
1473 && (fServer == s.fServer)
1474 )
1475 );
1476}
1477
1478/**
1479 * Comparison operator. This gets called from MachineConfigFile::operator==,
1480 * which in turn gets called from Machine::saveSettings to figure out whether
1481 * machine settings have really changed and thus need to be written out to disk.
1482 */
1483bool ParallelPort::operator==(const ParallelPort &s) const
1484{
1485 return ( (this == &s)
1486 || ( (ulSlot == s.ulSlot)
1487 && (fEnabled == s.fEnabled)
1488 && (ulIOBase == s.ulIOBase)
1489 && (ulIRQ == s.ulIRQ)
1490 && (strPath == s.strPath)
1491 )
1492 );
1493}
1494
1495/**
1496 * Comparison operator. This gets called from MachineConfigFile::operator==,
1497 * which in turn gets called from Machine::saveSettings to figure out whether
1498 * machine settings have really changed and thus need to be written out to disk.
1499 */
1500bool SharedFolder::operator==(const SharedFolder &g) const
1501{
1502 return ( (this == &g)
1503 || ( (strName == g.strName)
1504 && (strHostPath == g.strHostPath)
1505 && (fWritable == g.fWritable)
1506 && (fAutoMount == g.fAutoMount)
1507 )
1508 );
1509}
1510
1511/**
1512 * Comparison operator. This gets called from MachineConfigFile::operator==,
1513 * which in turn gets called from Machine::saveSettings to figure out whether
1514 * machine settings have really changed and thus need to be written out to disk.
1515 */
1516bool GuestProperty::operator==(const GuestProperty &g) const
1517{
1518 return ( (this == &g)
1519 || ( (strName == g.strName)
1520 && (strValue == g.strValue)
1521 && (timestamp == g.timestamp)
1522 && (strFlags == g.strFlags)
1523 )
1524 );
1525}
1526
1527// use a define for the platform-dependent default value of
1528// hwvirt exclusivity, since we'll need to check that value
1529// in bumpSettingsVersionIfNeeded()
1530#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
1531 #define HWVIRTEXCLUSIVEDEFAULT false
1532#else
1533 #define HWVIRTEXCLUSIVEDEFAULT true
1534#endif
1535
1536/**
1537 * Hardware struct constructor.
1538 */
1539Hardware::Hardware()
1540 : strVersion("1"),
1541 fHardwareVirt(true),
1542 fHardwareVirtExclusive(HWVIRTEXCLUSIVEDEFAULT),
1543 fNestedPaging(true),
1544 fVPID(true),
1545 fHardwareVirtForce(false),
1546 fSyntheticCpu(false),
1547 fPAE(false),
1548 cCPUs(1),
1549 fCpuHotPlug(false),
1550 fHpetEnabled(false),
1551 ulCpuExecutionCap(100),
1552 ulMemorySizeMB((uint32_t)-1),
1553 ulVRAMSizeMB(8),
1554 cMonitors(1),
1555 fAccelerate3D(false),
1556 fAccelerate2DVideo(false),
1557 firmwareType(FirmwareType_BIOS),
1558 pointingHidType(PointingHidType_PS2Mouse),
1559 keyboardHidType(KeyboardHidType_PS2Keyboard),
1560 chipsetType(ChipsetType_PIIX3),
1561 clipboardMode(ClipboardMode_Bidirectional),
1562 ulMemoryBalloonSize(0),
1563 fPageFusionEnabled(false)
1564{
1565 mapBootOrder[0] = DeviceType_Floppy;
1566 mapBootOrder[1] = DeviceType_DVD;
1567 mapBootOrder[2] = DeviceType_HardDisk;
1568
1569 /* The default value for PAE depends on the host:
1570 * - 64 bits host -> always true
1571 * - 32 bits host -> true for Windows & Darwin (masked off if the host cpu doesn't support it anyway)
1572 */
1573#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
1574 fPAE = true;
1575#endif
1576
1577 /* The default value of large page supports depends on the host:
1578 * - 64 bits host -> true, unless it's Linux (pending further prediction work due to excessively expensive large page allocations)
1579 * - 32 bits host -> false
1580 */
1581#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
1582 fLargePages = true;
1583#else
1584 /* Not supported on 32 bits hosts. */
1585 fLargePages = false;
1586#endif
1587}
1588
1589/**
1590 * Comparison operator. This gets called from MachineConfigFile::operator==,
1591 * which in turn gets called from Machine::saveSettings to figure out whether
1592 * machine settings have really changed and thus need to be written out to disk.
1593 */
1594bool Hardware::operator==(const Hardware& h) const
1595{
1596 return ( (this == &h)
1597 || ( (strVersion == h.strVersion)
1598 && (uuid == h.uuid)
1599 && (fHardwareVirt == h.fHardwareVirt)
1600 && (fHardwareVirtExclusive == h.fHardwareVirtExclusive)
1601 && (fNestedPaging == h.fNestedPaging)
1602 && (fLargePages == h.fLargePages)
1603 && (fVPID == h.fVPID)
1604 && (fHardwareVirtForce == h.fHardwareVirtForce)
1605 && (fSyntheticCpu == h.fSyntheticCpu)
1606 && (fPAE == h.fPAE)
1607 && (cCPUs == h.cCPUs)
1608 && (fCpuHotPlug == h.fCpuHotPlug)
1609 && (ulCpuExecutionCap == h.ulCpuExecutionCap)
1610 && (fHpetEnabled == h.fHpetEnabled)
1611 && (llCpus == h.llCpus)
1612 && (llCpuIdLeafs == h.llCpuIdLeafs)
1613 && (ulMemorySizeMB == h.ulMemorySizeMB)
1614 && (mapBootOrder == h.mapBootOrder)
1615 && (ulVRAMSizeMB == h.ulVRAMSizeMB)
1616 && (cMonitors == h.cMonitors)
1617 && (fAccelerate3D == h.fAccelerate3D)
1618 && (fAccelerate2DVideo == h.fAccelerate2DVideo)
1619 && (firmwareType == h.firmwareType)
1620 && (pointingHidType == h.pointingHidType)
1621 && (keyboardHidType == h.keyboardHidType)
1622 && (chipsetType == h.chipsetType)
1623 && (vrdeSettings == h.vrdeSettings)
1624 && (biosSettings == h.biosSettings)
1625 && (usbController == h.usbController)
1626 && (llNetworkAdapters == h.llNetworkAdapters)
1627 && (llSerialPorts == h.llSerialPorts)
1628 && (llParallelPorts == h.llParallelPorts)
1629 && (audioAdapter == h.audioAdapter)
1630 && (llSharedFolders == h.llSharedFolders)
1631 && (clipboardMode == h.clipboardMode)
1632 && (ulMemoryBalloonSize == h.ulMemoryBalloonSize)
1633 && (fPageFusionEnabled == h.fPageFusionEnabled)
1634 && (llGuestProperties == h.llGuestProperties)
1635 && (strNotificationPatterns == h.strNotificationPatterns)
1636 && (ioSettings == h.ioSettings)
1637 )
1638 );
1639}
1640
1641/**
1642 * Comparison operator. This gets called from MachineConfigFile::operator==,
1643 * which in turn gets called from Machine::saveSettings to figure out whether
1644 * machine settings have really changed and thus need to be written out to disk.
1645 */
1646bool AttachedDevice::operator==(const AttachedDevice &a) const
1647{
1648 return ( (this == &a)
1649 || ( (deviceType == a.deviceType)
1650 && (fPassThrough == a.fPassThrough)
1651 && (lPort == a.lPort)
1652 && (lDevice == a.lDevice)
1653 && (uuid == a.uuid)
1654 && (strHostDriveSrc == a.strHostDriveSrc)
1655 && (strBwGroup == a.strBwGroup)
1656 )
1657 );
1658}
1659
1660/**
1661 * Comparison operator. This gets called from MachineConfigFile::operator==,
1662 * which in turn gets called from Machine::saveSettings to figure out whether
1663 * machine settings have really changed and thus need to be written out to disk.
1664 */
1665bool StorageController::operator==(const StorageController &s) const
1666{
1667 return ( (this == &s)
1668 || ( (strName == s.strName)
1669 && (storageBus == s.storageBus)
1670 && (controllerType == s.controllerType)
1671 && (ulPortCount == s.ulPortCount)
1672 && (ulInstance == s.ulInstance)
1673 && (fUseHostIOCache == s.fUseHostIOCache)
1674 && (lIDE0MasterEmulationPort == s.lIDE0MasterEmulationPort)
1675 && (lIDE0SlaveEmulationPort == s.lIDE0SlaveEmulationPort)
1676 && (lIDE1MasterEmulationPort == s.lIDE1MasterEmulationPort)
1677 && (lIDE1SlaveEmulationPort == s.lIDE1SlaveEmulationPort)
1678 && (llAttachedDevices == s.llAttachedDevices)
1679 )
1680 );
1681}
1682
1683/**
1684 * Comparison operator. This gets called from MachineConfigFile::operator==,
1685 * which in turn gets called from Machine::saveSettings to figure out whether
1686 * machine settings have really changed and thus need to be written out to disk.
1687 */
1688bool Storage::operator==(const Storage &s) const
1689{
1690 return ( (this == &s)
1691 || (llStorageControllers == s.llStorageControllers) // deep compare
1692 );
1693}
1694
1695/**
1696 * Comparison operator. This gets called from MachineConfigFile::operator==,
1697 * which in turn gets called from Machine::saveSettings to figure out whether
1698 * machine settings have really changed and thus need to be written out to disk.
1699 */
1700bool Snapshot::operator==(const Snapshot &s) const
1701{
1702 return ( (this == &s)
1703 || ( (uuid == s.uuid)
1704 && (strName == s.strName)
1705 && (strDescription == s.strDescription)
1706 && (RTTimeSpecIsEqual(&timestamp, &s.timestamp))
1707 && (strStateFile == s.strStateFile)
1708 && (hardware == s.hardware) // deep compare
1709 && (storage == s.storage) // deep compare
1710 && (llChildSnapshots == s.llChildSnapshots) // deep compare
1711 )
1712 );
1713}
1714
1715/**
1716 * IoSettings constructor.
1717 */
1718IoSettings::IoSettings()
1719{
1720 fIoCacheEnabled = true;
1721 ulIoCacheSize = 5;
1722}
1723
1724////////////////////////////////////////////////////////////////////////////////
1725//
1726// MachineConfigFile
1727//
1728////////////////////////////////////////////////////////////////////////////////
1729
1730/**
1731 * Constructor.
1732 *
1733 * If pstrFilename is != NULL, this reads the given settings file into the member
1734 * variables and various substructures and lists. Otherwise, the member variables
1735 * are initialized with default values.
1736 *
1737 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1738 * the caller should catch; if this constructor does not throw, then the member
1739 * variables contain meaningful values (either from the file or defaults).
1740 *
1741 * @param strFilename
1742 */
1743MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
1744 : ConfigFileBase(pstrFilename),
1745 fCurrentStateModified(true),
1746 fAborted(false)
1747{
1748 RTTimeNow(&timeLastStateChange);
1749
1750 if (pstrFilename)
1751 {
1752 // the ConfigFileBase constructor has loaded the XML file, so now
1753 // we need only analyze what is in there
1754
1755 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1756 const xml::ElementNode *pelmRootChild;
1757 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1758 {
1759 if (pelmRootChild->nameEquals("Machine"))
1760 readMachine(*pelmRootChild);
1761 }
1762
1763 // clean up memory allocated by XML engine
1764 clearDocument();
1765 }
1766}
1767
1768/**
1769 * Public routine which returns true if this machine config file can have its
1770 * own media registry (which is true for settings version v1.11 and higher,
1771 * i.e. files created by VirtualBox 4.0 and higher).
1772 * @return
1773 */
1774bool MachineConfigFile::canHaveOwnMediaRegistry() const
1775{
1776 return (m->sv >= SettingsVersion_v1_11);
1777}
1778
1779/**
1780 * Public routine which allows for importing machine XML from an external DOM tree.
1781 * Use this after having called the constructor with a NULL argument.
1782 *
1783 * This is used by the OVF code if a <vbox:Machine> element has been encountered
1784 * in an OVF VirtualSystem element.
1785 *
1786 * @param elmMachine
1787 */
1788void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine)
1789{
1790 readMachine(elmMachine);
1791}
1792
1793/**
1794 * Comparison operator. This gets called from Machine::saveSettings to figure out
1795 * whether machine settings have really changed and thus need to be written out to disk.
1796 *
1797 * Even though this is called operator==, this does NOT compare all fields; the "equals"
1798 * should be understood as "has the same machine config as". The following fields are
1799 * NOT compared:
1800 * -- settings versions and file names inherited from ConfigFileBase;
1801 * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
1802 *
1803 * The "deep" comparisons marked below will invoke the operator== functions of the
1804 * structs defined in this file, which may in turn go into comparing lists of
1805 * other structures. As a result, invoking this can be expensive, but it's
1806 * less expensive than writing out XML to disk.
1807 */
1808bool MachineConfigFile::operator==(const MachineConfigFile &c) const
1809{
1810 return ( (this == &c)
1811 || ( (uuid == c.uuid)
1812 && (machineUserData == c.machineUserData)
1813 && (strStateFile == c.strStateFile)
1814 && (uuidCurrentSnapshot == c.uuidCurrentSnapshot)
1815 // skip fCurrentStateModified!
1816 && (RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange))
1817 && (fAborted == c.fAborted)
1818 && (hardwareMachine == c.hardwareMachine) // this one's deep
1819 && (storageMachine == c.storageMachine) // this one's deep
1820 && (mediaRegistry == c.mediaRegistry) // this one's deep
1821 && (mapExtraDataItems == c.mapExtraDataItems) // this one's deep
1822 && (llFirstSnapshot == c.llFirstSnapshot) // this one's deep
1823 )
1824 );
1825}
1826
1827/**
1828 * Called from MachineConfigFile::readHardware() to read cpu information.
1829 * @param elmCpuid
1830 * @param ll
1831 */
1832void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
1833 CpuList &ll)
1834{
1835 xml::NodesLoop nl1(elmCpu, "Cpu");
1836 const xml::ElementNode *pelmCpu;
1837 while ((pelmCpu = nl1.forAllNodes()))
1838 {
1839 Cpu cpu;
1840
1841 if (!pelmCpu->getAttributeValue("id", cpu.ulId))
1842 throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
1843
1844 ll.push_back(cpu);
1845 }
1846}
1847
1848/**
1849 * Called from MachineConfigFile::readHardware() to cpuid information.
1850 * @param elmCpuid
1851 * @param ll
1852 */
1853void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid,
1854 CpuIdLeafsList &ll)
1855{
1856 xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf");
1857 const xml::ElementNode *pelmCpuIdLeaf;
1858 while ((pelmCpuIdLeaf = nl1.forAllNodes()))
1859 {
1860 CpuIdLeaf leaf;
1861
1862 if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.ulId))
1863 throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing"));
1864
1865 pelmCpuIdLeaf->getAttributeValue("eax", leaf.ulEax);
1866 pelmCpuIdLeaf->getAttributeValue("ebx", leaf.ulEbx);
1867 pelmCpuIdLeaf->getAttributeValue("ecx", leaf.ulEcx);
1868 pelmCpuIdLeaf->getAttributeValue("edx", leaf.ulEdx);
1869
1870 ll.push_back(leaf);
1871 }
1872}
1873
1874/**
1875 * Called from MachineConfigFile::readHardware() to network information.
1876 * @param elmNetwork
1877 * @param ll
1878 */
1879void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
1880 NetworkAdaptersList &ll)
1881{
1882 xml::NodesLoop nl1(elmNetwork, "Adapter");
1883 const xml::ElementNode *pelmAdapter;
1884 while ((pelmAdapter = nl1.forAllNodes()))
1885 {
1886 NetworkAdapter nic;
1887
1888 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
1889 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
1890
1891 Utf8Str strTemp;
1892 if (pelmAdapter->getAttributeValue("type", strTemp))
1893 {
1894 if (strTemp == "Am79C970A")
1895 nic.type = NetworkAdapterType_Am79C970A;
1896 else if (strTemp == "Am79C973")
1897 nic.type = NetworkAdapterType_Am79C973;
1898 else if (strTemp == "82540EM")
1899 nic.type = NetworkAdapterType_I82540EM;
1900 else if (strTemp == "82543GC")
1901 nic.type = NetworkAdapterType_I82543GC;
1902 else if (strTemp == "82545EM")
1903 nic.type = NetworkAdapterType_I82545EM;
1904 else if (strTemp == "virtio")
1905 nic.type = NetworkAdapterType_Virtio;
1906 else
1907 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
1908 }
1909
1910 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
1911 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
1912 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
1913 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
1914 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
1915 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
1916 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
1917 pelmAdapter->getAttributeValue("bandwidthLimit", nic.ulBandwidthLimit);
1918
1919 xml::ElementNodesList llNetworkModes;
1920 pelmAdapter->getChildElements(llNetworkModes);
1921 xml::ElementNodesList::iterator it;
1922 /* We should have only active mode descriptor and disabled modes set */
1923 if (llNetworkModes.size() > 2)
1924 {
1925 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
1926 }
1927 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
1928 {
1929 const xml::ElementNode *pelmNode = *it;
1930 if (pelmNode->nameEquals("DisabledModes"))
1931 {
1932 xml::ElementNodesList llDisabledNetworkModes;
1933 xml::ElementNodesList::iterator itDisabled;
1934 pelmNode->getChildElements(llDisabledNetworkModes);
1935 /* run over disabled list and load settings */
1936 for (itDisabled = llDisabledNetworkModes.begin();
1937 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
1938 {
1939 const xml::ElementNode *pelmDisabledNode = *itDisabled;
1940 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
1941 }
1942 }
1943 else
1944 readAttachedNetworkMode(*pelmNode, true, nic);
1945 }
1946 // else: default is NetworkAttachmentType_Null
1947
1948 ll.push_back(nic);
1949 }
1950}
1951
1952void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
1953{
1954 if (elmMode.nameEquals("NAT"))
1955 {
1956 if (fEnabled)
1957 nic.mode = NetworkAttachmentType_NAT;
1958
1959 nic.fHasDisabledNAT = (nic.mode != NetworkAttachmentType_NAT && !fEnabled);
1960 elmMode.getAttributeValue("network", nic.nat.strNetwork); // optional network name
1961 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
1962 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
1963 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
1964 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
1965 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
1966 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
1967 const xml::ElementNode *pelmDNS;
1968 if ((pelmDNS = elmMode.findChildElement("DNS")))
1969 {
1970 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDnsPassDomain);
1971 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDnsProxy);
1972 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDnsUseHostResolver);
1973 }
1974 const xml::ElementNode *pelmAlias;
1975 if ((pelmAlias = elmMode.findChildElement("Alias")))
1976 {
1977 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
1978 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
1979 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
1980 }
1981 const xml::ElementNode *pelmTFTP;
1982 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
1983 {
1984 pelmTFTP->getAttributeValue("prefix", nic.nat.strTftpPrefix);
1985 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTftpBootFile);
1986 pelmTFTP->getAttributeValue("next-server", nic.nat.strTftpNextServer);
1987 }
1988 xml::ElementNodesList plstNatPF;
1989 elmMode.getChildElements(plstNatPF, "Forwarding");
1990 for (xml::ElementNodesList::iterator pf = plstNatPF.begin(); pf != plstNatPF.end(); ++pf)
1991 {
1992 NATRule rule;
1993 uint32_t port = 0;
1994 (*pf)->getAttributeValue("name", rule.strName);
1995 (*pf)->getAttributeValue("proto", (uint32_t&)rule.proto);
1996 (*pf)->getAttributeValue("hostip", rule.strHostIP);
1997 (*pf)->getAttributeValue("hostport", port);
1998 rule.u16HostPort = port;
1999 (*pf)->getAttributeValue("guestip", rule.strGuestIP);
2000 (*pf)->getAttributeValue("guestport", port);
2001 rule.u16GuestPort = port;
2002 nic.nat.llRules.push_back(rule);
2003 }
2004 }
2005 else if ( fEnabled
2006 && ( (elmMode.nameEquals("HostInterface"))
2007 || (elmMode.nameEquals("BridgedInterface")))
2008 )
2009 {
2010 nic.mode = NetworkAttachmentType_Bridged;
2011 elmMode.getAttributeValue("name", nic.strName); // optional host interface name
2012 }
2013 else if ( fEnabled
2014 && elmMode.nameEquals("InternalNetwork"))
2015 {
2016 nic.mode = NetworkAttachmentType_Internal;
2017 if (!elmMode.getAttributeValue("name", nic.strName)) // required network name
2018 throw ConfigFileError(this, &elmMode, N_("Required InternalNetwork/@name element is missing"));
2019 }
2020 else if ( fEnabled
2021 && elmMode.nameEquals("HostOnlyInterface"))
2022 {
2023 nic.mode = NetworkAttachmentType_HostOnly;
2024 if (!elmMode.getAttributeValue("name", nic.strName)) // required network name
2025 throw ConfigFileError(this, &elmMode, N_("Required HostOnlyInterface/@name element is missing"));
2026 }
2027#if defined(VBOX_WITH_VDE)
2028 else if ( fEnabled
2029 && elmMode.nameEquals("VDE"))
2030 {
2031 nic.mode = NetworkAttachmentType_VDE;
2032 elmMode.getAttributeValue("network", nic.strName); // optional network name
2033 }
2034#endif
2035}
2036
2037/**
2038 * Called from MachineConfigFile::readHardware() to read serial port information.
2039 * @param elmUART
2040 * @param ll
2041 */
2042void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
2043 SerialPortsList &ll)
2044{
2045 xml::NodesLoop nl1(elmUART, "Port");
2046 const xml::ElementNode *pelmPort;
2047 while ((pelmPort = nl1.forAllNodes()))
2048 {
2049 SerialPort port;
2050 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
2051 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
2052
2053 // slot must be unique
2054 for (SerialPortsList::const_iterator it = ll.begin();
2055 it != ll.end();
2056 ++it)
2057 if ((*it).ulSlot == port.ulSlot)
2058 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
2059
2060 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
2061 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
2062 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
2063 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
2064 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
2065 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
2066
2067 Utf8Str strPortMode;
2068 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
2069 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
2070 if (strPortMode == "RawFile")
2071 port.portMode = PortMode_RawFile;
2072 else if (strPortMode == "HostPipe")
2073 port.portMode = PortMode_HostPipe;
2074 else if (strPortMode == "HostDevice")
2075 port.portMode = PortMode_HostDevice;
2076 else if (strPortMode == "Disconnected")
2077 port.portMode = PortMode_Disconnected;
2078 else
2079 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
2080
2081 pelmPort->getAttributeValue("path", port.strPath);
2082 pelmPort->getAttributeValue("server", port.fServer);
2083
2084 ll.push_back(port);
2085 }
2086}
2087
2088/**
2089 * Called from MachineConfigFile::readHardware() to read parallel port information.
2090 * @param elmLPT
2091 * @param ll
2092 */
2093void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
2094 ParallelPortsList &ll)
2095{
2096 xml::NodesLoop nl1(elmLPT, "Port");
2097 const xml::ElementNode *pelmPort;
2098 while ((pelmPort = nl1.forAllNodes()))
2099 {
2100 ParallelPort port;
2101 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
2102 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
2103
2104 // slot must be unique
2105 for (ParallelPortsList::const_iterator it = ll.begin();
2106 it != ll.end();
2107 ++it)
2108 if ((*it).ulSlot == port.ulSlot)
2109 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
2110
2111 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
2112 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
2113 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
2114 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
2115 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
2116 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
2117
2118 pelmPort->getAttributeValue("path", port.strPath);
2119
2120 ll.push_back(port);
2121 }
2122}
2123
2124/**
2125 * Called from MachineConfigFile::readHardware() to read audio adapter information
2126 * and maybe fix driver information depending on the current host hardware.
2127 *
2128 * @param elmAudioAdapter "AudioAdapter" XML element.
2129 * @param hw
2130 */
2131void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
2132 AudioAdapter &aa)
2133{
2134 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
2135
2136 Utf8Str strTemp;
2137 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
2138 {
2139 if (strTemp == "SB16")
2140 aa.controllerType = AudioControllerType_SB16;
2141 else if (strTemp == "AC97")
2142 aa.controllerType = AudioControllerType_AC97;
2143 else if (strTemp == "HDA")
2144 aa.controllerType = AudioControllerType_HDA;
2145 else
2146 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
2147 }
2148
2149 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
2150 {
2151 // settings before 1.3 used lower case so make sure this is case-insensitive
2152 strTemp.toUpper();
2153 if (strTemp == "NULL")
2154 aa.driverType = AudioDriverType_Null;
2155 else if (strTemp == "WINMM")
2156 aa.driverType = AudioDriverType_WinMM;
2157 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
2158 aa.driverType = AudioDriverType_DirectSound;
2159 else if (strTemp == "SOLAUDIO")
2160 aa.driverType = AudioDriverType_SolAudio;
2161 else if (strTemp == "ALSA")
2162 aa.driverType = AudioDriverType_ALSA;
2163 else if (strTemp == "PULSE")
2164 aa.driverType = AudioDriverType_Pulse;
2165 else if (strTemp == "OSS")
2166 aa.driverType = AudioDriverType_OSS;
2167 else if (strTemp == "COREAUDIO")
2168 aa.driverType = AudioDriverType_CoreAudio;
2169 else if (strTemp == "MMPM")
2170 aa.driverType = AudioDriverType_MMPM;
2171 else
2172 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
2173
2174 // now check if this is actually supported on the current host platform;
2175 // people might be opening a file created on a Windows host, and that
2176 // VM should still start on a Linux host
2177 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
2178 aa.driverType = getHostDefaultAudioDriver();
2179 }
2180}
2181
2182/**
2183 * Called from MachineConfigFile::readHardware() to read guest property information.
2184 * @param elmGuestProperties
2185 * @param hw
2186 */
2187void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
2188 Hardware &hw)
2189{
2190 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
2191 const xml::ElementNode *pelmProp;
2192 while ((pelmProp = nl1.forAllNodes()))
2193 {
2194 GuestProperty prop;
2195 pelmProp->getAttributeValue("name", prop.strName);
2196 pelmProp->getAttributeValue("value", prop.strValue);
2197
2198 pelmProp->getAttributeValue("timestamp", prop.timestamp);
2199 pelmProp->getAttributeValue("flags", prop.strFlags);
2200 hw.llGuestProperties.push_back(prop);
2201 }
2202
2203 elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
2204}
2205
2206/**
2207 * Helper function to read attributes that are common to <SATAController> (pre-1.7)
2208 * and <StorageController>.
2209 * @param elmStorageController
2210 * @param strg
2211 */
2212void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
2213 StorageController &sctl)
2214{
2215 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
2216 elmStorageController.getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
2217 elmStorageController.getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
2218 elmStorageController.getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
2219 elmStorageController.getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
2220
2221 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
2222}
2223
2224/**
2225 * Reads in a <Hardware> block and stores it in the given structure. Used
2226 * both directly from readMachine and from readSnapshot, since snapshots
2227 * have their own hardware sections.
2228 *
2229 * For legacy pre-1.7 settings we also need a storage structure because
2230 * the IDE and SATA controllers used to be defined under <Hardware>.
2231 *
2232 * @param elmHardware
2233 * @param hw
2234 */
2235void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
2236 Hardware &hw,
2237 Storage &strg)
2238{
2239 if (!elmHardware.getAttributeValue("version", hw.strVersion))
2240 {
2241 /* KLUDGE ALERT! For a while during the 3.1 development this was not
2242 written because it was thought to have a default value of "2". For
2243 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
2244 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
2245 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
2246 missing the hardware version, then it probably should be "2" instead
2247 of "1". */
2248 if (m->sv < SettingsVersion_v1_7)
2249 hw.strVersion = "1";
2250 else
2251 hw.strVersion = "2";
2252 }
2253 Utf8Str strUUID;
2254 if (elmHardware.getAttributeValue("uuid", strUUID))
2255 parseUUID(hw.uuid, strUUID);
2256
2257 xml::NodesLoop nl1(elmHardware);
2258 const xml::ElementNode *pelmHwChild;
2259 while ((pelmHwChild = nl1.forAllNodes()))
2260 {
2261 if (pelmHwChild->nameEquals("CPU"))
2262 {
2263 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
2264 {
2265 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
2266 const xml::ElementNode *pelmCPUChild;
2267 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
2268 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
2269 }
2270
2271 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
2272 pelmHwChild->getAttributeValue("executionCap", hw.ulCpuExecutionCap);
2273
2274 const xml::ElementNode *pelmCPUChild;
2275 if (hw.fCpuHotPlug)
2276 {
2277 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
2278 readCpuTree(*pelmCPUChild, hw.llCpus);
2279 }
2280
2281 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
2282 {
2283 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
2284 pelmCPUChild->getAttributeValue("exclusive", hw.fHardwareVirtExclusive); // settings version 1.9
2285 }
2286 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
2287 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
2288 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
2289 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
2290 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
2291 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
2292 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtForce")))
2293 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirtForce);
2294
2295 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
2296 {
2297 /* The default for pre 3.1 was false, so we must respect that. */
2298 if (m->sv < SettingsVersion_v1_9)
2299 hw.fPAE = false;
2300 }
2301 else
2302 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
2303
2304 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
2305 pelmCPUChild->getAttributeValue("enabled", hw.fSyntheticCpu);
2306 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
2307 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
2308 }
2309 else if (pelmHwChild->nameEquals("Memory"))
2310 {
2311 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
2312 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
2313 }
2314 else if (pelmHwChild->nameEquals("Firmware"))
2315 {
2316 Utf8Str strFirmwareType;
2317 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
2318 {
2319 if ( (strFirmwareType == "BIOS")
2320 || (strFirmwareType == "1") // some trunk builds used the number here
2321 )
2322 hw.firmwareType = FirmwareType_BIOS;
2323 else if ( (strFirmwareType == "EFI")
2324 || (strFirmwareType == "2") // some trunk builds used the number here
2325 )
2326 hw.firmwareType = FirmwareType_EFI;
2327 else if ( strFirmwareType == "EFI32")
2328 hw.firmwareType = FirmwareType_EFI32;
2329 else if ( strFirmwareType == "EFI64")
2330 hw.firmwareType = FirmwareType_EFI64;
2331 else if ( strFirmwareType == "EFIDUAL")
2332 hw.firmwareType = FirmwareType_EFIDUAL;
2333 else
2334 throw ConfigFileError(this,
2335 pelmHwChild,
2336 N_("Invalid value '%s' in Firmware/@type"),
2337 strFirmwareType.c_str());
2338 }
2339 }
2340 else if (pelmHwChild->nameEquals("HID"))
2341 {
2342 Utf8Str strHidType;
2343 if (pelmHwChild->getAttributeValue("Keyboard", strHidType))
2344 {
2345 if (strHidType == "None")
2346 hw.keyboardHidType = KeyboardHidType_None;
2347 else if (strHidType == "USBKeyboard")
2348 hw.keyboardHidType = KeyboardHidType_USBKeyboard;
2349 else if (strHidType == "PS2Keyboard")
2350 hw.keyboardHidType = KeyboardHidType_PS2Keyboard;
2351 else if (strHidType == "ComboKeyboard")
2352 hw.keyboardHidType = KeyboardHidType_ComboKeyboard;
2353 else
2354 throw ConfigFileError(this,
2355 pelmHwChild,
2356 N_("Invalid value '%s' in HID/Keyboard/@type"),
2357 strHidType.c_str());
2358 }
2359 if (pelmHwChild->getAttributeValue("Pointing", strHidType))
2360 {
2361 if (strHidType == "None")
2362 hw.pointingHidType = PointingHidType_None;
2363 else if (strHidType == "USBMouse")
2364 hw.pointingHidType = PointingHidType_USBMouse;
2365 else if (strHidType == "USBTablet")
2366 hw.pointingHidType = PointingHidType_USBTablet;
2367 else if (strHidType == "PS2Mouse")
2368 hw.pointingHidType = PointingHidType_PS2Mouse;
2369 else if (strHidType == "ComboMouse")
2370 hw.pointingHidType = PointingHidType_ComboMouse;
2371 else
2372 throw ConfigFileError(this,
2373 pelmHwChild,
2374 N_("Invalid value '%s' in HID/Pointing/@type"),
2375 strHidType.c_str());
2376 }
2377 }
2378 else if (pelmHwChild->nameEquals("Chipset"))
2379 {
2380 Utf8Str strChipsetType;
2381 if (pelmHwChild->getAttributeValue("type", strChipsetType))
2382 {
2383 if (strChipsetType == "PIIX3")
2384 hw.chipsetType = ChipsetType_PIIX3;
2385 else if (strChipsetType == "ICH9")
2386 hw.chipsetType = ChipsetType_ICH9;
2387 else
2388 throw ConfigFileError(this,
2389 pelmHwChild,
2390 N_("Invalid value '%s' in Chipset/@type"),
2391 strChipsetType.c_str());
2392 }
2393 }
2394 else if (pelmHwChild->nameEquals("HPET"))
2395 {
2396 pelmHwChild->getAttributeValue("enabled", hw.fHpetEnabled);
2397 }
2398 else if (pelmHwChild->nameEquals("Boot"))
2399 {
2400 hw.mapBootOrder.clear();
2401
2402 xml::NodesLoop nl2(*pelmHwChild, "Order");
2403 const xml::ElementNode *pelmOrder;
2404 while ((pelmOrder = nl2.forAllNodes()))
2405 {
2406 uint32_t ulPos;
2407 Utf8Str strDevice;
2408 if (!pelmOrder->getAttributeValue("position", ulPos))
2409 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
2410
2411 if ( ulPos < 1
2412 || ulPos > SchemaDefs::MaxBootPosition
2413 )
2414 throw ConfigFileError(this,
2415 pelmOrder,
2416 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
2417 ulPos,
2418 SchemaDefs::MaxBootPosition + 1);
2419 // XML is 1-based but internal data is 0-based
2420 --ulPos;
2421
2422 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
2423 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
2424
2425 if (!pelmOrder->getAttributeValue("device", strDevice))
2426 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
2427
2428 DeviceType_T type;
2429 if (strDevice == "None")
2430 type = DeviceType_Null;
2431 else if (strDevice == "Floppy")
2432 type = DeviceType_Floppy;
2433 else if (strDevice == "DVD")
2434 type = DeviceType_DVD;
2435 else if (strDevice == "HardDisk")
2436 type = DeviceType_HardDisk;
2437 else if (strDevice == "Network")
2438 type = DeviceType_Network;
2439 else
2440 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
2441 hw.mapBootOrder[ulPos] = type;
2442 }
2443 }
2444 else if (pelmHwChild->nameEquals("Display"))
2445 {
2446 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
2447 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
2448 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
2449 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
2450 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
2451 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
2452 }
2453 else if (pelmHwChild->nameEquals("RemoteDisplay"))
2454 {
2455 pelmHwChild->getAttributeValue("enabled", hw.vrdeSettings.fEnabled);
2456
2457 Utf8Str str;
2458 if (pelmHwChild->getAttributeValue("port", str))
2459 hw.vrdeSettings.mapProperties["TCP/Ports"] = str;
2460 if (pelmHwChild->getAttributeValue("netAddress", str))
2461 hw.vrdeSettings.mapProperties["TCP/Address"] = str;
2462
2463 Utf8Str strAuthType;
2464 if (pelmHwChild->getAttributeValue("authType", strAuthType))
2465 {
2466 // settings before 1.3 used lower case so make sure this is case-insensitive
2467 strAuthType.toUpper();
2468 if (strAuthType == "NULL")
2469 hw.vrdeSettings.authType = AuthType_Null;
2470 else if (strAuthType == "GUEST")
2471 hw.vrdeSettings.authType = AuthType_Guest;
2472 else if (strAuthType == "EXTERNAL")
2473 hw.vrdeSettings.authType = AuthType_External;
2474 else
2475 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
2476 }
2477
2478 pelmHwChild->getAttributeValue("authLibrary", hw.vrdeSettings.strAuthLibrary);
2479 pelmHwChild->getAttributeValue("authTimeout", hw.vrdeSettings.ulAuthTimeout);
2480 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
2481 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
2482
2483 const xml::ElementNode *pelmVideoChannel;
2484 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
2485 {
2486 pelmVideoChannel->getAttributeValue("enabled", hw.vrdeSettings.fVideoChannel);
2487 pelmVideoChannel->getAttributeValue("quality", hw.vrdeSettings.ulVideoChannelQuality);
2488 hw.vrdeSettings.ulVideoChannelQuality = RT_CLAMP(hw.vrdeSettings.ulVideoChannelQuality, 10, 100);
2489 }
2490 pelmHwChild->getAttributeValue("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
2491
2492 const xml::ElementNode *pelmProperties = pelmHwChild->findChildElement("VRDEProperties");
2493 if (pelmProperties != NULL)
2494 {
2495 xml::NodesLoop nl(*pelmProperties);
2496 const xml::ElementNode *pelmProperty;
2497 while ((pelmProperty = nl.forAllNodes()))
2498 {
2499 if (pelmProperty->nameEquals("Property"))
2500 {
2501 /* <Property name="TCP/Ports" value="3000-3002"/> */
2502 Utf8Str strName, strValue;
2503 if ( ((pelmProperty->getAttributeValue("name", strName)))
2504 && ((pelmProperty->getAttributeValue("value", strValue)))
2505 )
2506 hw.vrdeSettings.mapProperties[strName] = strValue;
2507 else
2508 throw ConfigFileError(this, pelmProperty, N_("Required VRDE Property/@name or @value attribute is missing"));
2509 }
2510 }
2511 }
2512 }
2513 else if (pelmHwChild->nameEquals("BIOS"))
2514 {
2515 const xml::ElementNode *pelmBIOSChild;
2516 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
2517 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
2518 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
2519 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
2520 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
2521 {
2522 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
2523 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
2524 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
2525 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
2526 }
2527 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
2528 {
2529 Utf8Str strBootMenuMode;
2530 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
2531 {
2532 // settings before 1.3 used lower case so make sure this is case-insensitive
2533 strBootMenuMode.toUpper();
2534 if (strBootMenuMode == "DISABLED")
2535 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
2536 else if (strBootMenuMode == "MENUONLY")
2537 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
2538 else if (strBootMenuMode == "MESSAGEANDMENU")
2539 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
2540 else
2541 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
2542 }
2543 }
2544 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
2545 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
2546 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
2547 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
2548
2549 // legacy BIOS/IDEController (pre 1.7)
2550 if ( (m->sv < SettingsVersion_v1_7)
2551 && ((pelmBIOSChild = pelmHwChild->findChildElement("IDEController")))
2552 )
2553 {
2554 StorageController sctl;
2555 sctl.strName = "IDE Controller";
2556 sctl.storageBus = StorageBus_IDE;
2557
2558 Utf8Str strType;
2559 if (pelmBIOSChild->getAttributeValue("type", strType))
2560 {
2561 if (strType == "PIIX3")
2562 sctl.controllerType = StorageControllerType_PIIX3;
2563 else if (strType == "PIIX4")
2564 sctl.controllerType = StorageControllerType_PIIX4;
2565 else if (strType == "ICH6")
2566 sctl.controllerType = StorageControllerType_ICH6;
2567 else
2568 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
2569 }
2570 sctl.ulPortCount = 2;
2571 strg.llStorageControllers.push_back(sctl);
2572 }
2573 }
2574 else if (pelmHwChild->nameEquals("USBController"))
2575 {
2576 pelmHwChild->getAttributeValue("enabled", hw.usbController.fEnabled);
2577 pelmHwChild->getAttributeValue("enabledEhci", hw.usbController.fEnabledEHCI);
2578
2579 readUSBDeviceFilters(*pelmHwChild,
2580 hw.usbController.llDeviceFilters);
2581 }
2582 else if ( (m->sv < SettingsVersion_v1_7)
2583 && (pelmHwChild->nameEquals("SATAController"))
2584 )
2585 {
2586 bool f;
2587 if ( (pelmHwChild->getAttributeValue("enabled", f))
2588 && (f)
2589 )
2590 {
2591 StorageController sctl;
2592 sctl.strName = "SATA Controller";
2593 sctl.storageBus = StorageBus_SATA;
2594 sctl.controllerType = StorageControllerType_IntelAhci;
2595
2596 readStorageControllerAttributes(*pelmHwChild, sctl);
2597
2598 strg.llStorageControllers.push_back(sctl);
2599 }
2600 }
2601 else if (pelmHwChild->nameEquals("Network"))
2602 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
2603 else if (pelmHwChild->nameEquals("RTC"))
2604 {
2605 Utf8Str strLocalOrUTC;
2606 machineUserData.fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
2607 && strLocalOrUTC == "UTC";
2608 }
2609 else if ( (pelmHwChild->nameEquals("UART"))
2610 || (pelmHwChild->nameEquals("Uart")) // used before 1.3
2611 )
2612 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
2613 else if ( (pelmHwChild->nameEquals("LPT"))
2614 || (pelmHwChild->nameEquals("Lpt")) // used before 1.3
2615 )
2616 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
2617 else if (pelmHwChild->nameEquals("AudioAdapter"))
2618 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
2619 else if (pelmHwChild->nameEquals("SharedFolders"))
2620 {
2621 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
2622 const xml::ElementNode *pelmFolder;
2623 while ((pelmFolder = nl2.forAllNodes()))
2624 {
2625 SharedFolder sf;
2626 pelmFolder->getAttributeValue("name", sf.strName);
2627 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
2628 pelmFolder->getAttributeValue("writable", sf.fWritable);
2629 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
2630 hw.llSharedFolders.push_back(sf);
2631 }
2632 }
2633 else if (pelmHwChild->nameEquals("Clipboard"))
2634 {
2635 Utf8Str strTemp;
2636 if (pelmHwChild->getAttributeValue("mode", strTemp))
2637 {
2638 if (strTemp == "Disabled")
2639 hw.clipboardMode = ClipboardMode_Disabled;
2640 else if (strTemp == "HostToGuest")
2641 hw.clipboardMode = ClipboardMode_HostToGuest;
2642 else if (strTemp == "GuestToHost")
2643 hw.clipboardMode = ClipboardMode_GuestToHost;
2644 else if (strTemp == "Bidirectional")
2645 hw.clipboardMode = ClipboardMode_Bidirectional;
2646 else
2647 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
2648 }
2649 }
2650 else if (pelmHwChild->nameEquals("Guest"))
2651 {
2652 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
2653 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
2654 }
2655 else if (pelmHwChild->nameEquals("GuestProperties"))
2656 readGuestProperties(*pelmHwChild, hw);
2657 else if (pelmHwChild->nameEquals("IO"))
2658 {
2659 const xml::ElementNode *pelmBwGroups;
2660 const xml::ElementNode *pelmIoChild;
2661
2662 if ((pelmIoChild = pelmHwChild->findChildElement("IoCache")))
2663 {
2664 pelmIoChild->getAttributeValue("enabled", hw.ioSettings.fIoCacheEnabled);
2665 pelmIoChild->getAttributeValue("size", hw.ioSettings.ulIoCacheSize);
2666 }
2667
2668 if ((pelmBwGroups = pelmHwChild->findChildElement("BandwidthGroups")))
2669 {
2670 xml::NodesLoop nl2(*pelmBwGroups, "BandwidthGroup");
2671 const xml::ElementNode *pelmBandwidthGroup;
2672 while ((pelmBandwidthGroup = nl2.forAllNodes()))
2673 {
2674 BandwidthGroup gr;
2675 Utf8Str strTemp;
2676
2677 pelmBandwidthGroup->getAttributeValue("name", gr.strName);
2678
2679 if (pelmBandwidthGroup->getAttributeValue("type", strTemp))
2680 {
2681 if (strTemp == "Disk")
2682 gr.enmType = BandwidthGroupType_Disk;
2683 else if (strTemp == "Network")
2684 gr.enmType = BandwidthGroupType_Network;
2685 else
2686 throw ConfigFileError(this, pelmBandwidthGroup, N_("Invalid value '%s' in BandwidthGroup/@type attribute"), strTemp.c_str());
2687 }
2688 else
2689 throw ConfigFileError(this, pelmBandwidthGroup, N_("Missing BandwidthGroup/@type attribute"));
2690
2691 pelmBandwidthGroup->getAttributeValue("maxMbPerSec", gr.cMaxMbPerSec);
2692 hw.ioSettings.llBandwidthGroups.push_back(gr);
2693 }
2694 }
2695 }
2696 }
2697
2698 if (hw.ulMemorySizeMB == (uint32_t)-1)
2699 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
2700}
2701
2702/**
2703 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
2704 * files which have a <HardDiskAttachments> node and storage controller settings
2705 * hidden in the <Hardware> settings. We set the StorageControllers fields just the
2706 * same, just from different sources.
2707 * @param elmHardware <Hardware> XML node.
2708 * @param elmHardDiskAttachments <HardDiskAttachments> XML node.
2709 * @param strg
2710 */
2711void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
2712 Storage &strg)
2713{
2714 StorageController *pIDEController = NULL;
2715 StorageController *pSATAController = NULL;
2716
2717 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
2718 it != strg.llStorageControllers.end();
2719 ++it)
2720 {
2721 StorageController &s = *it;
2722 if (s.storageBus == StorageBus_IDE)
2723 pIDEController = &s;
2724 else if (s.storageBus == StorageBus_SATA)
2725 pSATAController = &s;
2726 }
2727
2728 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
2729 const xml::ElementNode *pelmAttachment;
2730 while ((pelmAttachment = nl1.forAllNodes()))
2731 {
2732 AttachedDevice att;
2733 Utf8Str strUUID, strBus;
2734
2735 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
2736 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
2737 parseUUID(att.uuid, strUUID);
2738
2739 if (!pelmAttachment->getAttributeValue("bus", strBus))
2740 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
2741 // pre-1.7 'channel' is now port
2742 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
2743 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
2744 // pre-1.7 'device' is still device
2745 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
2746 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
2747
2748 att.deviceType = DeviceType_HardDisk;
2749
2750 if (strBus == "IDE")
2751 {
2752 if (!pIDEController)
2753 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
2754 pIDEController->llAttachedDevices.push_back(att);
2755 }
2756 else if (strBus == "SATA")
2757 {
2758 if (!pSATAController)
2759 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
2760 pSATAController->llAttachedDevices.push_back(att);
2761 }
2762 else
2763 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
2764 }
2765}
2766
2767/**
2768 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
2769 * Used both directly from readMachine and from readSnapshot, since snapshots
2770 * have their own storage controllers sections.
2771 *
2772 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
2773 * for earlier versions.
2774 *
2775 * @param elmStorageControllers
2776 */
2777void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
2778 Storage &strg)
2779{
2780 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
2781 const xml::ElementNode *pelmController;
2782 while ((pelmController = nlStorageControllers.forAllNodes()))
2783 {
2784 StorageController sctl;
2785
2786 if (!pelmController->getAttributeValue("name", sctl.strName))
2787 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
2788 // canonicalize storage controller names for configs in the switchover
2789 // period.
2790 if (m->sv < SettingsVersion_v1_9)
2791 {
2792 if (sctl.strName == "IDE")
2793 sctl.strName = "IDE Controller";
2794 else if (sctl.strName == "SATA")
2795 sctl.strName = "SATA Controller";
2796 else if (sctl.strName == "SCSI")
2797 sctl.strName = "SCSI Controller";
2798 }
2799
2800 pelmController->getAttributeValue("Instance", sctl.ulInstance);
2801 // default from constructor is 0
2802
2803 pelmController->getAttributeValue("Bootable", sctl.fBootable);
2804 // default from constructor is true which is true
2805 // for settings below version 1.11 because they allowed only
2806 // one controller per type.
2807
2808 Utf8Str strType;
2809 if (!pelmController->getAttributeValue("type", strType))
2810 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
2811
2812 if (strType == "AHCI")
2813 {
2814 sctl.storageBus = StorageBus_SATA;
2815 sctl.controllerType = StorageControllerType_IntelAhci;
2816 }
2817 else if (strType == "LsiLogic")
2818 {
2819 sctl.storageBus = StorageBus_SCSI;
2820 sctl.controllerType = StorageControllerType_LsiLogic;
2821 }
2822 else if (strType == "BusLogic")
2823 {
2824 sctl.storageBus = StorageBus_SCSI;
2825 sctl.controllerType = StorageControllerType_BusLogic;
2826 }
2827 else if (strType == "PIIX3")
2828 {
2829 sctl.storageBus = StorageBus_IDE;
2830 sctl.controllerType = StorageControllerType_PIIX3;
2831 }
2832 else if (strType == "PIIX4")
2833 {
2834 sctl.storageBus = StorageBus_IDE;
2835 sctl.controllerType = StorageControllerType_PIIX4;
2836 }
2837 else if (strType == "ICH6")
2838 {
2839 sctl.storageBus = StorageBus_IDE;
2840 sctl.controllerType = StorageControllerType_ICH6;
2841 }
2842 else if ( (m->sv >= SettingsVersion_v1_9)
2843 && (strType == "I82078")
2844 )
2845 {
2846 sctl.storageBus = StorageBus_Floppy;
2847 sctl.controllerType = StorageControllerType_I82078;
2848 }
2849 else if (strType == "LsiLogicSas")
2850 {
2851 sctl.storageBus = StorageBus_SAS;
2852 sctl.controllerType = StorageControllerType_LsiLogicSas;
2853 }
2854 else
2855 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
2856
2857 readStorageControllerAttributes(*pelmController, sctl);
2858
2859 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
2860 const xml::ElementNode *pelmAttached;
2861 while ((pelmAttached = nlAttached.forAllNodes()))
2862 {
2863 AttachedDevice att;
2864 Utf8Str strTemp;
2865 pelmAttached->getAttributeValue("type", strTemp);
2866
2867 if (strTemp == "HardDisk")
2868 att.deviceType = DeviceType_HardDisk;
2869 else if (m->sv >= SettingsVersion_v1_9)
2870 {
2871 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
2872 if (strTemp == "DVD")
2873 {
2874 att.deviceType = DeviceType_DVD;
2875 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
2876 }
2877 else if (strTemp == "Floppy")
2878 att.deviceType = DeviceType_Floppy;
2879 }
2880
2881 if (att.deviceType != DeviceType_Null)
2882 {
2883 const xml::ElementNode *pelmImage;
2884 // all types can have images attached, but for HardDisk it's required
2885 if (!(pelmImage = pelmAttached->findChildElement("Image")))
2886 {
2887 if (att.deviceType == DeviceType_HardDisk)
2888 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
2889 else
2890 {
2891 // DVDs and floppies can also have <HostDrive> instead of <Image>
2892 const xml::ElementNode *pelmHostDrive;
2893 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
2894 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
2895 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
2896 }
2897 }
2898 else
2899 {
2900 if (!pelmImage->getAttributeValue("uuid", strTemp))
2901 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
2902 parseUUID(att.uuid, strTemp);
2903 }
2904
2905 if (!pelmAttached->getAttributeValue("port", att.lPort))
2906 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
2907 if (!pelmAttached->getAttributeValue("device", att.lDevice))
2908 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
2909
2910 pelmAttached->getAttributeValue("bandwidthGroup", att.strBwGroup);
2911 sctl.llAttachedDevices.push_back(att);
2912 }
2913 }
2914
2915 strg.llStorageControllers.push_back(sctl);
2916 }
2917}
2918
2919/**
2920 * This gets called for legacy pre-1.9 settings files after having parsed the
2921 * <Hardware> and <StorageControllers> sections to parse <Hardware> once more
2922 * for the <DVDDrive> and <FloppyDrive> sections.
2923 *
2924 * Before settings version 1.9, DVD and floppy drives were specified separately
2925 * under <Hardware>; we then need this extra loop to make sure the storage
2926 * controller structs are already set up so we can add stuff to them.
2927 *
2928 * @param elmHardware
2929 * @param strg
2930 */
2931void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
2932 Storage &strg)
2933{
2934 xml::NodesLoop nl1(elmHardware);
2935 const xml::ElementNode *pelmHwChild;
2936 while ((pelmHwChild = nl1.forAllNodes()))
2937 {
2938 if (pelmHwChild->nameEquals("DVDDrive"))
2939 {
2940 // create a DVD "attached device" and attach it to the existing IDE controller
2941 AttachedDevice att;
2942 att.deviceType = DeviceType_DVD;
2943 // legacy DVD drive is always secondary master (port 1, device 0)
2944 att.lPort = 1;
2945 att.lDevice = 0;
2946 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
2947
2948 const xml::ElementNode *pDriveChild;
2949 Utf8Str strTmp;
2950 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2951 && (pDriveChild->getAttributeValue("uuid", strTmp))
2952 )
2953 parseUUID(att.uuid, strTmp);
2954 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2955 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2956
2957 // find the IDE controller and attach the DVD drive
2958 bool fFound = false;
2959 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
2960 it != strg.llStorageControllers.end();
2961 ++it)
2962 {
2963 StorageController &sctl = *it;
2964 if (sctl.storageBus == StorageBus_IDE)
2965 {
2966 sctl.llAttachedDevices.push_back(att);
2967 fFound = true;
2968 break;
2969 }
2970 }
2971
2972 if (!fFound)
2973 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
2974 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
2975 // which should have gotten parsed in <StorageControllers> before this got called
2976 }
2977 else if (pelmHwChild->nameEquals("FloppyDrive"))
2978 {
2979 bool fEnabled;
2980 if ( (pelmHwChild->getAttributeValue("enabled", fEnabled))
2981 && (fEnabled)
2982 )
2983 {
2984 // create a new floppy controller and attach a floppy "attached device"
2985 StorageController sctl;
2986 sctl.strName = "Floppy Controller";
2987 sctl.storageBus = StorageBus_Floppy;
2988 sctl.controllerType = StorageControllerType_I82078;
2989 sctl.ulPortCount = 1;
2990
2991 AttachedDevice att;
2992 att.deviceType = DeviceType_Floppy;
2993 att.lPort = 0;
2994 att.lDevice = 0;
2995
2996 const xml::ElementNode *pDriveChild;
2997 Utf8Str strTmp;
2998 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2999 && (pDriveChild->getAttributeValue("uuid", strTmp))
3000 )
3001 parseUUID(att.uuid, strTmp);
3002 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
3003 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
3004
3005 // store attachment with controller
3006 sctl.llAttachedDevices.push_back(att);
3007 // store controller with storage
3008 strg.llStorageControllers.push_back(sctl);
3009 }
3010 }
3011 }
3012}
3013
3014/**
3015 * Called initially for the <Snapshot> element under <Machine>, if present,
3016 * to store the snapshot's data into the given Snapshot structure (which is
3017 * then the one in the Machine struct). This might then recurse if
3018 * a <Snapshots> (plural) element is found in the snapshot, which should
3019 * contain a list of child snapshots; such lists are maintained in the
3020 * Snapshot structure.
3021 *
3022 * @param elmSnapshot
3023 * @param snap
3024 */
3025void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
3026 Snapshot &snap)
3027{
3028 Utf8Str strTemp;
3029
3030 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
3031 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
3032 parseUUID(snap.uuid, strTemp);
3033
3034 if (!elmSnapshot.getAttributeValue("name", snap.strName))
3035 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
3036
3037 // earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
3038 elmSnapshot.getAttributeValue("Description", snap.strDescription);
3039
3040 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
3041 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
3042 parseTimestamp(snap.timestamp, strTemp);
3043
3044 elmSnapshot.getAttributeValue("stateFile", snap.strStateFile); // online snapshots only
3045
3046 // parse Hardware before the other elements because other things depend on it
3047 const xml::ElementNode *pelmHardware;
3048 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
3049 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
3050 readHardware(*pelmHardware, snap.hardware, snap.storage);
3051
3052 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
3053 const xml::ElementNode *pelmSnapshotChild;
3054 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
3055 {
3056 if (pelmSnapshotChild->nameEquals("Description"))
3057 snap.strDescription = pelmSnapshotChild->getValue();
3058 else if ( (m->sv < SettingsVersion_v1_7)
3059 && (pelmSnapshotChild->nameEquals("HardDiskAttachments"))
3060 )
3061 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.storage);
3062 else if ( (m->sv >= SettingsVersion_v1_7)
3063 && (pelmSnapshotChild->nameEquals("StorageControllers"))
3064 )
3065 readStorageControllers(*pelmSnapshotChild, snap.storage);
3066 else if (pelmSnapshotChild->nameEquals("Snapshots"))
3067 {
3068 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
3069 const xml::ElementNode *pelmChildSnapshot;
3070 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
3071 {
3072 if (pelmChildSnapshot->nameEquals("Snapshot"))
3073 {
3074 Snapshot child;
3075 readSnapshot(*pelmChildSnapshot, child);
3076 snap.llChildSnapshots.push_back(child);
3077 }
3078 }
3079 }
3080 }
3081
3082 if (m->sv < SettingsVersion_v1_9)
3083 // go through Hardware once more to repair the settings controller structures
3084 // with data from old DVDDrive and FloppyDrive elements
3085 readDVDAndFloppies_pre1_9(*pelmHardware, snap.storage);
3086}
3087
3088const struct {
3089 const char *pcszOld;
3090 const char *pcszNew;
3091} aConvertOSTypes[] =
3092{
3093 { "unknown", "Other" },
3094 { "dos", "DOS" },
3095 { "win31", "Windows31" },
3096 { "win95", "Windows95" },
3097 { "win98", "Windows98" },
3098 { "winme", "WindowsMe" },
3099 { "winnt4", "WindowsNT4" },
3100 { "win2k", "Windows2000" },
3101 { "winxp", "WindowsXP" },
3102 { "win2k3", "Windows2003" },
3103 { "winvista", "WindowsVista" },
3104 { "win2k8", "Windows2008" },
3105 { "os2warp3", "OS2Warp3" },
3106 { "os2warp4", "OS2Warp4" },
3107 { "os2warp45", "OS2Warp45" },
3108 { "ecs", "OS2eCS" },
3109 { "linux22", "Linux22" },
3110 { "linux24", "Linux24" },
3111 { "linux26", "Linux26" },
3112 { "archlinux", "ArchLinux" },
3113 { "debian", "Debian" },
3114 { "opensuse", "OpenSUSE" },
3115 { "fedoracore", "Fedora" },
3116 { "gentoo", "Gentoo" },
3117 { "mandriva", "Mandriva" },
3118 { "redhat", "RedHat" },
3119 { "ubuntu", "Ubuntu" },
3120 { "xandros", "Xandros" },
3121 { "freebsd", "FreeBSD" },
3122 { "openbsd", "OpenBSD" },
3123 { "netbsd", "NetBSD" },
3124 { "netware", "Netware" },
3125 { "solaris", "Solaris" },
3126 { "opensolaris", "OpenSolaris" },
3127 { "l4", "L4" }
3128};
3129
3130void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
3131{
3132 for (unsigned u = 0;
3133 u < RT_ELEMENTS(aConvertOSTypes);
3134 ++u)
3135 {
3136 if (str == aConvertOSTypes[u].pcszOld)
3137 {
3138 str = aConvertOSTypes[u].pcszNew;
3139 break;
3140 }
3141 }
3142}
3143
3144/**
3145 * Called from the constructor to actually read in the <Machine> element
3146 * of a machine config file.
3147 * @param elmMachine
3148 */
3149void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
3150{
3151 Utf8Str strUUID;
3152 if ( (elmMachine.getAttributeValue("uuid", strUUID))
3153 && (elmMachine.getAttributeValue("name", machineUserData.strName))
3154 )
3155 {
3156 parseUUID(uuid, strUUID);
3157
3158 elmMachine.getAttributeValue("nameSync", machineUserData.fNameSync);
3159
3160 Utf8Str str;
3161 elmMachine.getAttributeValue("Description", machineUserData.strDescription);
3162
3163 elmMachine.getAttributeValue("OSType", machineUserData.strOsType);
3164 if (m->sv < SettingsVersion_v1_5)
3165 convertOldOSType_pre1_5(machineUserData.strOsType);
3166
3167 elmMachine.getAttributeValue("stateFile", strStateFile);
3168 if (elmMachine.getAttributeValue("currentSnapshot", str))
3169 parseUUID(uuidCurrentSnapshot, str);
3170 elmMachine.getAttributeValue("snapshotFolder", machineUserData.strSnapshotFolder);
3171 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
3172 fCurrentStateModified = true;
3173 if (elmMachine.getAttributeValue("lastStateChange", str))
3174 parseTimestamp(timeLastStateChange, str);
3175 // constructor has called RTTimeNow(&timeLastStateChange) before
3176
3177 // parse Hardware before the other elements because other things depend on it
3178 const xml::ElementNode *pelmHardware;
3179 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
3180 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
3181 readHardware(*pelmHardware, hardwareMachine, storageMachine);
3182
3183 xml::NodesLoop nlRootChildren(elmMachine);
3184 const xml::ElementNode *pelmMachineChild;
3185 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
3186 {
3187 if (pelmMachineChild->nameEquals("ExtraData"))
3188 readExtraData(*pelmMachineChild,
3189 mapExtraDataItems);
3190 else if ( (m->sv < SettingsVersion_v1_7)
3191 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
3192 )
3193 readHardDiskAttachments_pre1_7(*pelmMachineChild, storageMachine);
3194 else if ( (m->sv >= SettingsVersion_v1_7)
3195 && (pelmMachineChild->nameEquals("StorageControllers"))
3196 )
3197 readStorageControllers(*pelmMachineChild, storageMachine);
3198 else if (pelmMachineChild->nameEquals("Snapshot"))
3199 {
3200 Snapshot snap;
3201 // this will recurse into child snapshots, if necessary
3202 readSnapshot(*pelmMachineChild, snap);
3203 llFirstSnapshot.push_back(snap);
3204 }
3205 else if (pelmMachineChild->nameEquals("Description"))
3206 machineUserData.strDescription = pelmMachineChild->getValue();
3207 else if (pelmMachineChild->nameEquals("Teleporter"))
3208 {
3209 pelmMachineChild->getAttributeValue("enabled", machineUserData.fTeleporterEnabled);
3210 pelmMachineChild->getAttributeValue("port", machineUserData.uTeleporterPort);
3211 pelmMachineChild->getAttributeValue("address", machineUserData.strTeleporterAddress);
3212 pelmMachineChild->getAttributeValue("password", machineUserData.strTeleporterPassword);
3213 }
3214 else if (pelmMachineChild->nameEquals("FaultTolerance"))
3215 {
3216 Utf8Str strFaultToleranceSate;
3217 if (pelmMachineChild->getAttributeValue("state", strFaultToleranceSate))
3218 {
3219 if (strFaultToleranceSate == "master")
3220 machineUserData.enmFaultToleranceState = FaultToleranceState_Master;
3221 else
3222 if (strFaultToleranceSate == "standby")
3223 machineUserData.enmFaultToleranceState = FaultToleranceState_Standby;
3224 else
3225 machineUserData.enmFaultToleranceState = FaultToleranceState_Inactive;
3226 }
3227 pelmMachineChild->getAttributeValue("port", machineUserData.uFaultTolerancePort);
3228 pelmMachineChild->getAttributeValue("address", machineUserData.strFaultToleranceAddress);
3229 pelmMachineChild->getAttributeValue("interval", machineUserData.uFaultToleranceInterval);
3230 pelmMachineChild->getAttributeValue("password", machineUserData.strFaultTolerancePassword);
3231 }
3232 else if (pelmMachineChild->nameEquals("MediaRegistry"))
3233 readMediaRegistry(*pelmMachineChild, mediaRegistry);
3234 }
3235
3236 if (m->sv < SettingsVersion_v1_9)
3237 // go through Hardware once more to repair the settings controller structures
3238 // with data from old DVDDrive and FloppyDrive elements
3239 readDVDAndFloppies_pre1_9(*pelmHardware, storageMachine);
3240 }
3241 else
3242 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
3243}
3244
3245/**
3246 * Creates a <Hardware> node under elmParent and then writes out the XML
3247 * keys under that. Called for both the <Machine> node and for snapshots.
3248 * @param elmParent
3249 * @param st
3250 */
3251void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
3252 const Hardware &hw,
3253 const Storage &strg)
3254{
3255 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
3256
3257 if (m->sv >= SettingsVersion_v1_4)
3258 pelmHardware->setAttribute("version", hw.strVersion);
3259 if ( (m->sv >= SettingsVersion_v1_9)
3260 && (!hw.uuid.isEmpty())
3261 )
3262 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
3263
3264 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
3265
3266 xml::ElementNode *pelmHwVirtEx = pelmCPU->createChild("HardwareVirtEx");
3267 pelmHwVirtEx->setAttribute("enabled", hw.fHardwareVirt);
3268 if (m->sv >= SettingsVersion_v1_9)
3269 pelmHwVirtEx->setAttribute("exclusive", hw.fHardwareVirtExclusive);
3270
3271 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
3272 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
3273 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
3274
3275 if (hw.fSyntheticCpu)
3276 pelmCPU->createChild("SyntheticCpu")->setAttribute("enabled", hw.fSyntheticCpu);
3277 pelmCPU->setAttribute("count", hw.cCPUs);
3278 if (hw.ulCpuExecutionCap != 100)
3279 pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap);
3280
3281 /* Always save this setting as we have changed the default in 4.0 (on for large memory 64-bit systems). */
3282 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
3283
3284 if (m->sv >= SettingsVersion_v1_9)
3285 pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce);
3286
3287 if (m->sv >= SettingsVersion_v1_10)
3288 {
3289 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
3290
3291 xml::ElementNode *pelmCpuTree = NULL;
3292 for (CpuList::const_iterator it = hw.llCpus.begin();
3293 it != hw.llCpus.end();
3294 ++it)
3295 {
3296 const Cpu &cpu = *it;
3297
3298 if (pelmCpuTree == NULL)
3299 pelmCpuTree = pelmCPU->createChild("CpuTree");
3300
3301 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
3302 pelmCpu->setAttribute("id", cpu.ulId);
3303 }
3304 }
3305
3306 xml::ElementNode *pelmCpuIdTree = NULL;
3307 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
3308 it != hw.llCpuIdLeafs.end();
3309 ++it)
3310 {
3311 const CpuIdLeaf &leaf = *it;
3312
3313 if (pelmCpuIdTree == NULL)
3314 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
3315
3316 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
3317 pelmCpuIdLeaf->setAttribute("id", leaf.ulId);
3318 pelmCpuIdLeaf->setAttribute("eax", leaf.ulEax);
3319 pelmCpuIdLeaf->setAttribute("ebx", leaf.ulEbx);
3320 pelmCpuIdLeaf->setAttribute("ecx", leaf.ulEcx);
3321 pelmCpuIdLeaf->setAttribute("edx", leaf.ulEdx);
3322 }
3323
3324 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
3325 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
3326 if (m->sv >= SettingsVersion_v1_10)
3327 {
3328 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
3329 }
3330
3331 if ( (m->sv >= SettingsVersion_v1_9)
3332 && (hw.firmwareType >= FirmwareType_EFI)
3333 )
3334 {
3335 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
3336 const char *pcszFirmware;
3337
3338 switch (hw.firmwareType)
3339 {
3340 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
3341 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
3342 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
3343 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
3344 default: pcszFirmware = "None"; break;
3345 }
3346 pelmFirmware->setAttribute("type", pcszFirmware);
3347 }
3348
3349 if ( (m->sv >= SettingsVersion_v1_10)
3350 )
3351 {
3352 xml::ElementNode *pelmHid = pelmHardware->createChild("HID");
3353 const char *pcszHid;
3354
3355 switch (hw.pointingHidType)
3356 {
3357 case PointingHidType_USBMouse: pcszHid = "USBMouse"; break;
3358 case PointingHidType_USBTablet: pcszHid = "USBTablet"; break;
3359 case PointingHidType_PS2Mouse: pcszHid = "PS2Mouse"; break;
3360 case PointingHidType_ComboMouse: pcszHid = "ComboMouse"; break;
3361 case PointingHidType_None: pcszHid = "None"; break;
3362 default: Assert(false); pcszHid = "PS2Mouse"; break;
3363 }
3364 pelmHid->setAttribute("Pointing", pcszHid);
3365
3366 switch (hw.keyboardHidType)
3367 {
3368 case KeyboardHidType_USBKeyboard: pcszHid = "USBKeyboard"; break;
3369 case KeyboardHidType_PS2Keyboard: pcszHid = "PS2Keyboard"; break;
3370 case KeyboardHidType_ComboKeyboard: pcszHid = "ComboKeyboard"; break;
3371 case KeyboardHidType_None: pcszHid = "None"; break;
3372 default: Assert(false); pcszHid = "PS2Keyboard"; break;
3373 }
3374 pelmHid->setAttribute("Keyboard", pcszHid);
3375 }
3376
3377 if ( (m->sv >= SettingsVersion_v1_10)
3378 )
3379 {
3380 xml::ElementNode *pelmHpet = pelmHardware->createChild("HPET");
3381 pelmHpet->setAttribute("enabled", hw.fHpetEnabled);
3382 }
3383
3384 if ( (m->sv >= SettingsVersion_v1_11)
3385 )
3386 {
3387 xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset");
3388 const char *pcszChipset;
3389
3390 switch (hw.chipsetType)
3391 {
3392 case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break;
3393 case ChipsetType_ICH9: pcszChipset = "ICH9"; break;
3394 default: Assert(false); pcszChipset = "PIIX3"; break;
3395 }
3396 pelmChipset->setAttribute("type", pcszChipset);
3397 }
3398
3399 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
3400 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
3401 it != hw.mapBootOrder.end();
3402 ++it)
3403 {
3404 uint32_t i = it->first;
3405 DeviceType_T type = it->second;
3406 const char *pcszDevice;
3407
3408 switch (type)
3409 {
3410 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
3411 case DeviceType_DVD: pcszDevice = "DVD"; break;
3412 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
3413 case DeviceType_Network: pcszDevice = "Network"; break;
3414 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
3415 }
3416
3417 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
3418 pelmOrder->setAttribute("position",
3419 i + 1); // XML is 1-based but internal data is 0-based
3420 pelmOrder->setAttribute("device", pcszDevice);
3421 }
3422
3423 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
3424 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
3425 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
3426 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
3427
3428 if (m->sv >= SettingsVersion_v1_8)
3429 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
3430
3431 xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay");
3432 pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled);
3433 if (m->sv < SettingsVersion_v1_11)
3434 {
3435 /* In VBox 4.0 these attributes are replaced with "Properties". */
3436 Utf8Str strPort;
3437 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports");
3438 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
3439 strPort = it->second;
3440 if (!strPort.length())
3441 strPort = "3389";
3442 pelmVRDE->setAttribute("port", strPort);
3443
3444 Utf8Str strAddress;
3445 it = hw.vrdeSettings.mapProperties.find("TCP/Address");
3446 if (it != hw.vrdeSettings.mapProperties.end())
3447 strAddress = it->second;
3448 if (strAddress.length())
3449 pelmVRDE->setAttribute("netAddress", strAddress);
3450 }
3451 const char *pcszAuthType;
3452 switch (hw.vrdeSettings.authType)
3453 {
3454 case AuthType_Guest: pcszAuthType = "Guest"; break;
3455 case AuthType_External: pcszAuthType = "External"; break;
3456 default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break;
3457 }
3458 pelmVRDE->setAttribute("authType", pcszAuthType);
3459
3460 if (hw.vrdeSettings.ulAuthTimeout != 0)
3461 pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout);
3462 if (hw.vrdeSettings.fAllowMultiConnection)
3463 pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
3464 if (hw.vrdeSettings.fReuseSingleConnection)
3465 pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
3466
3467 if (m->sv >= SettingsVersion_v1_10)
3468 {
3469 xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel");
3470 pelmVideoChannel->setAttribute("enabled", hw.vrdeSettings.fVideoChannel);
3471 pelmVideoChannel->setAttribute("quality", hw.vrdeSettings.ulVideoChannelQuality);
3472 }
3473 if (m->sv >= SettingsVersion_v1_11)
3474 {
3475 if (hw.vrdeSettings.strAuthLibrary.length())
3476 pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary);
3477 if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty())
3478 pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
3479 if (hw.vrdeSettings.mapProperties.size() > 0)
3480 {
3481 xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties");
3482 for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin();
3483 it != hw.vrdeSettings.mapProperties.end();
3484 ++it)
3485 {
3486 const Utf8Str &strName = it->first;
3487 const Utf8Str &strValue = it->second;
3488 xml::ElementNode *pelm = pelmProperties->createChild("Property");
3489 pelm->setAttribute("name", strName);
3490 pelm->setAttribute("value", strValue);
3491 }
3492 }
3493 }
3494
3495 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
3496 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
3497 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
3498
3499 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
3500 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
3501 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
3502 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
3503 if (hw.biosSettings.strLogoImagePath.length())
3504 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
3505
3506 const char *pcszBootMenu;
3507 switch (hw.biosSettings.biosBootMenuMode)
3508 {
3509 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
3510 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
3511 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
3512 }
3513 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
3514 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
3515 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
3516
3517 if (m->sv < SettingsVersion_v1_9)
3518 {
3519 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
3520 // run thru the storage controllers to see if we have a DVD or floppy drives
3521 size_t cDVDs = 0;
3522 size_t cFloppies = 0;
3523
3524 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
3525 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
3526
3527 for (StorageControllersList::const_iterator it = strg.llStorageControllers.begin();
3528 it != strg.llStorageControllers.end();
3529 ++it)
3530 {
3531 const StorageController &sctl = *it;
3532 // in old settings format, the DVD drive could only have been under the IDE controller
3533 if (sctl.storageBus == StorageBus_IDE)
3534 {
3535 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
3536 it2 != sctl.llAttachedDevices.end();
3537 ++it2)
3538 {
3539 const AttachedDevice &att = *it2;
3540 if (att.deviceType == DeviceType_DVD)
3541 {
3542 if (cDVDs > 0)
3543 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
3544
3545 ++cDVDs;
3546
3547 pelmDVD->setAttribute("passthrough", att.fPassThrough);
3548 if (!att.uuid.isEmpty())
3549 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
3550 else if (att.strHostDriveSrc.length())
3551 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
3552 }
3553 }
3554 }
3555 else if (sctl.storageBus == StorageBus_Floppy)
3556 {
3557 size_t cFloppiesHere = sctl.llAttachedDevices.size();
3558 if (cFloppiesHere > 1)
3559 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
3560 if (cFloppiesHere)
3561 {
3562 const AttachedDevice &att = sctl.llAttachedDevices.front();
3563 pelmFloppy->setAttribute("enabled", true);
3564 if (!att.uuid.isEmpty())
3565 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
3566 else if (att.strHostDriveSrc.length())
3567 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
3568 }
3569
3570 cFloppies += cFloppiesHere;
3571 }
3572 }
3573
3574 if (cFloppies == 0)
3575 pelmFloppy->setAttribute("enabled", false);
3576 else if (cFloppies > 1)
3577 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
3578 }
3579
3580 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
3581 pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
3582 pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
3583
3584 buildUSBDeviceFilters(*pelmUSB,
3585 hw.usbController.llDeviceFilters,
3586 false); // fHostMode
3587
3588 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
3589 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
3590 it != hw.llNetworkAdapters.end();
3591 ++it)
3592 {
3593 const NetworkAdapter &nic = *it;
3594
3595 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
3596 pelmAdapter->setAttribute("slot", nic.ulSlot);
3597 pelmAdapter->setAttribute("enabled", nic.fEnabled);
3598 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
3599 pelmAdapter->setAttribute("cable", nic.fCableConnected);
3600 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
3601 if (nic.ulBootPriority != 0)
3602 {
3603 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
3604 }
3605 if (nic.fTraceEnabled)
3606 {
3607 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
3608 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
3609 }
3610 if (nic.ulBandwidthLimit)
3611 pelmAdapter->setAttribute("bandwidthLimit", nic.ulBandwidthLimit);
3612
3613 const char *pcszType;
3614 switch (nic.type)
3615 {
3616 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
3617 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
3618 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
3619 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
3620 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
3621 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
3622 }
3623 pelmAdapter->setAttribute("type", pcszType);
3624
3625 xml::ElementNode *pelmNAT;
3626 if (m->sv < SettingsVersion_v1_10)
3627 {
3628 switch (nic.mode)
3629 {
3630 case NetworkAttachmentType_NAT:
3631 pelmNAT = pelmAdapter->createChild("NAT");
3632 if (nic.nat.strNetwork.length())
3633 pelmNAT->setAttribute("network", nic.nat.strNetwork);
3634 break;
3635
3636 case NetworkAttachmentType_Bridged:
3637 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
3638 break;
3639
3640 case NetworkAttachmentType_Internal:
3641 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
3642 break;
3643
3644 case NetworkAttachmentType_HostOnly:
3645 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
3646 break;
3647
3648#if defined(VBOX_WITH_VDE)
3649 case NetworkAttachmentType_VDE:
3650 pelmAdapter->createChild("VDE")->setAttribute("network", nic.strName);
3651 break;
3652#endif
3653
3654 default: /*case NetworkAttachmentType_Null:*/
3655 break;
3656 }
3657 }
3658 else
3659 {
3660 /* m->sv >= SettingsVersion_v1_10 */
3661 xml::ElementNode *pelmDisabledNode= NULL;
3662 if (nic.fHasDisabledNAT)
3663 pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
3664 if (nic.fHasDisabledNAT)
3665 buildNetworkXML(NetworkAttachmentType_NAT, *pelmDisabledNode, nic);
3666 buildNetworkXML(nic.mode, *pelmAdapter, nic);
3667 }
3668 }
3669
3670 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
3671 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
3672 it != hw.llSerialPorts.end();
3673 ++it)
3674 {
3675 const SerialPort &port = *it;
3676 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
3677 pelmPort->setAttribute("slot", port.ulSlot);
3678 pelmPort->setAttribute("enabled", port.fEnabled);
3679 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
3680 pelmPort->setAttribute("IRQ", port.ulIRQ);
3681
3682 const char *pcszHostMode;
3683 switch (port.portMode)
3684 {
3685 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
3686 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
3687 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
3688 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
3689 }
3690 switch (port.portMode)
3691 {
3692 case PortMode_HostPipe:
3693 pelmPort->setAttribute("server", port.fServer);
3694 /* no break */
3695 case PortMode_HostDevice:
3696 case PortMode_RawFile:
3697 pelmPort->setAttribute("path", port.strPath);
3698 break;
3699
3700 default:
3701 break;
3702 }
3703 pelmPort->setAttribute("hostMode", pcszHostMode);
3704 }
3705
3706 pelmPorts = pelmHardware->createChild("LPT");
3707 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
3708 it != hw.llParallelPorts.end();
3709 ++it)
3710 {
3711 const ParallelPort &port = *it;
3712 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
3713 pelmPort->setAttribute("slot", port.ulSlot);
3714 pelmPort->setAttribute("enabled", port.fEnabled);
3715 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
3716 pelmPort->setAttribute("IRQ", port.ulIRQ);
3717 if (port.strPath.length())
3718 pelmPort->setAttribute("path", port.strPath);
3719 }
3720
3721 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
3722 const char *pcszController;
3723 switch (hw.audioAdapter.controllerType)
3724 {
3725 case AudioControllerType_SB16:
3726 pcszController = "SB16";
3727 break;
3728 case AudioControllerType_HDA:
3729 if (m->sv >= SettingsVersion_v1_11)
3730 {
3731 pcszController = "HDA";
3732 break;
3733 }
3734 /* fall through */
3735 case AudioControllerType_AC97:
3736 default:
3737 pcszController = "AC97"; break;
3738 }
3739 pelmAudio->setAttribute("controller", pcszController);
3740
3741 if (m->sv >= SettingsVersion_v1_10)
3742 {
3743 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
3744 pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local");
3745 }
3746
3747 const char *pcszDriver;
3748 switch (hw.audioAdapter.driverType)
3749 {
3750 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
3751 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
3752 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
3753 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
3754 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
3755 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
3756 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
3757 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
3758 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
3759 }
3760 pelmAudio->setAttribute("driver", pcszDriver);
3761
3762 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
3763
3764 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
3765 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
3766 it != hw.llSharedFolders.end();
3767 ++it)
3768 {
3769 const SharedFolder &sf = *it;
3770 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
3771 pelmThis->setAttribute("name", sf.strName);
3772 pelmThis->setAttribute("hostPath", sf.strHostPath);
3773 pelmThis->setAttribute("writable", sf.fWritable);
3774 pelmThis->setAttribute("autoMount", sf.fAutoMount);
3775 }
3776
3777 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
3778 const char *pcszClip;
3779 switch (hw.clipboardMode)
3780 {
3781 case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
3782 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
3783 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
3784 default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
3785 }
3786 pelmClip->setAttribute("mode", pcszClip);
3787
3788 if (m->sv >= SettingsVersion_v1_10)
3789 {
3790 xml::ElementNode *pelmIo = pelmHardware->createChild("IO");
3791 xml::ElementNode *pelmIoCache;
3792
3793 pelmIoCache = pelmIo->createChild("IoCache");
3794 pelmIoCache->setAttribute("enabled", hw.ioSettings.fIoCacheEnabled);
3795 pelmIoCache->setAttribute("size", hw.ioSettings.ulIoCacheSize);
3796
3797 if (m->sv >= SettingsVersion_v1_11)
3798 {
3799 xml::ElementNode *pelmBandwidthGroups = pelmIo->createChild("BandwidthGroups");
3800 for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin();
3801 it != hw.ioSettings.llBandwidthGroups.end();
3802 ++it)
3803 {
3804 const BandwidthGroup &gr = *it;
3805 const char *pcszType;
3806 xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup");
3807 pelmThis->setAttribute("name", gr.strName);
3808 switch (gr.enmType)
3809 {
3810 case BandwidthGroupType_Network: pcszType = "Network"; break;
3811 default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break;
3812 }
3813 pelmThis->setAttribute("type", pcszType);
3814 pelmThis->setAttribute("maxMbPerSec", gr.cMaxMbPerSec);
3815 }
3816 }
3817 }
3818
3819 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
3820 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
3821
3822 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
3823 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
3824 it != hw.llGuestProperties.end();
3825 ++it)
3826 {
3827 const GuestProperty &prop = *it;
3828 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
3829 pelmProp->setAttribute("name", prop.strName);
3830 pelmProp->setAttribute("value", prop.strValue);
3831 pelmProp->setAttribute("timestamp", prop.timestamp);
3832 pelmProp->setAttribute("flags", prop.strFlags);
3833 }
3834
3835 if (hw.strNotificationPatterns.length())
3836 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
3837}
3838
3839/**
3840 * Fill a <Network> node. Only relevant for XML version >= v1_10.
3841 * @param mode
3842 * @param elmParent
3843 * @param nice
3844 */
3845void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
3846 xml::ElementNode &elmParent,
3847 const NetworkAdapter &nic)
3848{
3849 switch (mode)
3850 {
3851 case NetworkAttachmentType_NAT:
3852 xml::ElementNode *pelmNAT;
3853 pelmNAT = elmParent.createChild("NAT");
3854
3855 if (nic.nat.strNetwork.length())
3856 pelmNAT->setAttribute("network", nic.nat.strNetwork);
3857 if (nic.nat.strBindIP.length())
3858 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
3859 if (nic.nat.u32Mtu)
3860 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
3861 if (nic.nat.u32SockRcv)
3862 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
3863 if (nic.nat.u32SockSnd)
3864 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
3865 if (nic.nat.u32TcpRcv)
3866 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
3867 if (nic.nat.u32TcpSnd)
3868 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
3869 xml::ElementNode *pelmDNS;
3870 pelmDNS = pelmNAT->createChild("DNS");
3871 pelmDNS->setAttribute("pass-domain", nic.nat.fDnsPassDomain);
3872 pelmDNS->setAttribute("use-proxy", nic.nat.fDnsProxy);
3873 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDnsUseHostResolver);
3874
3875 xml::ElementNode *pelmAlias;
3876 pelmAlias = pelmNAT->createChild("Alias");
3877 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
3878 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
3879 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
3880
3881 if ( nic.nat.strTftpPrefix.length()
3882 || nic.nat.strTftpBootFile.length()
3883 || nic.nat.strTftpNextServer.length())
3884 {
3885 xml::ElementNode *pelmTFTP;
3886 pelmTFTP = pelmNAT->createChild("TFTP");
3887 if (nic.nat.strTftpPrefix.length())
3888 pelmTFTP->setAttribute("prefix", nic.nat.strTftpPrefix);
3889 if (nic.nat.strTftpBootFile.length())
3890 pelmTFTP->setAttribute("boot-file", nic.nat.strTftpBootFile);
3891 if (nic.nat.strTftpNextServer.length())
3892 pelmTFTP->setAttribute("next-server", nic.nat.strTftpNextServer);
3893 }
3894 for (NATRuleList::const_iterator rule = nic.nat.llRules.begin();
3895 rule != nic.nat.llRules.end(); ++rule)
3896 {
3897 xml::ElementNode *pelmPF;
3898 pelmPF = pelmNAT->createChild("Forwarding");
3899 if ((*rule).strName.length())
3900 pelmPF->setAttribute("name", (*rule).strName);
3901 pelmPF->setAttribute("proto", (*rule).proto);
3902 if ((*rule).strHostIP.length())
3903 pelmPF->setAttribute("hostip", (*rule).strHostIP);
3904 if ((*rule).u16HostPort)
3905 pelmPF->setAttribute("hostport", (*rule).u16HostPort);
3906 if ((*rule).strGuestIP.length())
3907 pelmPF->setAttribute("guestip", (*rule).strGuestIP);
3908 if ((*rule).u16GuestPort)
3909 pelmPF->setAttribute("guestport", (*rule).u16GuestPort);
3910 }
3911 break;
3912
3913 case NetworkAttachmentType_Bridged:
3914 elmParent.createChild("BridgedInterface")->setAttribute("name", nic.strName);
3915 break;
3916
3917 case NetworkAttachmentType_Internal:
3918 elmParent.createChild("InternalNetwork")->setAttribute("name", nic.strName);
3919 break;
3920
3921 case NetworkAttachmentType_HostOnly:
3922 elmParent.createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
3923 break;
3924
3925#ifdef VBOX_WITH_VDE
3926 case NetworkAttachmentType_VDE:
3927 elmParent.createChild("VDE")->setAttribute("network", nic.strName);
3928 break;
3929#endif
3930
3931 default: /*case NetworkAttachmentType_Null:*/
3932 break;
3933 }
3934}
3935
3936/**
3937 * Creates a <StorageControllers> node under elmParent and then writes out the XML
3938 * keys under that. Called for both the <Machine> node and for snapshots.
3939 * @param elmParent
3940 * @param st
3941 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
3942 * an empty drive is always written instead. This is for the OVF export case.
3943 * This parameter is ignored unless the settings version is at least v1.9, which
3944 * is always the case when this gets called for OVF export.
3945 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
3946 * pointers to which we will append all elements that we created here that contain
3947 * UUID attributes. This allows the OVF export code to quickly replace the internal
3948 * media UUIDs with the UUIDs of the media that were exported.
3949 */
3950void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
3951 const Storage &st,
3952 bool fSkipRemovableMedia,
3953 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
3954{
3955 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
3956
3957 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
3958 it != st.llStorageControllers.end();
3959 ++it)
3960 {
3961 const StorageController &sc = *it;
3962
3963 if ( (m->sv < SettingsVersion_v1_9)
3964 && (sc.controllerType == StorageControllerType_I82078)
3965 )
3966 // floppy controller already got written into <Hardware>/<FloppyController> in writeHardware()
3967 // for pre-1.9 settings
3968 continue;
3969
3970 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
3971 com::Utf8Str name = sc.strName;
3972 if (m->sv < SettingsVersion_v1_8)
3973 {
3974 // pre-1.8 settings use shorter controller names, they are
3975 // expanded when reading the settings
3976 if (name == "IDE Controller")
3977 name = "IDE";
3978 else if (name == "SATA Controller")
3979 name = "SATA";
3980 else if (name == "SCSI Controller")
3981 name = "SCSI";
3982 }
3983 pelmController->setAttribute("name", sc.strName);
3984
3985 const char *pcszType;
3986 switch (sc.controllerType)
3987 {
3988 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
3989 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
3990 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
3991 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
3992 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
3993 case StorageControllerType_I82078: pcszType = "I82078"; break;
3994 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
3995 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
3996 }
3997 pelmController->setAttribute("type", pcszType);
3998
3999 pelmController->setAttribute("PortCount", sc.ulPortCount);
4000
4001 if (m->sv >= SettingsVersion_v1_9)
4002 if (sc.ulInstance)
4003 pelmController->setAttribute("Instance", sc.ulInstance);
4004
4005 if (m->sv >= SettingsVersion_v1_10)
4006 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
4007
4008 if (m->sv >= SettingsVersion_v1_11)
4009 pelmController->setAttribute("Bootable", sc.fBootable);
4010
4011 if (sc.controllerType == StorageControllerType_IntelAhci)
4012 {
4013 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
4014 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
4015 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
4016 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
4017 }
4018
4019 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
4020 it2 != sc.llAttachedDevices.end();
4021 ++it2)
4022 {
4023 const AttachedDevice &att = *it2;
4024
4025 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
4026 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
4027 // the floppy controller at the top of the loop
4028 if ( att.deviceType == DeviceType_DVD
4029 && m->sv < SettingsVersion_v1_9
4030 )
4031 continue;
4032
4033 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
4034
4035 pcszType = NULL;
4036
4037 switch (att.deviceType)
4038 {
4039 case DeviceType_HardDisk:
4040 pcszType = "HardDisk";
4041 break;
4042
4043 case DeviceType_DVD:
4044 pcszType = "DVD";
4045 pelmDevice->setAttribute("passthrough", att.fPassThrough);
4046 break;
4047
4048 case DeviceType_Floppy:
4049 pcszType = "Floppy";
4050 break;
4051 }
4052
4053 pelmDevice->setAttribute("type", pcszType);
4054
4055 pelmDevice->setAttribute("port", att.lPort);
4056 pelmDevice->setAttribute("device", att.lDevice);
4057
4058 if (att.strBwGroup.length())
4059 pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup);
4060
4061 // attached image, if any
4062 if ( !att.uuid.isEmpty()
4063 && ( att.deviceType == DeviceType_HardDisk
4064 || !fSkipRemovableMedia
4065 )
4066 )
4067 {
4068 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
4069 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
4070
4071 // if caller wants a list of UUID elements, give it to them
4072 if (pllElementsWithUuidAttributes)
4073 pllElementsWithUuidAttributes->push_back(pelmImage);
4074 }
4075 else if ( (m->sv >= SettingsVersion_v1_9)
4076 && (att.strHostDriveSrc.length())
4077 )
4078 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
4079 }
4080 }
4081}
4082
4083/**
4084 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
4085 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
4086 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
4087 * @param elmParent
4088 * @param snap
4089 */
4090void MachineConfigFile::buildSnapshotXML(xml::ElementNode &elmParent,
4091 const Snapshot &snap)
4092{
4093 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
4094
4095 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
4096 pelmSnapshot->setAttribute("name", snap.strName);
4097 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
4098
4099 if (snap.strStateFile.length())
4100 pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
4101
4102 if (snap.strDescription.length())
4103 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
4104
4105 buildHardwareXML(*pelmSnapshot, snap.hardware, snap.storage);
4106 buildStorageControllersXML(*pelmSnapshot,
4107 snap.storage,
4108 false /* fSkipRemovableMedia */,
4109 NULL); /* pllElementsWithUuidAttributes */
4110 // we only skip removable media for OVF, but we never get here for OVF
4111 // since snapshots never get written then
4112
4113 if (snap.llChildSnapshots.size())
4114 {
4115 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
4116 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
4117 it != snap.llChildSnapshots.end();
4118 ++it)
4119 {
4120 const Snapshot &child = *it;
4121 buildSnapshotXML(*pelmChildren, child);
4122 }
4123 }
4124}
4125
4126/**
4127 * Builds the XML DOM tree for the machine config under the given XML element.
4128 *
4129 * This has been separated out from write() so it can be called from elsewhere,
4130 * such as the OVF code, to build machine XML in an existing XML tree.
4131 *
4132 * As a result, this gets called from two locations:
4133 *
4134 * -- MachineConfigFile::write();
4135 *
4136 * -- Appliance::buildXMLForOneVirtualSystem()
4137 *
4138 * In fl, the following flag bits are recognized:
4139 *
4140 * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will
4141 * be written, if present. This is not set when called from OVF because OVF
4142 * has its own variant of a media registry. This flag is ignored unless the
4143 * settings version is at least v1.11 (VirtualBox 4.0).
4144 *
4145 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
4146 * of the machine and write out <Snapshot> and possibly more snapshots under
4147 * that, if snapshots are present. Otherwise all snapshots are suppressed
4148 * (when called from OVF).
4149 *
4150 * -- BuildMachineXML_WriteVboxVersionAttribute: If set, add a settingsVersion
4151 * attribute to the machine tag with the vbox settings version. This is for
4152 * the OVF export case in which we don't have the settings version set in
4153 * the root element.
4154 *
4155 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
4156 * (DVDs, floppies) are silently skipped. This is for the OVF export case
4157 * until we support copying ISO and RAW media as well. This flag is ignored
4158 * unless the settings version is at least v1.9, which is always the case
4159 * when this gets called for OVF export.
4160 *
4161 * -- BuildMachineXML_SuppressSavedState: If set, the Machine/@stateFile
4162 * attribute is never set. This is also for the OVF export case because we
4163 * cannot save states with OVF.
4164 *
4165 * @param elmMachine XML <Machine> element to add attributes and elements to.
4166 * @param fl Flags.
4167 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
4168 * see buildStorageControllersXML() for details.
4169 */
4170void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
4171 uint32_t fl,
4172 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
4173{
4174 if (fl & BuildMachineXML_WriteVboxVersionAttribute)
4175 // add settings version attribute to machine element
4176 setVersionAttribute(elmMachine);
4177
4178 elmMachine.setAttribute("uuid", uuid.toStringCurly());
4179 elmMachine.setAttribute("name", machineUserData.strName);
4180 if (!machineUserData.fNameSync)
4181 elmMachine.setAttribute("nameSync", machineUserData.fNameSync);
4182 if (machineUserData.strDescription.length())
4183 elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
4184 elmMachine.setAttribute("OSType", machineUserData.strOsType);
4185 if ( strStateFile.length()
4186 && !(fl & BuildMachineXML_SuppressSavedState)
4187 )
4188 elmMachine.setAttribute("stateFile", strStateFile);
4189 if ( (fl & BuildMachineXML_IncludeSnapshots)
4190 && !uuidCurrentSnapshot.isEmpty())
4191 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
4192 if (machineUserData.strSnapshotFolder.length())
4193 elmMachine.setAttribute("snapshotFolder", machineUserData.strSnapshotFolder);
4194 if (!fCurrentStateModified)
4195 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
4196 elmMachine.setAttribute("lastStateChange", makeString(timeLastStateChange));
4197 if (fAborted)
4198 elmMachine.setAttribute("aborted", fAborted);
4199 if ( m->sv >= SettingsVersion_v1_9
4200 && ( machineUserData.fTeleporterEnabled
4201 || machineUserData.uTeleporterPort
4202 || !machineUserData.strTeleporterAddress.isEmpty()
4203 || !machineUserData.strTeleporterPassword.isEmpty()
4204 )
4205 )
4206 {
4207 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
4208 pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled);
4209 pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort);
4210 pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress);
4211 pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword);
4212 }
4213
4214 if ( m->sv >= SettingsVersion_v1_11
4215 && ( machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
4216 || machineUserData.uFaultTolerancePort
4217 || machineUserData.uFaultToleranceInterval
4218 || !machineUserData.strFaultToleranceAddress.isEmpty()
4219 )
4220 )
4221 {
4222 xml::ElementNode *pelmFaultTolerance = elmMachine.createChild("FaultTolerance");
4223 switch (machineUserData.enmFaultToleranceState)
4224 {
4225 case FaultToleranceState_Inactive:
4226 pelmFaultTolerance->setAttribute("state", "inactive");
4227 break;
4228 case FaultToleranceState_Master:
4229 pelmFaultTolerance->setAttribute("state", "master");
4230 break;
4231 case FaultToleranceState_Standby:
4232 pelmFaultTolerance->setAttribute("state", "standby");
4233 break;
4234 }
4235
4236 pelmFaultTolerance->setAttribute("port", machineUserData.uFaultTolerancePort);
4237 pelmFaultTolerance->setAttribute("address", machineUserData.strFaultToleranceAddress);
4238 pelmFaultTolerance->setAttribute("interval", machineUserData.uFaultToleranceInterval);
4239 pelmFaultTolerance->setAttribute("password", machineUserData.strFaultTolerancePassword);
4240 }
4241
4242 if ( (fl & BuildMachineXML_MediaRegistry)
4243 && (m->sv >= SettingsVersion_v1_11)
4244 )
4245 buildMediaRegistry(elmMachine, mediaRegistry);
4246
4247 buildExtraData(elmMachine, mapExtraDataItems);
4248
4249 if ( (fl & BuildMachineXML_IncludeSnapshots)
4250 && llFirstSnapshot.size())
4251 buildSnapshotXML(elmMachine, llFirstSnapshot.front());
4252
4253 buildHardwareXML(elmMachine, hardwareMachine, storageMachine);
4254 buildStorageControllersXML(elmMachine,
4255 storageMachine,
4256 !!(fl & BuildMachineXML_SkipRemovableMedia),
4257 pllElementsWithUuidAttributes);
4258}
4259
4260/**
4261 * Returns true only if the given AudioDriverType is supported on
4262 * the current host platform. For example, this would return false
4263 * for AudioDriverType_DirectSound when compiled on a Linux host.
4264 * @param drv AudioDriverType_* enum to test.
4265 * @return true only if the current host supports that driver.
4266 */
4267/*static*/
4268bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
4269{
4270 switch (drv)
4271 {
4272 case AudioDriverType_Null:
4273#ifdef RT_OS_WINDOWS
4274# ifdef VBOX_WITH_WINMM
4275 case AudioDriverType_WinMM:
4276# endif
4277 case AudioDriverType_DirectSound:
4278#endif /* RT_OS_WINDOWS */
4279#ifdef RT_OS_SOLARIS
4280 case AudioDriverType_SolAudio:
4281#endif
4282#ifdef RT_OS_LINUX
4283# ifdef VBOX_WITH_ALSA
4284 case AudioDriverType_ALSA:
4285# endif
4286# ifdef VBOX_WITH_PULSE
4287 case AudioDriverType_Pulse:
4288# endif
4289#endif /* RT_OS_LINUX */
4290#if defined (RT_OS_LINUX) || defined (RT_OS_FREEBSD) || defined(VBOX_WITH_SOLARIS_OSS)
4291 case AudioDriverType_OSS:
4292#endif
4293#ifdef RT_OS_FREEBSD
4294# ifdef VBOX_WITH_PULSE
4295 case AudioDriverType_Pulse:
4296# endif
4297#endif
4298#ifdef RT_OS_DARWIN
4299 case AudioDriverType_CoreAudio:
4300#endif
4301#ifdef RT_OS_OS2
4302 case AudioDriverType_MMPM:
4303#endif
4304 return true;
4305 }
4306
4307 return false;
4308}
4309
4310/**
4311 * Returns the AudioDriverType_* which should be used by default on this
4312 * host platform. On Linux, this will check at runtime whether PulseAudio
4313 * or ALSA are actually supported on the first call.
4314 * @return
4315 */
4316/*static*/
4317AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
4318{
4319#if defined(RT_OS_WINDOWS)
4320# ifdef VBOX_WITH_WINMM
4321 return AudioDriverType_WinMM;
4322# else /* VBOX_WITH_WINMM */
4323 return AudioDriverType_DirectSound;
4324# endif /* !VBOX_WITH_WINMM */
4325#elif defined(RT_OS_SOLARIS)
4326 return AudioDriverType_SolAudio;
4327#elif defined(RT_OS_LINUX)
4328 // on Linux, we need to check at runtime what's actually supported...
4329 static RTLockMtx s_mtx;
4330 static AudioDriverType_T s_linuxDriver = -1;
4331 RTLock lock(s_mtx);
4332 if (s_linuxDriver == (AudioDriverType_T)-1)
4333 {
4334# if defined(VBOX_WITH_PULSE)
4335 /* Check for the pulse library & that the pulse audio daemon is running. */
4336 if (RTProcIsRunningByName("pulseaudio") &&
4337 RTLdrIsLoadable("libpulse.so.0"))
4338 s_linuxDriver = AudioDriverType_Pulse;
4339 else
4340# endif /* VBOX_WITH_PULSE */
4341# if defined(VBOX_WITH_ALSA)
4342 /* Check if we can load the ALSA library */
4343 if (RTLdrIsLoadable("libasound.so.2"))
4344 s_linuxDriver = AudioDriverType_ALSA;
4345 else
4346# endif /* VBOX_WITH_ALSA */
4347 s_linuxDriver = AudioDriverType_OSS;
4348 }
4349 return s_linuxDriver;
4350// end elif defined(RT_OS_LINUX)
4351#elif defined(RT_OS_DARWIN)
4352 return AudioDriverType_CoreAudio;
4353#elif defined(RT_OS_OS2)
4354 return AudioDriverType_MMPM;
4355#elif defined(RT_OS_FREEBSD)
4356 return AudioDriverType_OSS;
4357#else
4358 return AudioDriverType_Null;
4359#endif
4360}
4361
4362/**
4363 * Called from write() before calling ConfigFileBase::createStubDocument().
4364 * This adjusts the settings version in m->sv if incompatible settings require
4365 * a settings bump, whereas otherwise we try to preserve the settings version
4366 * to avoid breaking compatibility with older versions.
4367 *
4368 * We do the checks in here in reverse order: newest first, oldest last, so
4369 * that we avoid unnecessary checks since some of these are expensive.
4370 */
4371void MachineConfigFile::bumpSettingsVersionIfNeeded()
4372{
4373 if (m->sv < SettingsVersion_v1_11)
4374 {
4375 // VirtualBox 4.0 adds HD audio, CPU priorities, fault tolerance,
4376 // per-machine media registries, VRDE, JRockitVE and bandwidth gorups.
4377 if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
4378 || hardwareMachine.ulCpuExecutionCap != 100
4379 || machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
4380 || machineUserData.uFaultTolerancePort
4381 || machineUserData.uFaultToleranceInterval
4382 || !machineUserData.strFaultToleranceAddress.isEmpty()
4383 || mediaRegistry.llHardDisks.size()
4384 || mediaRegistry.llDvdImages.size()
4385 || mediaRegistry.llFloppyImages.size()
4386 || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty()
4387 || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty()
4388 || machineUserData.strOsType == "JRockitVE"
4389 || hardwareMachine.ioSettings.llBandwidthGroups.size()
4390 )
4391 m->sv = SettingsVersion_v1_11;
4392 }
4393
4394 if (m->sv < SettingsVersion_v1_11)
4395 {
4396 /* If the properties contain elements other than "TCP/Ports" and "TCP/Address",
4397 * then increase the version to VBox 4.0.
4398 */
4399 unsigned cOldProperties = 0;
4400
4401 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
4402 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
4403 cOldProperties++;
4404 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
4405 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
4406 cOldProperties++;
4407
4408 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
4409 m->sv = SettingsVersion_v1_11;
4410 }
4411
4412 // Settings version 1.11 is required if more than one controller of each type
4413 // is present.
4414 if (m->sv < SettingsVersion_v1_11)
4415 {
4416 size_t cSata = 0;
4417 size_t cScsiLsi = 0;
4418 size_t cScsiBuslogic = 0;
4419 size_t cSas = 0;
4420 size_t cIde = 0;
4421 size_t cFloppy = 0;
4422
4423 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
4424 it != storageMachine.llStorageControllers.end();
4425 ++it)
4426 {
4427 switch ((*it).storageBus)
4428 {
4429 case StorageBus_IDE:
4430 cIde++;
4431 break;
4432 case StorageBus_SATA:
4433 cSata++;
4434 break;
4435 case StorageBus_SAS:
4436 cSas++;
4437 break;
4438 case StorageBus_SCSI:
4439 if ((*it).controllerType == StorageControllerType_LsiLogic)
4440 cScsiLsi++;
4441 else
4442 cScsiBuslogic++;
4443 break;
4444 case StorageBus_Floppy:
4445 cFloppy++;
4446 break;
4447 default:
4448 // Do nothing
4449 break;
4450 }
4451
4452 if ( cSata > 1
4453 || cScsiLsi > 1
4454 || cScsiBuslogic > 1
4455 || cSas > 1
4456 || cIde > 1
4457 || cFloppy > 1)
4458 m->sv = SettingsVersion_v1_11;
4459 }
4460 }
4461
4462 // settings version 1.9 is required if there is not exactly one DVD
4463 // or more than one floppy drive present or the DVD is not at the secondary
4464 // master; this check is a bit more complicated
4465 //
4466 // settings version 1.10 is required if the host cache should be disabled
4467 //
4468 // settings version 1.11 is required for bandwidth limits
4469 if (m->sv < SettingsVersion_v1_11)
4470 {
4471 // count attached DVDs and floppies (only if < v1.9)
4472 size_t cDVDs = 0;
4473 size_t cFloppies = 0;
4474
4475 // need to run thru all the storage controllers and attached devices to figure this out
4476 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
4477 it != storageMachine.llStorageControllers.end();
4478 ++it)
4479 {
4480 const StorageController &sctl = *it;
4481 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
4482 it2 != sctl.llAttachedDevices.end();
4483 ++it2)
4484 {
4485 const AttachedDevice &att = *it2;
4486
4487 // Bandwidth limitations are new in VirtualBox 4.0 (1.11)
4488 if ( (m->sv < SettingsVersion_v1_11)
4489 && (att.strBwGroup.length() != 0)
4490 )
4491 {
4492 m->sv = SettingsVersion_v1_11;
4493 break; /* abort the loop -- we will not raise the version further */
4494 }
4495
4496 // disabling the host IO cache requires settings version 1.10
4497 if ( (m->sv < SettingsVersion_v1_10)
4498 && (!sctl.fUseHostIOCache)
4499 )
4500 m->sv = SettingsVersion_v1_10;
4501
4502 // we can only write the StorageController/@Instance attribute with v1.9
4503 if ( (m->sv < SettingsVersion_v1_9)
4504 && (sctl.ulInstance != 0)
4505 )
4506 m->sv = SettingsVersion_v1_9;
4507
4508 if (m->sv < SettingsVersion_v1_9)
4509 {
4510 if (att.deviceType == DeviceType_DVD)
4511 {
4512 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
4513 || (att.lPort != 1) // DVDs not at secondary master?
4514 || (att.lDevice != 0)
4515 )
4516 m->sv = SettingsVersion_v1_9;
4517
4518 ++cDVDs;
4519 }
4520 else if (att.deviceType == DeviceType_Floppy)
4521 ++cFloppies;
4522 }
4523 }
4524 }
4525
4526 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
4527 // so any deviation from that will require settings version 1.9
4528 if ( (m->sv < SettingsVersion_v1_9)
4529 && ( (cDVDs != 1)
4530 || (cFloppies > 1)
4531 )
4532 )
4533 m->sv = SettingsVersion_v1_9;
4534 }
4535
4536 // VirtualBox 3.2: Check for non default I/O settings
4537 if (m->sv < SettingsVersion_v1_10)
4538 {
4539 if ( (hardwareMachine.ioSettings.fIoCacheEnabled != true)
4540 || (hardwareMachine.ioSettings.ulIoCacheSize != 5)
4541 // and remote desktop video redirection channel
4542 || (hardwareMachine.vrdeSettings.fVideoChannel)
4543 // and page fusion
4544 || (hardwareMachine.fPageFusionEnabled)
4545 // and CPU hotplug, RTC timezone control, HID type and HPET
4546 || machineUserData.fRTCUseUTC
4547 || hardwareMachine.fCpuHotPlug
4548 || hardwareMachine.pointingHidType != PointingHidType_PS2Mouse
4549 || hardwareMachine.keyboardHidType != KeyboardHidType_PS2Keyboard
4550 || hardwareMachine.fHpetEnabled
4551 )
4552 m->sv = SettingsVersion_v1_10;
4553 }
4554
4555 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main
4556 if (m->sv < SettingsVersion_v1_10)
4557 {
4558 NetworkAdaptersList::const_iterator netit;
4559 for (netit = hardwareMachine.llNetworkAdapters.begin();
4560 netit != hardwareMachine.llNetworkAdapters.end();
4561 ++netit)
4562 {
4563 if ( (m->sv < SettingsVersion_v1_11)
4564 && (netit->ulBandwidthLimit)
4565 )
4566 {
4567 /* New in VirtualBox 4.0 */
4568 m->sv = SettingsVersion_v1_11;
4569 break;
4570 }
4571 else if ( (m->sv < SettingsVersion_v1_10)
4572 && (netit->fEnabled)
4573 && (netit->mode == NetworkAttachmentType_NAT)
4574 && ( netit->nat.u32Mtu != 0
4575 || netit->nat.u32SockRcv != 0
4576 || netit->nat.u32SockSnd != 0
4577 || netit->nat.u32TcpRcv != 0
4578 || netit->nat.u32TcpSnd != 0
4579 || !netit->nat.fDnsPassDomain
4580 || netit->nat.fDnsProxy
4581 || netit->nat.fDnsUseHostResolver
4582 || netit->nat.fAliasLog
4583 || netit->nat.fAliasProxyOnly
4584 || netit->nat.fAliasUseSamePorts
4585 || netit->nat.strTftpPrefix.length()
4586 || netit->nat.strTftpBootFile.length()
4587 || netit->nat.strTftpNextServer.length()
4588 || netit->nat.llRules.size()
4589 )
4590 )
4591 {
4592 m->sv = SettingsVersion_v1_10;
4593 // no break because we still might need v1.11 above
4594 }
4595 else if ( (m->sv < SettingsVersion_v1_10)
4596 && (netit->fEnabled)
4597 && (netit->ulBootPriority != 0)
4598 )
4599 {
4600 m->sv = SettingsVersion_v1_10;
4601 // no break because we still might need v1.11 above
4602 }
4603 }
4604 }
4605
4606 // all the following require settings version 1.9
4607 if ( (m->sv < SettingsVersion_v1_9)
4608 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
4609 || (hardwareMachine.fHardwareVirtExclusive != HWVIRTEXCLUSIVEDEFAULT)
4610 || machineUserData.fTeleporterEnabled
4611 || machineUserData.uTeleporterPort
4612 || !machineUserData.strTeleporterAddress.isEmpty()
4613 || !machineUserData.strTeleporterPassword.isEmpty()
4614 || !hardwareMachine.uuid.isEmpty()
4615 )
4616 )
4617 m->sv = SettingsVersion_v1_9;
4618
4619 // "accelerate 2d video" requires settings version 1.8
4620 if ( (m->sv < SettingsVersion_v1_8)
4621 && (hardwareMachine.fAccelerate2DVideo)
4622 )
4623 m->sv = SettingsVersion_v1_8;
4624
4625 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
4626 if ( m->sv < SettingsVersion_v1_4
4627 && hardwareMachine.strVersion != "1"
4628 )
4629 m->sv = SettingsVersion_v1_4;
4630}
4631
4632/**
4633 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
4634 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
4635 * in particular if the file cannot be written.
4636 */
4637void MachineConfigFile::write(const com::Utf8Str &strFilename)
4638{
4639 try
4640 {
4641 // createStubDocument() sets the settings version to at least 1.7; however,
4642 // we might need to enfore a later settings version if incompatible settings
4643 // are present:
4644 bumpSettingsVersionIfNeeded();
4645
4646 m->strFilename = strFilename;
4647 createStubDocument();
4648
4649 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
4650 buildMachineXML(*pelmMachine,
4651 MachineConfigFile::BuildMachineXML_IncludeSnapshots
4652 | MachineConfigFile::BuildMachineXML_MediaRegistry,
4653 // but not BuildMachineXML_WriteVboxVersionAttribute
4654 NULL); /* pllElementsWithUuidAttributes */
4655
4656 // now go write the XML
4657 xml::XmlFileWriter writer(*m->pDoc);
4658 writer.write(m->strFilename.c_str(), true /*fSafe*/);
4659
4660 m->fFileExists = true;
4661 clearDocument();
4662 }
4663 catch (...)
4664 {
4665 clearDocument();
4666 throw;
4667 }
4668}
注意: 瀏覽 TracBrowser 來幫助您使用儲存庫瀏覽器

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette