VirtualBox

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

Last change on this file since 56050 was 56035, checked in by vboxsync, 10 years ago

Main/Machine+USBController+StorageController: Mix of deleting useless functionality, fixing of missing sanity checks and adding some additional functionality. The removed functionality is the possibility to specify patters of guest properties which will trigger notifications, which wasn't useful as it is per VM, sabotaging other API clients (e.g. the VM process assumes it gets the notifications it needs). The storage controller setters were lacking a lot of state and consistency sanity checking, which is now fixed. Both the USB and storage controllers can now be renamed (no API client uses this functionality though), all with very pessimistic assumptions (only when the VM is powered off).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 248.1 KB
Line 
1/* $Id: Settings.cpp 56035 2015-05-22 16:03:35Z 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 * VBOX_XML_VERSION does not have to be changed if the settings for a default VM do not
20 * touch newly introduced attributes or tags. It has the benefit that older VirtualBox
21 * versions do not trigger their "newer" code path.
22 *
23 * Once a new settings version has been added, these are the rules for introducing a new
24 * setting: If an XML element or attribute or value is introduced that was not present in
25 * previous versions, then settings version checks need to be introduced. See the
26 * SettingsVersion enumeration in src/VBox/Main/idl/VirtualBox.xidl for details about which
27 * version was used when.
28 *
29 * The settings versions checks are necessary because since version 3.1, VirtualBox no longer
30 * automatically converts XML settings files but only if necessary, that is, if settings are
31 * present that the old format does not support. If we write an element or attribute to a
32 * settings file of an older version, then an old VirtualBox (before 3.1) will attempt to
33 * validate it with XML schema, and that will certainly fail.
34 *
35 * So, to introduce a new setting:
36 *
37 * 1) Make sure the constructor of corresponding settings structure has a proper default.
38 *
39 * 2) In the settings reader method, try to read the setting; if it's there, great, if not,
40 * the default value will have been set by the constructor. The rule is to be tolerant
41 * here.
42 *
43 * 3) In MachineConfigFile::bumpSettingsVersionIfNeeded(), check if the new setting has
44 * a non-default value (i.e. that differs from the constructor). If so, bump the
45 * settings version to the current version so the settings writer (4) can write out
46 * the non-default value properly.
47 *
48 * So far a corresponding method for MainConfigFile has not been necessary since there
49 * have been no incompatible changes yet.
50 *
51 * 4) In the settings writer method, write the setting _only_ if the current settings
52 * version (stored in m->sv) is high enough. That is, for VirtualBox 4.0, write it
53 * only if (m->sv >= SettingsVersion_v1_11).
54 */
55
56/*
57 * Copyright (C) 2007-2015 Oracle Corporation
58 *
59 * This file is part of VirtualBox Open Source Edition (OSE), as
60 * available from http://www.virtualbox.org. This file is free software;
61 * you can redistribute it and/or modify it under the terms of the GNU
62 * General Public License (GPL) as published by the Free Software
63 * Foundation, in version 2 as it comes in the "COPYING" file of the
64 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
65 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
66 */
67
68#include "VBox/com/string.h"
69#include "VBox/settings.h"
70#include <iprt/cpp/xml.h>
71#include <iprt/stream.h>
72#include <iprt/ctype.h>
73#include <iprt/file.h>
74#include <iprt/process.h>
75#include <iprt/ldr.h>
76#include <iprt/cpp/lock.h>
77
78// generated header
79#include "SchemaDefs.h"
80
81#include "Logging.h"
82#include "HashedPw.h"
83
84using namespace com;
85using namespace settings;
86
87////////////////////////////////////////////////////////////////////////////////
88//
89// Defines
90//
91////////////////////////////////////////////////////////////////////////////////
92
93/** VirtualBox XML settings namespace */
94#define VBOX_XML_NAMESPACE "http://www.innotek.de/VirtualBox-settings"
95
96/** VirtualBox XML settings version number substring ("x.y") */
97#define VBOX_XML_VERSION "1.12"
98
99/** VirtualBox XML settings version platform substring */
100#if defined (RT_OS_DARWIN)
101# define VBOX_XML_PLATFORM "macosx"
102#elif defined (RT_OS_FREEBSD)
103# define VBOX_XML_PLATFORM "freebsd"
104#elif defined (RT_OS_LINUX)
105# define VBOX_XML_PLATFORM "linux"
106#elif defined (RT_OS_NETBSD)
107# define VBOX_XML_PLATFORM "netbsd"
108#elif defined (RT_OS_OPENBSD)
109# define VBOX_XML_PLATFORM "openbsd"
110#elif defined (RT_OS_OS2)
111# define VBOX_XML_PLATFORM "os2"
112#elif defined (RT_OS_SOLARIS)
113# define VBOX_XML_PLATFORM "solaris"
114#elif defined (RT_OS_WINDOWS)
115# define VBOX_XML_PLATFORM "windows"
116#else
117# error Unsupported platform!
118#endif
119
120/** VirtualBox XML settings full version string ("x.y-platform") */
121#define VBOX_XML_VERSION_FULL VBOX_XML_VERSION "-" VBOX_XML_PLATFORM
122
123const struct Snapshot settings::g_SnapshotEmpty; /* default ctor is OK */
124const struct Medium settings::g_MediumEmpty; /* default ctor is OK */
125
126////////////////////////////////////////////////////////////////////////////////
127//
128// Internal data
129//
130////////////////////////////////////////////////////////////////////////////////
131
132/**
133 * Opaque data structore for ConfigFileBase (only declared
134 * in header, defined only here).
135 */
136
137struct ConfigFileBase::Data
138{
139 Data()
140 : pDoc(NULL),
141 pelmRoot(NULL),
142 sv(SettingsVersion_Null),
143 svRead(SettingsVersion_Null)
144 {}
145
146 ~Data()
147 {
148 cleanup();
149 }
150
151 RTCString strFilename;
152 bool fFileExists;
153
154 xml::Document *pDoc;
155 xml::ElementNode *pelmRoot;
156
157 com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux"
158 SettingsVersion_T sv; // e.g. SettingsVersion_v1_7
159
160 SettingsVersion_T svRead; // settings version that the original file had when it was read,
161 // or SettingsVersion_Null if none
162
163 void copyFrom(const Data &d)
164 {
165 strFilename = d.strFilename;
166 fFileExists = d.fFileExists;
167 strSettingsVersionFull = d.strSettingsVersionFull;
168 sv = d.sv;
169 svRead = d.svRead;
170 }
171
172 void cleanup()
173 {
174 if (pDoc)
175 {
176 delete pDoc;
177 pDoc = NULL;
178 pelmRoot = NULL;
179 }
180 }
181};
182
183/**
184 * Private exception class (not in the header file) that makes
185 * throwing xml::LogicError instances easier. That class is public
186 * and should be caught by client code.
187 */
188class settings::ConfigFileError : public xml::LogicError
189{
190public:
191 ConfigFileError(const ConfigFileBase *file,
192 const xml::Node *pNode,
193 const char *pcszFormat, ...)
194 : xml::LogicError()
195 {
196 va_list args;
197 va_start(args, pcszFormat);
198 Utf8Str strWhat(pcszFormat, args);
199 va_end(args);
200
201 Utf8Str strLine;
202 if (pNode)
203 strLine = Utf8StrFmt(" (line %RU32)", pNode->getLineNumber());
204
205 const char *pcsz = strLine.c_str();
206 Utf8StrFmt str(N_("Error in %s%s -- %s"),
207 file->m->strFilename.c_str(),
208 (pcsz) ? pcsz : "",
209 strWhat.c_str());
210
211 setWhat(str.c_str());
212 }
213};
214
215////////////////////////////////////////////////////////////////////////////////
216//
217// MediaRegistry
218//
219////////////////////////////////////////////////////////////////////////////////
220
221bool Medium::operator==(const Medium &m) const
222{
223 return (uuid == m.uuid)
224 && (strLocation == m.strLocation)
225 && (strDescription == m.strDescription)
226 && (strFormat == m.strFormat)
227 && (fAutoReset == m.fAutoReset)
228 && (properties == m.properties)
229 && (hdType == m.hdType)
230 && (llChildren== m.llChildren); // this is deep and recurses
231}
232
233bool MediaRegistry::operator==(const MediaRegistry &m) const
234{
235 return llHardDisks == m.llHardDisks
236 && llDvdImages == m.llDvdImages
237 && llFloppyImages == m.llFloppyImages;
238}
239
240////////////////////////////////////////////////////////////////////////////////
241//
242// ConfigFileBase
243//
244////////////////////////////////////////////////////////////////////////////////
245
246/**
247 * Constructor. Allocates the XML internals, parses the XML file if
248 * pstrFilename is != NULL and reads the settings version from it.
249 * @param strFilename
250 */
251ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
252 : m(new Data)
253{
254 Utf8Str strMajor;
255 Utf8Str strMinor;
256
257 m->fFileExists = false;
258
259 if (pstrFilename)
260 {
261 // reading existing settings file:
262 m->strFilename = *pstrFilename;
263
264 xml::XmlFileParser parser;
265 m->pDoc = new xml::Document;
266 parser.read(*pstrFilename,
267 *m->pDoc);
268
269 m->fFileExists = true;
270
271 m->pelmRoot = m->pDoc->getRootElement();
272 if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
273 throw ConfigFileError(this, NULL, N_("Root element in VirtualBox settings files must be \"VirtualBox\"."));
274
275 if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
276 throw ConfigFileError(this, m->pelmRoot, N_("Required VirtualBox/@version attribute is missing"));
277
278 LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
279
280 // parse settings version; allow future versions but fail if file is older than 1.6
281 m->sv = SettingsVersion_Null;
282 if (m->strSettingsVersionFull.length() > 3)
283 {
284 const char *pcsz = m->strSettingsVersionFull.c_str();
285 char c;
286
287 while ( (c = *pcsz)
288 && RT_C_IS_DIGIT(c)
289 )
290 {
291 strMajor.append(c);
292 ++pcsz;
293 }
294
295 if (*pcsz++ == '.')
296 {
297 while ( (c = *pcsz)
298 && RT_C_IS_DIGIT(c)
299 )
300 {
301 strMinor.append(c);
302 ++pcsz;
303 }
304 }
305
306 uint32_t ulMajor = RTStrToUInt32(strMajor.c_str());
307 uint32_t ulMinor = RTStrToUInt32(strMinor.c_str());
308
309 if (ulMajor == 1)
310 {
311 if (ulMinor == 3)
312 m->sv = SettingsVersion_v1_3;
313 else if (ulMinor == 4)
314 m->sv = SettingsVersion_v1_4;
315 else if (ulMinor == 5)
316 m->sv = SettingsVersion_v1_5;
317 else if (ulMinor == 6)
318 m->sv = SettingsVersion_v1_6;
319 else if (ulMinor == 7)
320 m->sv = SettingsVersion_v1_7;
321 else if (ulMinor == 8)
322 m->sv = SettingsVersion_v1_8;
323 else if (ulMinor == 9)
324 m->sv = SettingsVersion_v1_9;
325 else if (ulMinor == 10)
326 m->sv = SettingsVersion_v1_10;
327 else if (ulMinor == 11)
328 m->sv = SettingsVersion_v1_11;
329 else if (ulMinor == 12)
330 m->sv = SettingsVersion_v1_12;
331 else if (ulMinor == 13)
332 m->sv = SettingsVersion_v1_13;
333 else if (ulMinor == 14)
334 m->sv = SettingsVersion_v1_14;
335 else if (ulMinor == 15)
336 m->sv = SettingsVersion_v1_15;
337 else if (ulMinor > 15)
338 m->sv = SettingsVersion_Future;
339 }
340 else if (ulMajor > 1)
341 m->sv = SettingsVersion_Future;
342
343 Log(("Parsed settings version %d.%d to enum value %d\n", ulMajor, ulMinor, m->sv));
344 }
345
346 if (m->sv == SettingsVersion_Null)
347 throw ConfigFileError(this, m->pelmRoot, N_("Cannot handle settings version '%s'"), m->strSettingsVersionFull.c_str());
348
349 // remember the settings version we read in case it gets upgraded later,
350 // so we know when to make backups
351 m->svRead = m->sv;
352 }
353 else
354 {
355 // creating new settings file:
356 m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
357 m->sv = SettingsVersion_v1_12;
358 }
359}
360
361ConfigFileBase::ConfigFileBase(const ConfigFileBase &other)
362 : m(new Data)
363{
364 copyBaseFrom(other);
365 m->strFilename = "";
366 m->fFileExists = false;
367}
368
369/**
370 * Clean up.
371 */
372ConfigFileBase::~ConfigFileBase()
373{
374 if (m)
375 {
376 delete m;
377 m = NULL;
378 }
379}
380
381/**
382 * Helper function to convert a MediaType enum value into string from.
383 * @param t
384 */
385/*static*/
386const char *ConfigFileBase::stringifyMediaType(MediaType t)
387{
388 switch (t)
389 {
390 case HardDisk:
391 return "hard disk";
392 case DVDImage:
393 return "DVD";
394 case FloppyImage:
395 return "floppy";
396 default:
397 AssertMsgFailed(("media type %d\n", t));
398 return "UNKNOWN";
399 }
400}
401
402/**
403 * Helper function that parses a UUID in string form into
404 * a com::Guid item. Accepts UUIDs both with and without
405 * "{}" brackets. Throws on errors.
406 * @param guid
407 * @param strUUID
408 */
409void ConfigFileBase::parseUUID(Guid &guid,
410 const Utf8Str &strUUID) const
411{
412 guid = strUUID.c_str();
413 if (guid.isZero())
414 throw ConfigFileError(this, NULL, N_("UUID \"%s\" has zero format"), strUUID.c_str());
415 else if (!guid.isValid())
416 throw ConfigFileError(this, NULL, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
417}
418
419/**
420 * Parses the given string in str and attempts to treat it as an ISO
421 * date/time stamp to put into timestamp. Throws on errors.
422 * @param timestamp
423 * @param str
424 */
425void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
426 const com::Utf8Str &str) const
427{
428 const char *pcsz = str.c_str();
429 // yyyy-mm-ddThh:mm:ss
430 // "2009-07-10T11:54:03Z"
431 // 01234567890123456789
432 // 1
433 if (str.length() > 19)
434 {
435 // timezone must either be unspecified or 'Z' for UTC
436 if ( (pcsz[19])
437 && (pcsz[19] != 'Z')
438 )
439 throw ConfigFileError(this, NULL, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
440
441 int32_t yyyy;
442 uint32_t mm, dd, hh, min, secs;
443 if ( (pcsz[4] == '-')
444 && (pcsz[7] == '-')
445 && (pcsz[10] == 'T')
446 && (pcsz[13] == ':')
447 && (pcsz[16] == ':')
448 )
449 {
450 int rc;
451 if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
452 // could theoretically be negative but let's assume that nobody
453 // created virtual machines before the Christian era
454 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
455 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
456 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
457 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
458 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
459 )
460 {
461 RTTIME time =
462 {
463 yyyy,
464 (uint8_t)mm,
465 0,
466 0,
467 (uint8_t)dd,
468 (uint8_t)hh,
469 (uint8_t)min,
470 (uint8_t)secs,
471 0,
472 RTTIME_FLAGS_TYPE_UTC,
473 0
474 };
475 if (RTTimeNormalize(&time))
476 if (RTTimeImplode(&timestamp, &time))
477 return;
478 }
479
480 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
481 }
482
483 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
484 }
485}
486
487/**
488 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
489 * @param stamp
490 * @return
491 */
492com::Utf8Str ConfigFileBase::stringifyTimestamp(const RTTIMESPEC &stamp) const
493{
494 RTTIME time;
495 if (!RTTimeExplode(&time, &stamp))
496 throw ConfigFileError(this, NULL, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
497
498 return Utf8StrFmt("%04u-%02u-%02uT%02u:%02u:%02uZ",
499 time.i32Year, time.u8Month, time.u8MonthDay,
500 time.u8Hour, time.u8Minute, time.u8Second);
501}
502
503/**
504 * Helper method to read in an ExtraData subtree and stores its contents
505 * in the given map of extradata items. Used for both main and machine
506 * extradata (MainConfigFile and MachineConfigFile).
507 * @param elmExtraData
508 * @param map
509 */
510void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
511 StringsMap &map)
512{
513 xml::NodesLoop nlLevel4(elmExtraData);
514 const xml::ElementNode *pelmExtraDataItem;
515 while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
516 {
517 if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
518 {
519 // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
520 Utf8Str strName, strValue;
521 if ( pelmExtraDataItem->getAttributeValue("name", strName)
522 && pelmExtraDataItem->getAttributeValue("value", strValue) )
523 map[strName] = strValue;
524 else
525 throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing"));
526 }
527 }
528}
529
530/**
531 * Reads <USBDeviceFilter> entries from under the given elmDeviceFilters node and
532 * stores them in the given linklist. This is in ConfigFileBase because it's used
533 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
534 * filters).
535 * @param elmDeviceFilters
536 * @param ll
537 */
538void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
539 USBDeviceFiltersList &ll)
540{
541 xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
542 const xml::ElementNode *pelmLevel4Child;
543 while ((pelmLevel4Child = nl1.forAllNodes()))
544 {
545 USBDeviceFilter flt;
546 flt.action = USBDeviceFilterAction_Ignore;
547 Utf8Str strAction;
548 if ( pelmLevel4Child->getAttributeValue("name", flt.strName)
549 && pelmLevel4Child->getAttributeValue("active", flt.fActive))
550 {
551 if (!pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId))
552 pelmLevel4Child->getAttributeValue("vendorid", flt.strVendorId); // used before 1.3
553 if (!pelmLevel4Child->getAttributeValue("productId", flt.strProductId))
554 pelmLevel4Child->getAttributeValue("productid", flt.strProductId); // used before 1.3
555 pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
556 pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
557 pelmLevel4Child->getAttributeValue("product", flt.strProduct);
558 if (!pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber))
559 pelmLevel4Child->getAttributeValue("serialnumber", flt.strSerialNumber); // used before 1.3
560 pelmLevel4Child->getAttributeValue("port", flt.strPort);
561
562 // the next 2 are irrelevant for host USB objects
563 pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
564 pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
565
566 // action is only used with host USB objects
567 if (pelmLevel4Child->getAttributeValue("action", strAction))
568 {
569 if (strAction == "Ignore")
570 flt.action = USBDeviceFilterAction_Ignore;
571 else if (strAction == "Hold")
572 flt.action = USBDeviceFilterAction_Hold;
573 else
574 throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str());
575 }
576
577 ll.push_back(flt);
578 }
579 }
580}
581
582/**
583 * Reads a media registry entry from the main VirtualBox.xml file.
584 *
585 * Whereas the current media registry code is fairly straightforward, it was quite a mess
586 * with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements
587 * in the media registry were much more inconsistent, and different elements were used
588 * depending on the type of device and image.
589 *
590 * @param t
591 * @param elmMedium
592 * @param med
593 */
594void ConfigFileBase::readMediumOne(MediaType t,
595 const xml::ElementNode &elmMedium,
596 Medium &med)
597{
598 // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
599
600 Utf8Str strUUID;
601 if (!elmMedium.getAttributeValue("uuid", strUUID))
602 throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName());
603
604 parseUUID(med.uuid, strUUID);
605
606 bool fNeedsLocation = true;
607
608 if (t == HardDisk)
609 {
610 if (m->sv < SettingsVersion_v1_4)
611 {
612 // here the system is:
613 // <HardDisk uuid="{....}" type="normal">
614 // <VirtualDiskImage filePath="/path/to/xxx.vdi"/>
615 // </HardDisk>
616
617 fNeedsLocation = false;
618 bool fNeedsFilePath = true;
619 const xml::ElementNode *pelmImage;
620 if ((pelmImage = elmMedium.findChildElement("VirtualDiskImage")))
621 med.strFormat = "VDI";
622 else if ((pelmImage = elmMedium.findChildElement("VMDKImage")))
623 med.strFormat = "VMDK";
624 else if ((pelmImage = elmMedium.findChildElement("VHDImage")))
625 med.strFormat = "VHD";
626 else if ((pelmImage = elmMedium.findChildElement("ISCSIHardDisk")))
627 {
628 med.strFormat = "iSCSI";
629
630 fNeedsFilePath = false;
631 // location is special here: current settings specify an "iscsi://user@server:port/target/lun"
632 // string for the location and also have several disk properties for these, whereas this used
633 // to be hidden in several sub-elements before 1.4, so compose a location string and set up
634 // the properties:
635 med.strLocation = "iscsi://";
636 Utf8Str strUser, strServer, strPort, strTarget, strLun;
637 if (pelmImage->getAttributeValue("userName", strUser))
638 {
639 med.strLocation.append(strUser);
640 med.strLocation.append("@");
641 }
642 Utf8Str strServerAndPort;
643 if (pelmImage->getAttributeValue("server", strServer))
644 {
645 strServerAndPort = strServer;
646 }
647 if (pelmImage->getAttributeValue("port", strPort))
648 {
649 if (strServerAndPort.length())
650 strServerAndPort.append(":");
651 strServerAndPort.append(strPort);
652 }
653 med.strLocation.append(strServerAndPort);
654 if (pelmImage->getAttributeValue("target", strTarget))
655 {
656 med.strLocation.append("/");
657 med.strLocation.append(strTarget);
658 }
659 if (pelmImage->getAttributeValue("lun", strLun))
660 {
661 med.strLocation.append("/");
662 med.strLocation.append(strLun);
663 }
664
665 if (strServer.length() && strPort.length())
666 med.properties["TargetAddress"] = strServerAndPort;
667 if (strTarget.length())
668 med.properties["TargetName"] = strTarget;
669 if (strUser.length())
670 med.properties["InitiatorUsername"] = strUser;
671 Utf8Str strPassword;
672 if (pelmImage->getAttributeValue("password", strPassword))
673 med.properties["InitiatorSecret"] = strPassword;
674 if (strLun.length())
675 med.properties["LUN"] = strLun;
676 }
677 else if ((pelmImage = elmMedium.findChildElement("CustomHardDisk")))
678 {
679 fNeedsFilePath = false;
680 fNeedsLocation = true;
681 // also requires @format attribute, which will be queried below
682 }
683 else
684 throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName());
685
686 if (fNeedsFilePath)
687 {
688 if (!(pelmImage->getAttributeValuePath("filePath", med.strLocation)))
689 throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName());
690 }
691 }
692
693 if (med.strFormat.isEmpty()) // not set with 1.4 format above, or 1.4 Custom format?
694 if (!elmMedium.getAttributeValue("format", med.strFormat))
695 throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName());
696
697 if (!elmMedium.getAttributeValue("autoReset", med.fAutoReset))
698 med.fAutoReset = false;
699
700 Utf8Str strType;
701 if (elmMedium.getAttributeValue("type", strType))
702 {
703 // pre-1.4 used lower case, so make this case-insensitive
704 strType.toUpper();
705 if (strType == "NORMAL")
706 med.hdType = MediumType_Normal;
707 else if (strType == "IMMUTABLE")
708 med.hdType = MediumType_Immutable;
709 else if (strType == "WRITETHROUGH")
710 med.hdType = MediumType_Writethrough;
711 else if (strType == "SHAREABLE")
712 med.hdType = MediumType_Shareable;
713 else if (strType == "READONLY")
714 med.hdType = MediumType_Readonly;
715 else if (strType == "MULTIATTACH")
716 med.hdType = MediumType_MultiAttach;
717 else
718 throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable, Writethrough, Shareable, Readonly or MultiAttach"));
719 }
720 }
721 else
722 {
723 if (m->sv < SettingsVersion_v1_4)
724 {
725 // DVD and floppy images before 1.4 had "src" attribute instead of "location"
726 if (!elmMedium.getAttributeValue("src", med.strLocation))
727 throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName());
728
729 fNeedsLocation = false;
730 }
731
732 if (!elmMedium.getAttributeValue("format", med.strFormat))
733 {
734 // DVD and floppy images before 1.11 had no format attribute. assign the default.
735 med.strFormat = "RAW";
736 }
737
738 if (t == DVDImage)
739 med.hdType = MediumType_Readonly;
740 else if (t == FloppyImage)
741 med.hdType = MediumType_Writethrough;
742 }
743
744 if (fNeedsLocation)
745 // current files and 1.4 CustomHardDisk elements must have a location attribute
746 if (!elmMedium.getAttributeValue("location", med.strLocation))
747 throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName());
748
749 elmMedium.getAttributeValue("Description", med.strDescription); // optional
750
751 // handle medium properties
752 xml::NodesLoop nl2(elmMedium, "Property");
753 const xml::ElementNode *pelmHDChild;
754 while ((pelmHDChild = nl2.forAllNodes()))
755 {
756 Utf8Str strPropName, strPropValue;
757 if ( pelmHDChild->getAttributeValue("name", strPropName)
758 && pelmHDChild->getAttributeValue("value", strPropValue) )
759 med.properties[strPropName] = strPropValue;
760 else
761 throw ConfigFileError(this, pelmHDChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
762 }
763}
764
765/**
766 * Reads a media registry entry from the main VirtualBox.xml file and recurses
767 * into children where applicable.
768 *
769 * @param t
770 * @param depth
771 * @param elmMedium
772 * @param med
773 */
774void ConfigFileBase::readMedium(MediaType t,
775 uint32_t depth,
776 const xml::ElementNode &elmMedium, // HardDisk node if root; if recursing,
777 // child HardDisk node or DiffHardDisk node for pre-1.4
778 Medium &med) // medium settings to fill out
779{
780 if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
781 throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
782
783 // Do not inline this method call, as the purpose of having this separate
784 // is to save on stack size. Less local variables are the key for reaching
785 // deep recursion levels with small stack (XPCOM/g++ without optimization).
786 readMediumOne(t, elmMedium, med);
787
788 if (t != HardDisk)
789 return;
790
791 // recurse to handle children
792 MediaList &llSettingsChildren = med.llChildren;
793 xml::NodesLoop nl2(elmMedium, m->sv >= SettingsVersion_v1_4 ? "HardDisk" : "DiffHardDisk");
794 const xml::ElementNode *pelmHDChild;
795 while ((pelmHDChild = nl2.forAllNodes()))
796 {
797 // recurse with this element and put the child at the end of the list.
798 // XPCOM has very small stack, avoid big local variables and use the
799 // list element.
800 llSettingsChildren.push_back(g_MediumEmpty);
801 readMedium(t,
802 depth + 1,
803 *pelmHDChild,
804 llSettingsChildren.back());
805 }
806}
807
808/**
809 * Reads in the entire <MediaRegistry> chunk and stores its media in the lists
810 * of the given MediaRegistry structure.
811 *
812 * This is used in both MainConfigFile and MachineConfigFile since starting with
813 * VirtualBox 4.0, we can have media registries in both.
814 *
815 * For pre-1.4 files, this gets called with the <DiskRegistry> chunk instead.
816 *
817 * @param elmMediaRegistry
818 */
819void ConfigFileBase::readMediaRegistry(const xml::ElementNode &elmMediaRegistry,
820 MediaRegistry &mr)
821{
822 xml::NodesLoop nl1(elmMediaRegistry);
823 const xml::ElementNode *pelmChild1;
824 while ((pelmChild1 = nl1.forAllNodes()))
825 {
826 MediaType t = Error;
827 if (pelmChild1->nameEquals("HardDisks"))
828 t = HardDisk;
829 else if (pelmChild1->nameEquals("DVDImages"))
830 t = DVDImage;
831 else if (pelmChild1->nameEquals("FloppyImages"))
832 t = FloppyImage;
833 else
834 continue;
835
836 xml::NodesLoop nl2(*pelmChild1);
837 const xml::ElementNode *pelmMedium;
838 while ((pelmMedium = nl2.forAllNodes()))
839 {
840 if ( t == HardDisk
841 && (pelmMedium->nameEquals("HardDisk")))
842 {
843 mr.llHardDisks.push_back(g_MediumEmpty);
844 readMedium(t, 1, *pelmMedium, mr.llHardDisks.back());
845 }
846 else if ( t == DVDImage
847 && (pelmMedium->nameEquals("Image")))
848 {
849 mr.llDvdImages.push_back(g_MediumEmpty);
850 readMedium(t, 1, *pelmMedium, mr.llDvdImages.back());
851 }
852 else if ( t == FloppyImage
853 && (pelmMedium->nameEquals("Image")))
854 {
855 mr.llFloppyImages.push_back(g_MediumEmpty);
856 readMedium(t, 1, *pelmMedium, mr.llFloppyImages.back());
857 }
858 }
859 }
860}
861
862/**
863 * This is common version for reading NAT port forward rule in per-_machine's_adapter_ and
864 * per-network approaches.
865 * Note: this function doesn't in fill given list from xml::ElementNodesList, because there is conflicting
866 * declaration in ovmfreader.h.
867 */
868void ConfigFileBase::readNATForwardRuleList(const xml::ElementNode &elmParent, NATRuleList &llRules)
869{
870 xml::ElementNodesList plstRules;
871 elmParent.getChildElements(plstRules, "Forwarding");
872 for (xml::ElementNodesList::iterator pf = plstRules.begin(); pf != plstRules.end(); ++pf)
873 {
874 NATRule rule;
875 uint32_t port = 0;
876 (*pf)->getAttributeValue("name", rule.strName);
877 (*pf)->getAttributeValue("proto", (uint32_t&)rule.proto);
878 (*pf)->getAttributeValue("hostip", rule.strHostIP);
879 (*pf)->getAttributeValue("hostport", port);
880 rule.u16HostPort = port;
881 (*pf)->getAttributeValue("guestip", rule.strGuestIP);
882 (*pf)->getAttributeValue("guestport", port);
883 rule.u16GuestPort = port;
884 llRules.push_back(rule);
885 }
886}
887
888void ConfigFileBase::readNATLoopbacks(const xml::ElementNode &elmParent, NATLoopbackOffsetList &llLoopbacks)
889{
890 xml::ElementNodesList plstLoopbacks;
891 elmParent.getChildElements(plstLoopbacks, "Loopback4");
892 for (xml::ElementNodesList::iterator lo = plstLoopbacks.begin();
893 lo != plstLoopbacks.end(); ++lo)
894 {
895 NATHostLoopbackOffset loopback;
896 (*lo)->getAttributeValue("address", loopback.strLoopbackHostAddress);
897 (*lo)->getAttributeValue("offset", (uint32_t&)loopback.u32Offset);
898 llLoopbacks.push_back(loopback);
899 }
900}
901
902
903/**
904 * Adds a "version" attribute to the given XML element with the
905 * VirtualBox settings version (e.g. "1.10-linux"). Used by
906 * the XML format for the root element and by the OVF export
907 * for the vbox:Machine element.
908 * @param elm
909 */
910void ConfigFileBase::setVersionAttribute(xml::ElementNode &elm)
911{
912 const char *pcszVersion = NULL;
913 switch (m->sv)
914 {
915 case SettingsVersion_v1_8:
916 pcszVersion = "1.8";
917 break;
918
919 case SettingsVersion_v1_9:
920 pcszVersion = "1.9";
921 break;
922
923 case SettingsVersion_v1_10:
924 pcszVersion = "1.10";
925 break;
926
927 case SettingsVersion_v1_11:
928 pcszVersion = "1.11";
929 break;
930
931 case SettingsVersion_v1_12:
932 pcszVersion = "1.12";
933 break;
934
935 case SettingsVersion_v1_13:
936 pcszVersion = "1.13";
937 break;
938
939 case SettingsVersion_v1_14:
940 pcszVersion = "1.14";
941 break;
942
943 case SettingsVersion_v1_15:
944 pcszVersion = "1.15";
945 break;
946
947 default:
948 // catch human error: the assertion below will trigger in debug
949 // or dbgopt builds, so hopefully this will get noticed sooner in
950 // the future, because it's easy to forget top update something.
951 AssertMsg(m->sv <= SettingsVersion_v1_7, ("Settings.cpp: unexpected settings version %d, unhandled future version?\n", m->sv));
952 // silently upgrade if this is less than 1.7 because that's the oldest we can write
953 if (m->sv <= SettingsVersion_v1_7)
954 {
955 pcszVersion = "1.7";
956 m->sv = SettingsVersion_v1_7;
957 }
958 else
959 {
960 // This is reached for SettingsVersion_Future and forgotten
961 // settings version after SettingsVersion_v1_7, which should
962 // not happen (see assertion above). Set the version to the
963 // latest known version, to minimize loss of information, but
964 // as we can't predict the future we have to use some format
965 // we know, and latest should be the best choice. Note that
966 // for "forgotten settings" this may not be the best choice,
967 // but as it's an omission of someone who changed this file
968 // it's the only generic possibility.
969 pcszVersion = "1.15";
970 m->sv = SettingsVersion_v1_15;
971 }
972 break;
973 }
974
975 elm.setAttribute("version", Utf8StrFmt("%s-%s",
976 pcszVersion,
977 VBOX_XML_PLATFORM)); // e.g. "linux"
978}
979
980/**
981 * Creates a new stub xml::Document in the m->pDoc member with the
982 * root "VirtualBox" element set up. This is used by both
983 * MainConfigFile and MachineConfigFile at the beginning of writing
984 * out their XML.
985 *
986 * Before calling this, it is the responsibility of the caller to
987 * set the "sv" member to the required settings version that is to
988 * be written. For newly created files, the settings version will be
989 * the latest (1.12); for files read in from disk earlier, it will be
990 * the settings version indicated in the file. However, this method
991 * will silently make sure that the settings version is always
992 * at least 1.7 and change it if necessary, since there is no write
993 * support for earlier settings versions.
994 */
995void ConfigFileBase::createStubDocument()
996{
997 Assert(m->pDoc == NULL);
998 m->pDoc = new xml::Document;
999
1000 m->pelmRoot = m->pDoc->createRootElement("VirtualBox",
1001 "\n"
1002 "** DO NOT EDIT THIS FILE.\n"
1003 "** If you make changes to this file while any VirtualBox related application\n"
1004 "** is running, your changes will be overwritten later, without taking effect.\n"
1005 "** Use VBoxManage or the VirtualBox Manager GUI to make changes.\n"
1006);
1007 m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
1008
1009 // add settings version attribute to root element
1010 setVersionAttribute(*m->pelmRoot);
1011
1012 // since this gets called before the XML document is actually written out,
1013 // this is where we must check whether we're upgrading the settings version
1014 // and need to make a backup, so the user can go back to an earlier
1015 // VirtualBox version and recover his old settings files.
1016 if ( (m->svRead != SettingsVersion_Null) // old file exists?
1017 && (m->svRead < m->sv) // we're upgrading?
1018 )
1019 {
1020 // compose new filename: strip off trailing ".xml"/".vbox"
1021 Utf8Str strFilenameNew;
1022 Utf8Str strExt = ".xml";
1023 if (m->strFilename.endsWith(".xml"))
1024 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 4);
1025 else if (m->strFilename.endsWith(".vbox"))
1026 {
1027 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 5);
1028 strExt = ".vbox";
1029 }
1030
1031 // and append something like "-1.3-linux.xml"
1032 strFilenameNew.append("-");
1033 strFilenameNew.append(m->strSettingsVersionFull); // e.g. "1.3-linux"
1034 strFilenameNew.append(strExt); // .xml for main config, .vbox for machine config
1035
1036 RTFileMove(m->strFilename.c_str(),
1037 strFilenameNew.c_str(),
1038 0); // no RTFILEMOVE_FLAGS_REPLACE
1039
1040 // do this only once
1041 m->svRead = SettingsVersion_Null;
1042 }
1043}
1044
1045/**
1046 * Creates an <ExtraData> node under the given parent element with
1047 * <ExtraDataItem> childern according to the contents of the given
1048 * map.
1049 *
1050 * This is in ConfigFileBase because it's used in both MainConfigFile
1051 * and MachineConfigFile, which both can have extradata.
1052 *
1053 * @param elmParent
1054 * @param me
1055 */
1056void ConfigFileBase::buildExtraData(xml::ElementNode &elmParent,
1057 const StringsMap &me)
1058{
1059 if (me.size())
1060 {
1061 xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
1062 for (StringsMap::const_iterator it = me.begin();
1063 it != me.end();
1064 ++it)
1065 {
1066 const Utf8Str &strName = it->first;
1067 const Utf8Str &strValue = it->second;
1068 xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
1069 pelmThis->setAttribute("name", strName);
1070 pelmThis->setAttribute("value", strValue);
1071 }
1072 }
1073}
1074
1075/**
1076 * Creates <DeviceFilter> nodes under the given parent element according to
1077 * the contents of the given USBDeviceFiltersList. This is in ConfigFileBase
1078 * because it's used in both MainConfigFile (for host filters) and
1079 * MachineConfigFile (for machine filters).
1080 *
1081 * If fHostMode is true, this means that we're supposed to write filters
1082 * for the IHost interface (respect "action", omit "strRemote" and
1083 * "ulMaskedInterfaces" in struct USBDeviceFilter).
1084 *
1085 * @param elmParent
1086 * @param ll
1087 * @param fHostMode
1088 */
1089void ConfigFileBase::buildUSBDeviceFilters(xml::ElementNode &elmParent,
1090 const USBDeviceFiltersList &ll,
1091 bool fHostMode)
1092{
1093 for (USBDeviceFiltersList::const_iterator it = ll.begin();
1094 it != ll.end();
1095 ++it)
1096 {
1097 const USBDeviceFilter &flt = *it;
1098 xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
1099 pelmFilter->setAttribute("name", flt.strName);
1100 pelmFilter->setAttribute("active", flt.fActive);
1101 if (flt.strVendorId.length())
1102 pelmFilter->setAttribute("vendorId", flt.strVendorId);
1103 if (flt.strProductId.length())
1104 pelmFilter->setAttribute("productId", flt.strProductId);
1105 if (flt.strRevision.length())
1106 pelmFilter->setAttribute("revision", flt.strRevision);
1107 if (flt.strManufacturer.length())
1108 pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
1109 if (flt.strProduct.length())
1110 pelmFilter->setAttribute("product", flt.strProduct);
1111 if (flt.strSerialNumber.length())
1112 pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
1113 if (flt.strPort.length())
1114 pelmFilter->setAttribute("port", flt.strPort);
1115
1116 if (fHostMode)
1117 {
1118 const char *pcsz =
1119 (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
1120 : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
1121 pelmFilter->setAttribute("action", pcsz);
1122 }
1123 else
1124 {
1125 if (flt.strRemote.length())
1126 pelmFilter->setAttribute("remote", flt.strRemote);
1127 if (flt.ulMaskedInterfaces)
1128 pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
1129 }
1130 }
1131}
1132
1133/**
1134 * Creates a single <HardDisk> element for the given Medium structure
1135 * and recurses to write the child hard disks underneath. Called from
1136 * MainConfigFile::write().
1137 *
1138 * @param t
1139 * @param depth
1140 * @param elmMedium
1141 * @param mdm
1142 */
1143void ConfigFileBase::buildMedium(MediaType t,
1144 uint32_t depth,
1145 xml::ElementNode &elmMedium,
1146 const Medium &mdm)
1147{
1148 if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
1149 throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
1150
1151 xml::ElementNode *pelmMedium;
1152
1153 if (t == HardDisk)
1154 pelmMedium = elmMedium.createChild("HardDisk");
1155 else
1156 pelmMedium = elmMedium.createChild("Image");
1157
1158 pelmMedium->setAttribute("uuid", mdm.uuid.toStringCurly());
1159
1160 pelmMedium->setAttributePath("location", mdm.strLocation);
1161
1162 if (t == HardDisk || RTStrICmp(mdm.strFormat.c_str(), "RAW"))
1163 pelmMedium->setAttribute("format", mdm.strFormat);
1164 if ( t == HardDisk
1165 && mdm.fAutoReset)
1166 pelmMedium->setAttribute("autoReset", mdm.fAutoReset);
1167 if (mdm.strDescription.length())
1168 pelmMedium->setAttribute("Description", mdm.strDescription);
1169
1170 for (StringsMap::const_iterator it = mdm.properties.begin();
1171 it != mdm.properties.end();
1172 ++it)
1173 {
1174 xml::ElementNode *pelmProp = pelmMedium->createChild("Property");
1175 pelmProp->setAttribute("name", it->first);
1176 pelmProp->setAttribute("value", it->second);
1177 }
1178
1179 // only for base hard disks, save the type
1180 if (depth == 1)
1181 {
1182 // no need to save the usual DVD/floppy medium types
1183 if ( ( t != DVDImage
1184 || ( mdm.hdType != MediumType_Writethrough // shouldn't happen
1185 && mdm.hdType != MediumType_Readonly))
1186 && ( t != FloppyImage
1187 || mdm.hdType != MediumType_Writethrough))
1188 {
1189 const char *pcszType =
1190 mdm.hdType == MediumType_Normal ? "Normal" :
1191 mdm.hdType == MediumType_Immutable ? "Immutable" :
1192 mdm.hdType == MediumType_Writethrough ? "Writethrough" :
1193 mdm.hdType == MediumType_Shareable ? "Shareable" :
1194 mdm.hdType == MediumType_Readonly ? "Readonly" :
1195 mdm.hdType == MediumType_MultiAttach ? "MultiAttach" :
1196 "INVALID";
1197 pelmMedium->setAttribute("type", pcszType);
1198 }
1199 }
1200
1201 for (MediaList::const_iterator it = mdm.llChildren.begin();
1202 it != mdm.llChildren.end();
1203 ++it)
1204 {
1205 // recurse for children
1206 buildMedium(t, // device type
1207 depth + 1, // depth
1208 *pelmMedium, // parent
1209 *it); // settings::Medium
1210 }
1211}
1212
1213/**
1214 * Creates a <MediaRegistry> node under the given parent and writes out all
1215 * hard disks and DVD and floppy images from the lists in the given MediaRegistry
1216 * structure under it.
1217 *
1218 * This is used in both MainConfigFile and MachineConfigFile since starting with
1219 * VirtualBox 4.0, we can have media registries in both.
1220 *
1221 * @param elmParent
1222 * @param mr
1223 */
1224void ConfigFileBase::buildMediaRegistry(xml::ElementNode &elmParent,
1225 const MediaRegistry &mr)
1226{
1227 xml::ElementNode *pelmMediaRegistry = elmParent.createChild("MediaRegistry");
1228
1229 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
1230 for (MediaList::const_iterator it = mr.llHardDisks.begin();
1231 it != mr.llHardDisks.end();
1232 ++it)
1233 {
1234 buildMedium(HardDisk, 1, *pelmHardDisks, *it);
1235 }
1236
1237 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
1238 for (MediaList::const_iterator it = mr.llDvdImages.begin();
1239 it != mr.llDvdImages.end();
1240 ++it)
1241 {
1242 buildMedium(DVDImage, 1, *pelmDVDImages, *it);
1243 }
1244
1245 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
1246 for (MediaList::const_iterator it = mr.llFloppyImages.begin();
1247 it != mr.llFloppyImages.end();
1248 ++it)
1249 {
1250 buildMedium(FloppyImage, 1, *pelmFloppyImages, *it);
1251 }
1252}
1253
1254/**
1255 * Serialize NAT port-forwarding rules in parent container.
1256 * Note: it's responsibility of caller to create parent of the list tag.
1257 * because this method used for serializing per-_mahine's_adapter_ and per-network approaches.
1258 */
1259void ConfigFileBase::buildNATForwardRuleList(xml::ElementNode &elmParent, const NATRuleList &natRuleList)
1260{
1261 for (NATRuleList::const_iterator r = natRuleList.begin();
1262 r != natRuleList.end(); ++r)
1263 {
1264 xml::ElementNode *pelmPF;
1265 pelmPF = elmParent.createChild("Forwarding");
1266 if ((*r).strName.length())
1267 pelmPF->setAttribute("name", (*r).strName);
1268 pelmPF->setAttribute("proto", (*r).proto);
1269 if ((*r).strHostIP.length())
1270 pelmPF->setAttribute("hostip", (*r).strHostIP);
1271 if ((*r).u16HostPort)
1272 pelmPF->setAttribute("hostport", (*r).u16HostPort);
1273 if ((*r).strGuestIP.length())
1274 pelmPF->setAttribute("guestip", (*r).strGuestIP);
1275 if ((*r).u16GuestPort)
1276 pelmPF->setAttribute("guestport", (*r).u16GuestPort);
1277 }
1278}
1279
1280
1281void ConfigFileBase::buildNATLoopbacks(xml::ElementNode &elmParent, const NATLoopbackOffsetList &natLoopbackOffsetList)
1282{
1283 for (NATLoopbackOffsetList::const_iterator lo = natLoopbackOffsetList.begin();
1284 lo != natLoopbackOffsetList.end(); ++lo)
1285 {
1286 xml::ElementNode *pelmLo;
1287 pelmLo = elmParent.createChild("Loopback4");
1288 pelmLo->setAttribute("address", (*lo).strLoopbackHostAddress);
1289 pelmLo->setAttribute("offset", (*lo).u32Offset);
1290 }
1291}
1292
1293/**
1294 * Cleans up memory allocated by the internal XML parser. To be called by
1295 * descendant classes when they're done analyzing the DOM tree to discard it.
1296 */
1297void ConfigFileBase::clearDocument()
1298{
1299 m->cleanup();
1300}
1301
1302/**
1303 * Returns true only if the underlying config file exists on disk;
1304 * either because the file has been loaded from disk, or it's been written
1305 * to disk, or both.
1306 * @return
1307 */
1308bool ConfigFileBase::fileExists()
1309{
1310 return m->fFileExists;
1311}
1312
1313/**
1314 * Copies the base variables from another instance. Used by Machine::saveSettings
1315 * so that the settings version does not get lost when a copy of the Machine settings
1316 * file is made to see if settings have actually changed.
1317 * @param b
1318 */
1319void ConfigFileBase::copyBaseFrom(const ConfigFileBase &b)
1320{
1321 m->copyFrom(*b.m);
1322}
1323
1324////////////////////////////////////////////////////////////////////////////////
1325//
1326// Structures shared between Machine XML and VirtualBox.xml
1327//
1328////////////////////////////////////////////////////////////////////////////////
1329
1330/**
1331 * Comparison operator. This gets called from MachineConfigFile::operator==,
1332 * which in turn gets called from Machine::saveSettings to figure out whether
1333 * machine settings have really changed and thus need to be written out to disk.
1334 */
1335bool USBDeviceFilter::operator==(const USBDeviceFilter &u) const
1336{
1337 return ( (this == &u)
1338 || ( (strName == u.strName)
1339 && (fActive == u.fActive)
1340 && (strVendorId == u.strVendorId)
1341 && (strProductId == u.strProductId)
1342 && (strRevision == u.strRevision)
1343 && (strManufacturer == u.strManufacturer)
1344 && (strProduct == u.strProduct)
1345 && (strSerialNumber == u.strSerialNumber)
1346 && (strPort == u.strPort)
1347 && (action == u.action)
1348 && (strRemote == u.strRemote)
1349 && (ulMaskedInterfaces == u.ulMaskedInterfaces)
1350 )
1351 );
1352}
1353
1354////////////////////////////////////////////////////////////////////////////////
1355//
1356// MainConfigFile
1357//
1358////////////////////////////////////////////////////////////////////////////////
1359
1360/**
1361 * Reads one <MachineEntry> from the main VirtualBox.xml file.
1362 * @param elmMachineRegistry
1363 */
1364void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
1365{
1366 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
1367 xml::NodesLoop nl1(elmMachineRegistry);
1368 const xml::ElementNode *pelmChild1;
1369 while ((pelmChild1 = nl1.forAllNodes()))
1370 {
1371 if (pelmChild1->nameEquals("MachineEntry"))
1372 {
1373 MachineRegistryEntry mre;
1374 Utf8Str strUUID;
1375 if ( pelmChild1->getAttributeValue("uuid", strUUID)
1376 && pelmChild1->getAttributeValue("src", mre.strSettingsFile) )
1377 {
1378 parseUUID(mre.uuid, strUUID);
1379 llMachines.push_back(mre);
1380 }
1381 else
1382 throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing"));
1383 }
1384 }
1385}
1386
1387/**
1388 * Reads in the <DHCPServers> chunk.
1389 * @param elmDHCPServers
1390 */
1391void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
1392{
1393 xml::NodesLoop nl1(elmDHCPServers);
1394 const xml::ElementNode *pelmServer;
1395 while ((pelmServer = nl1.forAllNodes()))
1396 {
1397 if (pelmServer->nameEquals("DHCPServer"))
1398 {
1399 DHCPServer srv;
1400 if ( pelmServer->getAttributeValue("networkName", srv.strNetworkName)
1401 && pelmServer->getAttributeValue("IPAddress", srv.strIPAddress)
1402 && pelmServer->getAttributeValue("networkMask", srv.GlobalDhcpOptions[DhcpOpt_SubnetMask].text)
1403 && pelmServer->getAttributeValue("lowerIP", srv.strIPLower)
1404 && pelmServer->getAttributeValue("upperIP", srv.strIPUpper)
1405 && pelmServer->getAttributeValue("enabled", srv.fEnabled) )
1406 {
1407 xml::NodesLoop nlOptions(*pelmServer, "Options");
1408 const xml::ElementNode *options;
1409 /* XXX: Options are in 1:1 relation to DHCPServer */
1410
1411 while ((options = nlOptions.forAllNodes()))
1412 {
1413 readDhcpOptions(srv.GlobalDhcpOptions, *options);
1414 } /* end of forall("Options") */
1415 xml::NodesLoop nlConfig(*pelmServer, "Config");
1416 const xml::ElementNode *cfg;
1417 while ((cfg = nlConfig.forAllNodes()))
1418 {
1419 com::Utf8Str strVmName;
1420 uint32_t u32Slot;
1421 cfg->getAttributeValue("vm-name", strVmName);
1422 cfg->getAttributeValue("slot", u32Slot);
1423 readDhcpOptions(srv.VmSlot2OptionsM[VmNameSlotKey(strVmName, u32Slot)], *cfg);
1424 }
1425 llDhcpServers.push_back(srv);
1426 }
1427 else
1428 throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing"));
1429 }
1430 }
1431}
1432
1433void MainConfigFile::readDhcpOptions(DhcpOptionMap& map,
1434 const xml::ElementNode& options)
1435{
1436 xml::NodesLoop nl2(options, "Option");
1437 const xml::ElementNode *opt;
1438 while ((opt = nl2.forAllNodes()))
1439 {
1440 DhcpOpt_T OptName;
1441 com::Utf8Str OptText;
1442 int32_t OptEnc = DhcpOptValue::LEGACY;
1443
1444 opt->getAttributeValue("name", (uint32_t&)OptName);
1445
1446 if (OptName == DhcpOpt_SubnetMask)
1447 continue;
1448
1449 opt->getAttributeValue("value", OptText);
1450 opt->getAttributeValue("encoding", OptEnc);
1451
1452 map[OptName] = DhcpOptValue(OptText, (DhcpOptValue::Encoding)OptEnc);
1453 } /* end of forall("Option") */
1454
1455}
1456
1457/**
1458 * Reads in the <NATNetworks> chunk.
1459 * @param elmNATNetworks
1460 */
1461void MainConfigFile::readNATNetworks(const xml::ElementNode &elmNATNetworks)
1462{
1463 xml::NodesLoop nl1(elmNATNetworks);
1464 const xml::ElementNode *pelmNet;
1465 while ((pelmNet = nl1.forAllNodes()))
1466 {
1467 if (pelmNet->nameEquals("NATNetwork"))
1468 {
1469 NATNetwork net;
1470 if ( pelmNet->getAttributeValue("networkName", net.strNetworkName)
1471 && pelmNet->getAttributeValue("enabled", net.fEnabled)
1472 && pelmNet->getAttributeValue("network", net.strNetwork)
1473 && pelmNet->getAttributeValue("ipv6", net.fIPv6)
1474 && pelmNet->getAttributeValue("ipv6prefix", net.strIPv6Prefix)
1475 && pelmNet->getAttributeValue("advertiseDefaultIPv6Route", net.fAdvertiseDefaultIPv6Route)
1476 && pelmNet->getAttributeValue("needDhcp", net.fNeedDhcpServer) )
1477 {
1478 pelmNet->getAttributeValue("loopback6", net.u32HostLoopback6Offset);
1479 const xml::ElementNode *pelmMappings;
1480 if ((pelmMappings = pelmNet->findChildElement("Mappings")))
1481 readNATLoopbacks(*pelmMappings, net.llHostLoopbackOffsetList);
1482
1483 const xml::ElementNode *pelmPortForwardRules4;
1484 if ((pelmPortForwardRules4 = pelmNet->findChildElement("PortForwarding4")))
1485 readNATForwardRuleList(*pelmPortForwardRules4,
1486 net.llPortForwardRules4);
1487
1488 const xml::ElementNode *pelmPortForwardRules6;
1489 if ((pelmPortForwardRules6 = pelmNet->findChildElement("PortForwarding6")))
1490 readNATForwardRuleList(*pelmPortForwardRules6,
1491 net.llPortForwardRules6);
1492
1493 llNATNetworks.push_back(net);
1494 }
1495 else
1496 throw ConfigFileError(this, pelmNet, N_("Required NATNetwork/@networkName, @gateway, @network,@advertiseDefaultIpv6Route , @needDhcp or @enabled attribute is missing"));
1497 }
1498 }
1499}
1500
1501/**
1502 * Constructor.
1503 *
1504 * If pstrFilename is != NULL, this reads the given settings file into the member
1505 * variables and various substructures and lists. Otherwise, the member variables
1506 * are initialized with default values.
1507 *
1508 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1509 * the caller should catch; if this constructor does not throw, then the member
1510 * variables contain meaningful values (either from the file or defaults).
1511 *
1512 * @param strFilename
1513 */
1514MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
1515 : ConfigFileBase(pstrFilename)
1516{
1517 if (pstrFilename)
1518 {
1519 // the ConfigFileBase constructor has loaded the XML file, so now
1520 // we need only analyze what is in there
1521 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1522 const xml::ElementNode *pelmRootChild;
1523 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1524 {
1525 if (pelmRootChild->nameEquals("Global"))
1526 {
1527 xml::NodesLoop nlGlobalChildren(*pelmRootChild);
1528 const xml::ElementNode *pelmGlobalChild;
1529 while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
1530 {
1531 if (pelmGlobalChild->nameEquals("SystemProperties"))
1532 {
1533 pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1534 pelmGlobalChild->getAttributeValue("LoggingLevel", systemProperties.strLoggingLevel);
1535 pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1536 if (!pelmGlobalChild->getAttributeValue("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary))
1537 // pre-1.11 used @remoteDisplayAuthLibrary instead
1538 pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strVRDEAuthLibrary);
1539 pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1540 pelmGlobalChild->getAttributeValue("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
1541 pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.ulLogHistoryCount);
1542 pelmGlobalChild->getAttributeValue("autostartDatabasePath", systemProperties.strAutostartDatabasePath);
1543 pelmGlobalChild->getAttributeValue("defaultFrontend", systemProperties.strDefaultFrontend);
1544 pelmGlobalChild->getAttributeValue("exclusiveHwVirt", systemProperties.fExclusiveHwVirt);
1545 }
1546 else if (pelmGlobalChild->nameEquals("ExtraData"))
1547 readExtraData(*pelmGlobalChild, mapExtraDataItems);
1548 else if (pelmGlobalChild->nameEquals("MachineRegistry"))
1549 readMachineRegistry(*pelmGlobalChild);
1550 else if ( (pelmGlobalChild->nameEquals("MediaRegistry"))
1551 || ( (m->sv < SettingsVersion_v1_4)
1552 && (pelmGlobalChild->nameEquals("DiskRegistry"))
1553 )
1554 )
1555 readMediaRegistry(*pelmGlobalChild, mediaRegistry);
1556 else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
1557 {
1558 xml::NodesLoop nlLevel4(*pelmGlobalChild);
1559 const xml::ElementNode *pelmLevel4Child;
1560 while ((pelmLevel4Child = nlLevel4.forAllNodes()))
1561 {
1562 if (pelmLevel4Child->nameEquals("DHCPServers"))
1563 readDHCPServers(*pelmLevel4Child);
1564 if (pelmLevel4Child->nameEquals("NATNetworks"))
1565 readNATNetworks(*pelmLevel4Child);
1566 }
1567 }
1568 else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
1569 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
1570 }
1571 } // end if (pelmRootChild->nameEquals("Global"))
1572 }
1573
1574 clearDocument();
1575 }
1576
1577 // DHCP servers were introduced with settings version 1.7; if we're loading
1578 // from an older version OR this is a fresh install, then add one DHCP server
1579 // with default settings
1580 if ( (!llDhcpServers.size())
1581 && ( (!pstrFilename) // empty VirtualBox.xml file
1582 || (m->sv < SettingsVersion_v1_7) // upgrading from before 1.7
1583 )
1584 )
1585 {
1586 DHCPServer srv;
1587 srv.strNetworkName =
1588#ifdef RT_OS_WINDOWS
1589 "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
1590#else
1591 "HostInterfaceNetworking-vboxnet0";
1592#endif
1593 srv.strIPAddress = "192.168.56.100";
1594 srv.GlobalDhcpOptions[DhcpOpt_SubnetMask] = DhcpOptValue("255.255.255.0");
1595 srv.strIPLower = "192.168.56.101";
1596 srv.strIPUpper = "192.168.56.254";
1597 srv.fEnabled = true;
1598 llDhcpServers.push_back(srv);
1599 }
1600}
1601
1602void MainConfigFile::bumpSettingsVersionIfNeeded()
1603{
1604 if (m->sv < SettingsVersion_v1_14)
1605 {
1606 // VirtualBox 4.3 adds NAT networks.
1607 if ( !llNATNetworks.empty())
1608 m->sv = SettingsVersion_v1_14;
1609 }
1610}
1611
1612
1613/**
1614 * Called from the IVirtualBox interface to write out VirtualBox.xml. This
1615 * builds an XML DOM tree and writes it out to disk.
1616 */
1617void MainConfigFile::write(const com::Utf8Str strFilename)
1618{
1619 bumpSettingsVersionIfNeeded();
1620
1621 m->strFilename = strFilename;
1622 createStubDocument();
1623
1624 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
1625
1626 buildExtraData(*pelmGlobal, mapExtraDataItems);
1627
1628 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
1629 for (MachinesRegistry::const_iterator it = llMachines.begin();
1630 it != llMachines.end();
1631 ++it)
1632 {
1633 // <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"/>
1634 const MachineRegistryEntry &mre = *it;
1635 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
1636 pelmMachineEntry->setAttribute("uuid", mre.uuid.toStringCurly());
1637 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
1638 }
1639
1640 buildMediaRegistry(*pelmGlobal, mediaRegistry);
1641
1642 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
1643 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
1644 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
1645 it != llDhcpServers.end();
1646 ++it)
1647 {
1648 const DHCPServer &d = *it;
1649 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
1650 DhcpOptConstIterator itOpt;
1651 itOpt = d.GlobalDhcpOptions.find(DhcpOpt_SubnetMask);
1652
1653 pelmThis->setAttribute("networkName", d.strNetworkName);
1654 pelmThis->setAttribute("IPAddress", d.strIPAddress);
1655 if (itOpt != d.GlobalDhcpOptions.end())
1656 pelmThis->setAttribute("networkMask", itOpt->second.text);
1657 pelmThis->setAttribute("lowerIP", d.strIPLower);
1658 pelmThis->setAttribute("upperIP", d.strIPUpper);
1659 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
1660 /* We assume that if there're only 1 element it means that */
1661 size_t cOpt = d.GlobalDhcpOptions.size();
1662 /* We don't want duplicate validation check of networkMask here*/
1663 if ( ( itOpt == d.GlobalDhcpOptions.end()
1664 && cOpt > 0)
1665 || cOpt > 1)
1666 {
1667 xml::ElementNode *pelmOptions = pelmThis->createChild("Options");
1668 for (itOpt = d.GlobalDhcpOptions.begin();
1669 itOpt != d.GlobalDhcpOptions.end();
1670 ++itOpt)
1671 {
1672 if (itOpt->first == DhcpOpt_SubnetMask)
1673 continue;
1674
1675 xml::ElementNode *pelmOpt = pelmOptions->createChild("Option");
1676
1677 if (!pelmOpt)
1678 break;
1679
1680 pelmOpt->setAttribute("name", itOpt->first);
1681 pelmOpt->setAttribute("value", itOpt->second.text);
1682 if (itOpt->second.encoding != DhcpOptValue::LEGACY)
1683 pelmOpt->setAttribute("encoding", (int)itOpt->second.encoding);
1684 }
1685 } /* end of if */
1686
1687 if (d.VmSlot2OptionsM.size() > 0)
1688 {
1689 VmSlot2OptionsConstIterator itVmSlot;
1690 DhcpOptConstIterator itOpt1;
1691 for(itVmSlot = d.VmSlot2OptionsM.begin();
1692 itVmSlot != d.VmSlot2OptionsM.end();
1693 ++itVmSlot)
1694 {
1695 xml::ElementNode *pelmCfg = pelmThis->createChild("Config");
1696 pelmCfg->setAttribute("vm-name", itVmSlot->first.VmName);
1697 pelmCfg->setAttribute("slot", itVmSlot->first.Slot);
1698
1699 for (itOpt1 = itVmSlot->second.begin();
1700 itOpt1 != itVmSlot->second.end();
1701 ++itOpt1)
1702 {
1703 xml::ElementNode *pelmOpt = pelmCfg->createChild("Option");
1704 pelmOpt->setAttribute("name", itOpt1->first);
1705 pelmOpt->setAttribute("value", itOpt1->second.text);
1706 if (itOpt1->second.encoding != DhcpOptValue::LEGACY)
1707 pelmOpt->setAttribute("encoding", (int)itOpt1->second.encoding);
1708 }
1709 }
1710 } /* and of if */
1711
1712 }
1713
1714 xml::ElementNode *pelmNATNetworks;
1715 /* don't create entry if no NAT networks are registered. */
1716 if (!llNATNetworks.empty())
1717 {
1718 pelmNATNetworks = pelmNetserviceRegistry->createChild("NATNetworks");
1719 for (NATNetworksList::const_iterator it = llNATNetworks.begin();
1720 it != llNATNetworks.end();
1721 ++it)
1722 {
1723 const NATNetwork &n = *it;
1724 xml::ElementNode *pelmThis = pelmNATNetworks->createChild("NATNetwork");
1725 pelmThis->setAttribute("networkName", n.strNetworkName);
1726 pelmThis->setAttribute("network", n.strNetwork);
1727 pelmThis->setAttribute("ipv6", n.fIPv6 ? 1 : 0);
1728 pelmThis->setAttribute("ipv6prefix", n.strIPv6Prefix);
1729 pelmThis->setAttribute("advertiseDefaultIPv6Route", (n.fAdvertiseDefaultIPv6Route)? 1 : 0);
1730 pelmThis->setAttribute("needDhcp", (n.fNeedDhcpServer) ? 1 : 0);
1731 pelmThis->setAttribute("enabled", (n.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
1732 if (n.llPortForwardRules4.size())
1733 {
1734 xml::ElementNode *pelmPf4 = pelmThis->createChild("PortForwarding4");
1735 buildNATForwardRuleList(*pelmPf4, n.llPortForwardRules4);
1736 }
1737 if (n.llPortForwardRules6.size())
1738 {
1739 xml::ElementNode *pelmPf6 = pelmThis->createChild("PortForwarding6");
1740 buildNATForwardRuleList(*pelmPf6, n.llPortForwardRules6);
1741 }
1742
1743 if (n.llHostLoopbackOffsetList.size())
1744 {
1745 xml::ElementNode *pelmMappings = pelmThis->createChild("Mappings");
1746 buildNATLoopbacks(*pelmMappings, n.llHostLoopbackOffsetList);
1747
1748 }
1749 }
1750 }
1751
1752
1753 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
1754 if (systemProperties.strDefaultMachineFolder.length())
1755 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1756 if (systemProperties.strLoggingLevel.length())
1757 pelmSysProps->setAttribute("LoggingLevel", systemProperties.strLoggingLevel);
1758 if (systemProperties.strDefaultHardDiskFormat.length())
1759 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1760 if (systemProperties.strVRDEAuthLibrary.length())
1761 pelmSysProps->setAttribute("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary);
1762 if (systemProperties.strWebServiceAuthLibrary.length())
1763 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1764 if (systemProperties.strDefaultVRDEExtPack.length())
1765 pelmSysProps->setAttribute("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
1766 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.ulLogHistoryCount);
1767 if (systemProperties.strAutostartDatabasePath.length())
1768 pelmSysProps->setAttribute("autostartDatabasePath", systemProperties.strAutostartDatabasePath);
1769 if (systemProperties.strDefaultFrontend.length())
1770 pelmSysProps->setAttribute("defaultFrontend", systemProperties.strDefaultFrontend);
1771 pelmSysProps->setAttribute("exclusiveHwVirt", systemProperties.fExclusiveHwVirt);
1772
1773 buildUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
1774 host.llUSBDeviceFilters,
1775 true); // fHostMode
1776
1777 // now go write the XML
1778 xml::XmlFileWriter writer(*m->pDoc);
1779 writer.write(m->strFilename.c_str(), true /*fSafe*/);
1780
1781 m->fFileExists = true;
1782
1783 clearDocument();
1784}
1785
1786////////////////////////////////////////////////////////////////////////////////
1787//
1788// Machine XML structures
1789//
1790////////////////////////////////////////////////////////////////////////////////
1791
1792/**
1793 * Comparison operator. This gets called from MachineConfigFile::operator==,
1794 * which in turn gets called from Machine::saveSettings to figure out whether
1795 * machine settings have really changed and thus need to be written out to disk.
1796 */
1797bool VRDESettings::operator==(const VRDESettings& v) const
1798{
1799 return ( (this == &v)
1800 || ( (fEnabled == v.fEnabled)
1801 && (authType == v.authType)
1802 && (ulAuthTimeout == v.ulAuthTimeout)
1803 && (strAuthLibrary == v.strAuthLibrary)
1804 && (fAllowMultiConnection == v.fAllowMultiConnection)
1805 && (fReuseSingleConnection == v.fReuseSingleConnection)
1806 && (strVrdeExtPack == v.strVrdeExtPack)
1807 && (mapProperties == v.mapProperties)
1808 )
1809 );
1810}
1811
1812/**
1813 * Comparison operator. This gets called from MachineConfigFile::operator==,
1814 * which in turn gets called from Machine::saveSettings to figure out whether
1815 * machine settings have really changed and thus need to be written out to disk.
1816 */
1817bool BIOSSettings::operator==(const BIOSSettings &d) const
1818{
1819 return ( (this == &d)
1820 || ( fACPIEnabled == d.fACPIEnabled
1821 && fIOAPICEnabled == d.fIOAPICEnabled
1822 && fLogoFadeIn == d.fLogoFadeIn
1823 && fLogoFadeOut == d.fLogoFadeOut
1824 && ulLogoDisplayTime == d.ulLogoDisplayTime
1825 && strLogoImagePath == d.strLogoImagePath
1826 && biosBootMenuMode == d.biosBootMenuMode
1827 && fPXEDebugEnabled == d.fPXEDebugEnabled
1828 && llTimeOffset == d.llTimeOffset)
1829 );
1830}
1831
1832/**
1833 * Comparison operator. This gets called from MachineConfigFile::operator==,
1834 * which in turn gets called from Machine::saveSettings to figure out whether
1835 * machine settings have really changed and thus need to be written out to disk.
1836 */
1837bool USBController::operator==(const USBController &u) const
1838{
1839 return ( (this == &u)
1840 || ( (strName == u.strName)
1841 && (enmType == u.enmType)
1842 )
1843 );
1844}
1845
1846/**
1847 * Comparison operator. This gets called from MachineConfigFile::operator==,
1848 * which in turn gets called from Machine::saveSettings to figure out whether
1849 * machine settings have really changed and thus need to be written out to disk.
1850 */
1851bool USB::operator==(const USB &u) const
1852{
1853 return ( (this == &u)
1854 || ( (llUSBControllers == u.llUSBControllers)
1855 && (llDeviceFilters == u.llDeviceFilters)
1856 )
1857 );
1858}
1859
1860/**
1861 * Comparison operator. This gets called from MachineConfigFile::operator==,
1862 * which in turn gets called from Machine::saveSettings to figure out whether
1863 * machine settings have really changed and thus need to be written out to disk.
1864 */
1865bool NetworkAdapter::operator==(const NetworkAdapter &n) const
1866{
1867 return ( (this == &n)
1868 || ( (ulSlot == n.ulSlot)
1869 && (type == n.type)
1870 && (fEnabled == n.fEnabled)
1871 && (strMACAddress == n.strMACAddress)
1872 && (fCableConnected == n.fCableConnected)
1873 && (ulLineSpeed == n.ulLineSpeed)
1874 && (enmPromiscModePolicy == n.enmPromiscModePolicy)
1875 && (fTraceEnabled == n.fTraceEnabled)
1876 && (strTraceFile == n.strTraceFile)
1877 && (mode == n.mode)
1878 && (nat == n.nat)
1879 && (strBridgedName == n.strBridgedName)
1880 && (strHostOnlyName == n.strHostOnlyName)
1881 && (strInternalNetworkName == n.strInternalNetworkName)
1882 && (strGenericDriver == n.strGenericDriver)
1883 && (genericProperties == n.genericProperties)
1884 && (ulBootPriority == n.ulBootPriority)
1885 && (strBandwidthGroup == n.strBandwidthGroup)
1886 )
1887 );
1888}
1889
1890/**
1891 * Comparison operator. This gets called from MachineConfigFile::operator==,
1892 * which in turn gets called from Machine::saveSettings to figure out whether
1893 * machine settings have really changed and thus need to be written out to disk.
1894 */
1895bool SerialPort::operator==(const SerialPort &s) const
1896{
1897 return ( (this == &s)
1898 || ( (ulSlot == s.ulSlot)
1899 && (fEnabled == s.fEnabled)
1900 && (ulIOBase == s.ulIOBase)
1901 && (ulIRQ == s.ulIRQ)
1902 && (portMode == s.portMode)
1903 && (strPath == s.strPath)
1904 && (fServer == s.fServer)
1905 )
1906 );
1907}
1908
1909/**
1910 * Comparison operator. This gets called from MachineConfigFile::operator==,
1911 * which in turn gets called from Machine::saveSettings to figure out whether
1912 * machine settings have really changed and thus need to be written out to disk.
1913 */
1914bool ParallelPort::operator==(const ParallelPort &s) const
1915{
1916 return ( (this == &s)
1917 || ( (ulSlot == s.ulSlot)
1918 && (fEnabled == s.fEnabled)
1919 && (ulIOBase == s.ulIOBase)
1920 && (ulIRQ == s.ulIRQ)
1921 && (strPath == s.strPath)
1922 )
1923 );
1924}
1925
1926/**
1927 * Comparison operator. This gets called from MachineConfigFile::operator==,
1928 * which in turn gets called from Machine::saveSettings to figure out whether
1929 * machine settings have really changed and thus need to be written out to disk.
1930 */
1931bool SharedFolder::operator==(const SharedFolder &g) const
1932{
1933 return ( (this == &g)
1934 || ( (strName == g.strName)
1935 && (strHostPath == g.strHostPath)
1936 && (fWritable == g.fWritable)
1937 && (fAutoMount == g.fAutoMount)
1938 )
1939 );
1940}
1941
1942/**
1943 * Comparison operator. This gets called from MachineConfigFile::operator==,
1944 * which in turn gets called from Machine::saveSettings to figure out whether
1945 * machine settings have really changed and thus need to be written out to disk.
1946 */
1947bool GuestProperty::operator==(const GuestProperty &g) const
1948{
1949 return ( (this == &g)
1950 || ( (strName == g.strName)
1951 && (strValue == g.strValue)
1952 && (timestamp == g.timestamp)
1953 && (strFlags == g.strFlags)
1954 )
1955 );
1956}
1957
1958Hardware::Hardware()
1959 : strVersion("1"),
1960 fHardwareVirt(true),
1961 fNestedPaging(true),
1962 fVPID(true),
1963 fUnrestrictedExecution(true),
1964 fHardwareVirtForce(false),
1965 fTripleFaultReset(false),
1966 fPAE(false),
1967 enmLongMode(HC_ARCH_BITS == 64 ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled),
1968 cCPUs(1),
1969 fCpuHotPlug(false),
1970 fHPETEnabled(false),
1971 ulCpuExecutionCap(100),
1972 uCpuIdPortabilityLevel(0),
1973 ulMemorySizeMB((uint32_t)-1),
1974 graphicsControllerType(GraphicsControllerType_VBoxVGA),
1975 ulVRAMSizeMB(8),
1976 cMonitors(1),
1977 fAccelerate3D(false),
1978 fAccelerate2DVideo(false),
1979 ulVideoCaptureHorzRes(1024),
1980 ulVideoCaptureVertRes(768),
1981 ulVideoCaptureRate(512),
1982 ulVideoCaptureFPS(25),
1983 ulVideoCaptureMaxTime(0),
1984 ulVideoCaptureMaxSize(0),
1985 fVideoCaptureEnabled(false),
1986 u64VideoCaptureScreens(UINT64_C(0xffffffffffffffff)),
1987 strVideoCaptureFile(""),
1988 firmwareType(FirmwareType_BIOS),
1989 pointingHIDType(PointingHIDType_PS2Mouse),
1990 keyboardHIDType(KeyboardHIDType_PS2Keyboard),
1991 chipsetType(ChipsetType_PIIX3),
1992 paravirtProvider(ParavirtProvider_Legacy),
1993 fEmulatedUSBCardReader(false),
1994 clipboardMode(ClipboardMode_Disabled),
1995 dndMode(DnDMode_Disabled),
1996 ulMemoryBalloonSize(0),
1997 fPageFusionEnabled(false)
1998{
1999 mapBootOrder[0] = DeviceType_Floppy;
2000 mapBootOrder[1] = DeviceType_DVD;
2001 mapBootOrder[2] = DeviceType_HardDisk;
2002
2003 /* The default value for PAE depends on the host:
2004 * - 64 bits host -> always true
2005 * - 32 bits host -> true for Windows & Darwin (masked off if the host cpu doesn't support it anyway)
2006 */
2007#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
2008 fPAE = true;
2009#endif
2010
2011 /* The default value of large page supports depends on the host:
2012 * - 64 bits host -> true, unless it's Linux (pending further prediction work due to excessively expensive large page allocations)
2013 * - 32 bits host -> false
2014 */
2015#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
2016 fLargePages = true;
2017#else
2018 /* Not supported on 32 bits hosts. */
2019 fLargePages = false;
2020#endif
2021}
2022
2023/**
2024 * Comparison operator. This gets called from MachineConfigFile::operator==,
2025 * which in turn gets called from Machine::saveSettings to figure out whether
2026 * machine settings have really changed and thus need to be written out to disk.
2027 */
2028bool Hardware::operator==(const Hardware& h) const
2029{
2030 return ( (this == &h)
2031 || ( (strVersion == h.strVersion)
2032 && (uuid == h.uuid)
2033 && (fHardwareVirt == h.fHardwareVirt)
2034 && (fNestedPaging == h.fNestedPaging)
2035 && (fLargePages == h.fLargePages)
2036 && (fVPID == h.fVPID)
2037 && (fUnrestrictedExecution == h.fUnrestrictedExecution)
2038 && (fHardwareVirtForce == h.fHardwareVirtForce)
2039 && (fPAE == h.fPAE)
2040 && (enmLongMode == h.enmLongMode)
2041 && (fTripleFaultReset == h.fTripleFaultReset)
2042 && (cCPUs == h.cCPUs)
2043 && (fCpuHotPlug == h.fCpuHotPlug)
2044 && (ulCpuExecutionCap == h.ulCpuExecutionCap)
2045 && (uCpuIdPortabilityLevel == h.uCpuIdPortabilityLevel)
2046 && (fHPETEnabled == h.fHPETEnabled)
2047 && (llCpus == h.llCpus)
2048 && (llCpuIdLeafs == h.llCpuIdLeafs)
2049 && (ulMemorySizeMB == h.ulMemorySizeMB)
2050 && (mapBootOrder == h.mapBootOrder)
2051 && (graphicsControllerType == h.graphicsControllerType)
2052 && (ulVRAMSizeMB == h.ulVRAMSizeMB)
2053 && (cMonitors == h.cMonitors)
2054 && (fAccelerate3D == h.fAccelerate3D)
2055 && (fAccelerate2DVideo == h.fAccelerate2DVideo)
2056 && (fVideoCaptureEnabled == h.fVideoCaptureEnabled)
2057 && (u64VideoCaptureScreens == h.u64VideoCaptureScreens)
2058 && (strVideoCaptureFile == h.strVideoCaptureFile)
2059 && (ulVideoCaptureHorzRes == h.ulVideoCaptureHorzRes)
2060 && (ulVideoCaptureVertRes == h.ulVideoCaptureVertRes)
2061 && (ulVideoCaptureRate == h.ulVideoCaptureRate)
2062 && (ulVideoCaptureFPS == h.ulVideoCaptureFPS)
2063 && (ulVideoCaptureMaxTime == h.ulVideoCaptureMaxTime)
2064 && (ulVideoCaptureMaxSize == h.ulVideoCaptureMaxTime)
2065 && (firmwareType == h.firmwareType)
2066 && (pointingHIDType == h.pointingHIDType)
2067 && (keyboardHIDType == h.keyboardHIDType)
2068 && (chipsetType == h.chipsetType)
2069 && (paravirtProvider == h.paravirtProvider)
2070 && (fEmulatedUSBCardReader == h.fEmulatedUSBCardReader)
2071 && (vrdeSettings == h.vrdeSettings)
2072 && (biosSettings == h.biosSettings)
2073 && (usbSettings == h.usbSettings)
2074 && (llNetworkAdapters == h.llNetworkAdapters)
2075 && (llSerialPorts == h.llSerialPorts)
2076 && (llParallelPorts == h.llParallelPorts)
2077 && (audioAdapter == h.audioAdapter)
2078 && (llSharedFolders == h.llSharedFolders)
2079 && (clipboardMode == h.clipboardMode)
2080 && (dndMode == h.dndMode)
2081 && (ulMemoryBalloonSize == h.ulMemoryBalloonSize)
2082 && (fPageFusionEnabled == h.fPageFusionEnabled)
2083 && (llGuestProperties == h.llGuestProperties)
2084 && (ioSettings == h.ioSettings)
2085 && (pciAttachments == h.pciAttachments)
2086 && (strDefaultFrontend == h.strDefaultFrontend)
2087 )
2088 );
2089}
2090
2091/**
2092 * Comparison operator. This gets called from MachineConfigFile::operator==,
2093 * which in turn gets called from Machine::saveSettings to figure out whether
2094 * machine settings have really changed and thus need to be written out to disk.
2095 */
2096bool AttachedDevice::operator==(const AttachedDevice &a) const
2097{
2098 return ( (this == &a)
2099 || ( (deviceType == a.deviceType)
2100 && (fPassThrough == a.fPassThrough)
2101 && (fTempEject == a.fTempEject)
2102 && (fNonRotational == a.fNonRotational)
2103 && (fDiscard == a.fDiscard)
2104 && (fHotPluggable == a.fHotPluggable)
2105 && (lPort == a.lPort)
2106 && (lDevice == a.lDevice)
2107 && (uuid == a.uuid)
2108 && (strHostDriveSrc == a.strHostDriveSrc)
2109 && (strBwGroup == a.strBwGroup)
2110 )
2111 );
2112}
2113
2114/**
2115 * Comparison operator. This gets called from MachineConfigFile::operator==,
2116 * which in turn gets called from Machine::saveSettings to figure out whether
2117 * machine settings have really changed and thus need to be written out to disk.
2118 */
2119bool StorageController::operator==(const StorageController &s) const
2120{
2121 return ( (this == &s)
2122 || ( (strName == s.strName)
2123 && (storageBus == s.storageBus)
2124 && (controllerType == s.controllerType)
2125 && (ulPortCount == s.ulPortCount)
2126 && (ulInstance == s.ulInstance)
2127 && (fUseHostIOCache == s.fUseHostIOCache)
2128 && (lIDE0MasterEmulationPort == s.lIDE0MasterEmulationPort)
2129 && (lIDE0SlaveEmulationPort == s.lIDE0SlaveEmulationPort)
2130 && (lIDE1MasterEmulationPort == s.lIDE1MasterEmulationPort)
2131 && (lIDE1SlaveEmulationPort == s.lIDE1SlaveEmulationPort)
2132 && (llAttachedDevices == s.llAttachedDevices)
2133 )
2134 );
2135}
2136
2137/**
2138 * Comparison operator. This gets called from MachineConfigFile::operator==,
2139 * which in turn gets called from Machine::saveSettings to figure out whether
2140 * machine settings have really changed and thus need to be written out to disk.
2141 */
2142bool Storage::operator==(const Storage &s) const
2143{
2144 return ( (this == &s)
2145 || (llStorageControllers == s.llStorageControllers) // deep compare
2146 );
2147}
2148
2149/**
2150 * Comparison operator. This gets called from MachineConfigFile::operator==,
2151 * which in turn gets called from Machine::saveSettings to figure out whether
2152 * machine settings have really changed and thus need to be written out to disk.
2153 */
2154bool Snapshot::operator==(const Snapshot &s) const
2155{
2156 return ( (this == &s)
2157 || ( (uuid == s.uuid)
2158 && (strName == s.strName)
2159 && (strDescription == s.strDescription)
2160 && (RTTimeSpecIsEqual(&timestamp, &s.timestamp))
2161 && (strStateFile == s.strStateFile)
2162 && (hardware == s.hardware) // deep compare
2163 && (storage == s.storage) // deep compare
2164 && (llChildSnapshots == s.llChildSnapshots) // deep compare
2165 && debugging == s.debugging
2166 && autostart == s.autostart
2167 )
2168 );
2169}
2170
2171/**
2172 * IOSettings constructor.
2173 */
2174IOSettings::IOSettings()
2175{
2176 fIOCacheEnabled = true;
2177 ulIOCacheSize = 5;
2178}
2179
2180////////////////////////////////////////////////////////////////////////////////
2181//
2182// MachineConfigFile
2183//
2184////////////////////////////////////////////////////////////////////////////////
2185
2186/**
2187 * Constructor.
2188 *
2189 * If pstrFilename is != NULL, this reads the given settings file into the member
2190 * variables and various substructures and lists. Otherwise, the member variables
2191 * are initialized with default values.
2192 *
2193 * Throws variants of xml::Error for I/O, XML and logical content errors, which
2194 * the caller should catch; if this constructor does not throw, then the member
2195 * variables contain meaningful values (either from the file or defaults).
2196 *
2197 * @param strFilename
2198 */
2199MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
2200 : ConfigFileBase(pstrFilename),
2201 fCurrentStateModified(true),
2202 fAborted(false)
2203{
2204 RTTimeNow(&timeLastStateChange);
2205
2206 if (pstrFilename)
2207 {
2208 // the ConfigFileBase constructor has loaded the XML file, so now
2209 // we need only analyze what is in there
2210
2211 xml::NodesLoop nlRootChildren(*m->pelmRoot);
2212 const xml::ElementNode *pelmRootChild;
2213 while ((pelmRootChild = nlRootChildren.forAllNodes()))
2214 {
2215 if (pelmRootChild->nameEquals("Machine"))
2216 readMachine(*pelmRootChild);
2217 }
2218
2219 // clean up memory allocated by XML engine
2220 clearDocument();
2221 }
2222}
2223
2224/**
2225 * Public routine which returns true if this machine config file can have its
2226 * own media registry (which is true for settings version v1.11 and higher,
2227 * i.e. files created by VirtualBox 4.0 and higher).
2228 * @return
2229 */
2230bool MachineConfigFile::canHaveOwnMediaRegistry() const
2231{
2232 return (m->sv >= SettingsVersion_v1_11);
2233}
2234
2235/**
2236 * Public routine which allows for importing machine XML from an external DOM tree.
2237 * Use this after having called the constructor with a NULL argument.
2238 *
2239 * This is used by the OVF code if a <vbox:Machine> element has been encountered
2240 * in an OVF VirtualSystem element.
2241 *
2242 * @param elmMachine
2243 */
2244void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine)
2245{
2246 readMachine(elmMachine);
2247}
2248
2249/**
2250 * Comparison operator. This gets called from Machine::saveSettings to figure out
2251 * whether machine settings have really changed and thus need to be written out to disk.
2252 *
2253 * Even though this is called operator==, this does NOT compare all fields; the "equals"
2254 * should be understood as "has the same machine config as". The following fields are
2255 * NOT compared:
2256 * -- settings versions and file names inherited from ConfigFileBase;
2257 * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
2258 *
2259 * The "deep" comparisons marked below will invoke the operator== functions of the
2260 * structs defined in this file, which may in turn go into comparing lists of
2261 * other structures. As a result, invoking this can be expensive, but it's
2262 * less expensive than writing out XML to disk.
2263 */
2264bool MachineConfigFile::operator==(const MachineConfigFile &c) const
2265{
2266 return ( (this == &c)
2267 || ( (uuid == c.uuid)
2268 && (machineUserData == c.machineUserData)
2269 && (strStateFile == c.strStateFile)
2270 && (uuidCurrentSnapshot == c.uuidCurrentSnapshot)
2271 // skip fCurrentStateModified!
2272 && (RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange))
2273 && (fAborted == c.fAborted)
2274 && (hardwareMachine == c.hardwareMachine) // this one's deep
2275 && (storageMachine == c.storageMachine) // this one's deep
2276 && (mediaRegistry == c.mediaRegistry) // this one's deep
2277 // skip mapExtraDataItems! there is no old state available as it's always forced
2278 && (llFirstSnapshot == c.llFirstSnapshot) // this one's deep
2279 )
2280 );
2281}
2282
2283/**
2284 * Called from MachineConfigFile::readHardware() to read cpu information.
2285 * @param elmCpuid
2286 * @param ll
2287 */
2288void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
2289 CpuList &ll)
2290{
2291 xml::NodesLoop nl1(elmCpu, "Cpu");
2292 const xml::ElementNode *pelmCpu;
2293 while ((pelmCpu = nl1.forAllNodes()))
2294 {
2295 Cpu cpu;
2296
2297 if (!pelmCpu->getAttributeValue("id", cpu.ulId))
2298 throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
2299
2300 ll.push_back(cpu);
2301 }
2302}
2303
2304/**
2305 * Called from MachineConfigFile::readHardware() to cpuid information.
2306 * @param elmCpuid
2307 * @param ll
2308 */
2309void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid,
2310 CpuIdLeafsList &ll)
2311{
2312 xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf");
2313 const xml::ElementNode *pelmCpuIdLeaf;
2314 while ((pelmCpuIdLeaf = nl1.forAllNodes()))
2315 {
2316 CpuIdLeaf leaf;
2317
2318 if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.ulId))
2319 throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing"));
2320
2321 pelmCpuIdLeaf->getAttributeValue("eax", leaf.ulEax);
2322 pelmCpuIdLeaf->getAttributeValue("ebx", leaf.ulEbx);
2323 pelmCpuIdLeaf->getAttributeValue("ecx", leaf.ulEcx);
2324 pelmCpuIdLeaf->getAttributeValue("edx", leaf.ulEdx);
2325
2326 ll.push_back(leaf);
2327 }
2328}
2329
2330/**
2331 * Called from MachineConfigFile::readHardware() to network information.
2332 * @param elmNetwork
2333 * @param ll
2334 */
2335void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
2336 NetworkAdaptersList &ll)
2337{
2338 xml::NodesLoop nl1(elmNetwork, "Adapter");
2339 const xml::ElementNode *pelmAdapter;
2340 while ((pelmAdapter = nl1.forAllNodes()))
2341 {
2342 NetworkAdapter nic;
2343
2344 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
2345 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
2346
2347 Utf8Str strTemp;
2348 if (pelmAdapter->getAttributeValue("type", strTemp))
2349 {
2350 if (strTemp == "Am79C970A")
2351 nic.type = NetworkAdapterType_Am79C970A;
2352 else if (strTemp == "Am79C973")
2353 nic.type = NetworkAdapterType_Am79C973;
2354 else if (strTemp == "82540EM")
2355 nic.type = NetworkAdapterType_I82540EM;
2356 else if (strTemp == "82543GC")
2357 nic.type = NetworkAdapterType_I82543GC;
2358 else if (strTemp == "82545EM")
2359 nic.type = NetworkAdapterType_I82545EM;
2360 else if (strTemp == "virtio")
2361 nic.type = NetworkAdapterType_Virtio;
2362 else
2363 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
2364 }
2365
2366 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
2367 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
2368 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
2369 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
2370
2371 if (pelmAdapter->getAttributeValue("promiscuousModePolicy", strTemp))
2372 {
2373 if (strTemp == "Deny")
2374 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
2375 else if (strTemp == "AllowNetwork")
2376 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
2377 else if (strTemp == "AllowAll")
2378 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
2379 else
2380 throw ConfigFileError(this, pelmAdapter,
2381 N_("Invalid value '%s' in Adapter/@promiscuousModePolicy attribute"), strTemp.c_str());
2382 }
2383
2384 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
2385 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
2386 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
2387 pelmAdapter->getAttributeValue("bandwidthGroup", nic.strBandwidthGroup);
2388
2389 xml::ElementNodesList llNetworkModes;
2390 pelmAdapter->getChildElements(llNetworkModes);
2391 xml::ElementNodesList::iterator it;
2392 /* We should have only active mode descriptor and disabled modes set */
2393 if (llNetworkModes.size() > 2)
2394 {
2395 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
2396 }
2397 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
2398 {
2399 const xml::ElementNode *pelmNode = *it;
2400 if (pelmNode->nameEquals("DisabledModes"))
2401 {
2402 xml::ElementNodesList llDisabledNetworkModes;
2403 xml::ElementNodesList::iterator itDisabled;
2404 pelmNode->getChildElements(llDisabledNetworkModes);
2405 /* run over disabled list and load settings */
2406 for (itDisabled = llDisabledNetworkModes.begin();
2407 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
2408 {
2409 const xml::ElementNode *pelmDisabledNode = *itDisabled;
2410 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
2411 }
2412 }
2413 else
2414 readAttachedNetworkMode(*pelmNode, true, nic);
2415 }
2416 // else: default is NetworkAttachmentType_Null
2417
2418 ll.push_back(nic);
2419 }
2420}
2421
2422void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
2423{
2424 NetworkAttachmentType_T enmAttachmentType = NetworkAttachmentType_Null;
2425
2426 if (elmMode.nameEquals("NAT"))
2427 {
2428 enmAttachmentType = NetworkAttachmentType_NAT;
2429
2430 elmMode.getAttributeValue("network", nic.nat.strNetwork);
2431 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
2432 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
2433 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
2434 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
2435 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
2436 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
2437 const xml::ElementNode *pelmDNS;
2438 if ((pelmDNS = elmMode.findChildElement("DNS")))
2439 {
2440 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDNSPassDomain);
2441 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDNSProxy);
2442 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDNSUseHostResolver);
2443 }
2444 const xml::ElementNode *pelmAlias;
2445 if ((pelmAlias = elmMode.findChildElement("Alias")))
2446 {
2447 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
2448 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
2449 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
2450 }
2451 const xml::ElementNode *pelmTFTP;
2452 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
2453 {
2454 pelmTFTP->getAttributeValue("prefix", nic.nat.strTFTPPrefix);
2455 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTFTPBootFile);
2456 pelmTFTP->getAttributeValue("next-server", nic.nat.strTFTPNextServer);
2457 }
2458
2459 readNATForwardRuleList(elmMode, nic.nat.llRules);
2460 }
2461 else if ( elmMode.nameEquals("HostInterface")
2462 || elmMode.nameEquals("BridgedInterface"))
2463 {
2464 enmAttachmentType = NetworkAttachmentType_Bridged;
2465
2466 elmMode.getAttributeValue("name", nic.strBridgedName); // optional bridged interface name
2467 }
2468 else if (elmMode.nameEquals("InternalNetwork"))
2469 {
2470 enmAttachmentType = NetworkAttachmentType_Internal;
2471
2472 if (!elmMode.getAttributeValue("name", nic.strInternalNetworkName)) // required network name
2473 throw ConfigFileError(this, &elmMode, N_("Required InternalNetwork/@name element is missing"));
2474 }
2475 else if (elmMode.nameEquals("HostOnlyInterface"))
2476 {
2477 enmAttachmentType = NetworkAttachmentType_HostOnly;
2478
2479 if (!elmMode.getAttributeValue("name", nic.strHostOnlyName)) // required network name
2480 throw ConfigFileError(this, &elmMode, N_("Required HostOnlyInterface/@name element is missing"));
2481 }
2482 else if (elmMode.nameEquals("GenericInterface"))
2483 {
2484 enmAttachmentType = NetworkAttachmentType_Generic;
2485
2486 elmMode.getAttributeValue("driver", nic.strGenericDriver); // optional network attachment driver
2487
2488 // get all properties
2489 xml::NodesLoop nl(elmMode);
2490 const xml::ElementNode *pelmModeChild;
2491 while ((pelmModeChild = nl.forAllNodes()))
2492 {
2493 if (pelmModeChild->nameEquals("Property"))
2494 {
2495 Utf8Str strPropName, strPropValue;
2496 if ( pelmModeChild->getAttributeValue("name", strPropName)
2497 && pelmModeChild->getAttributeValue("value", strPropValue) )
2498 nic.genericProperties[strPropName] = strPropValue;
2499 else
2500 throw ConfigFileError(this, pelmModeChild, N_("Required GenericInterface/Property/@name or @value attribute is missing"));
2501 }
2502 }
2503 }
2504 else if (elmMode.nameEquals("NATNetwork"))
2505 {
2506 enmAttachmentType = NetworkAttachmentType_NATNetwork;
2507
2508 if (!elmMode.getAttributeValue("name", nic.strNATNetworkName)) // required network name
2509 throw ConfigFileError(this, &elmMode, N_("Required NATNetwork/@name element is missing"));
2510 }
2511 else if (elmMode.nameEquals("VDE"))
2512 {
2513 enmAttachmentType = NetworkAttachmentType_Generic;
2514
2515 com::Utf8Str strVDEName;
2516 elmMode.getAttributeValue("network", strVDEName); // optional network name
2517 nic.strGenericDriver = "VDE";
2518 nic.genericProperties["network"] = strVDEName;
2519 }
2520
2521 if (fEnabled && enmAttachmentType != NetworkAttachmentType_Null)
2522 nic.mode = enmAttachmentType;
2523}
2524
2525/**
2526 * Called from MachineConfigFile::readHardware() to read serial port information.
2527 * @param elmUART
2528 * @param ll
2529 */
2530void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
2531 SerialPortsList &ll)
2532{
2533 xml::NodesLoop nl1(elmUART, "Port");
2534 const xml::ElementNode *pelmPort;
2535 while ((pelmPort = nl1.forAllNodes()))
2536 {
2537 SerialPort port;
2538 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
2539 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
2540
2541 // slot must be unique
2542 for (SerialPortsList::const_iterator it = ll.begin();
2543 it != ll.end();
2544 ++it)
2545 if ((*it).ulSlot == port.ulSlot)
2546 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
2547
2548 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
2549 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
2550 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
2551 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
2552 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
2553 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
2554
2555 Utf8Str strPortMode;
2556 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
2557 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
2558 if (strPortMode == "RawFile")
2559 port.portMode = PortMode_RawFile;
2560 else if (strPortMode == "HostPipe")
2561 port.portMode = PortMode_HostPipe;
2562 else if (strPortMode == "HostDevice")
2563 port.portMode = PortMode_HostDevice;
2564 else if (strPortMode == "Disconnected")
2565 port.portMode = PortMode_Disconnected;
2566 else if (strPortMode == "TCP")
2567 port.portMode = PortMode_TCP;
2568 else
2569 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
2570
2571 pelmPort->getAttributeValue("path", port.strPath);
2572 pelmPort->getAttributeValue("server", port.fServer);
2573
2574 ll.push_back(port);
2575 }
2576}
2577
2578/**
2579 * Called from MachineConfigFile::readHardware() to read parallel port information.
2580 * @param elmLPT
2581 * @param ll
2582 */
2583void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
2584 ParallelPortsList &ll)
2585{
2586 xml::NodesLoop nl1(elmLPT, "Port");
2587 const xml::ElementNode *pelmPort;
2588 while ((pelmPort = nl1.forAllNodes()))
2589 {
2590 ParallelPort port;
2591 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
2592 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
2593
2594 // slot must be unique
2595 for (ParallelPortsList::const_iterator it = ll.begin();
2596 it != ll.end();
2597 ++it)
2598 if ((*it).ulSlot == port.ulSlot)
2599 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
2600
2601 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
2602 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
2603 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
2604 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
2605 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
2606 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
2607
2608 pelmPort->getAttributeValue("path", port.strPath);
2609
2610 ll.push_back(port);
2611 }
2612}
2613
2614/**
2615 * Called from MachineConfigFile::readHardware() to read audio adapter information
2616 * and maybe fix driver information depending on the current host hardware.
2617 *
2618 * @param elmAudioAdapter "AudioAdapter" XML element.
2619 * @param hw
2620 */
2621void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
2622 AudioAdapter &aa)
2623{
2624 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
2625
2626 Utf8Str strTemp;
2627 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
2628 {
2629 if (strTemp == "SB16")
2630 aa.controllerType = AudioControllerType_SB16;
2631 else if (strTemp == "AC97")
2632 aa.controllerType = AudioControllerType_AC97;
2633 else if (strTemp == "HDA")
2634 aa.controllerType = AudioControllerType_HDA;
2635 else
2636 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
2637 }
2638
2639 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
2640 {
2641 // settings before 1.3 used lower case so make sure this is case-insensitive
2642 strTemp.toUpper();
2643 if (strTemp == "NULL")
2644 aa.driverType = AudioDriverType_Null;
2645 else if (strTemp == "WINMM")
2646 aa.driverType = AudioDriverType_WinMM;
2647 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
2648 aa.driverType = AudioDriverType_DirectSound;
2649 else if (strTemp == "SOLAUDIO")
2650 aa.driverType = AudioDriverType_SolAudio;
2651 else if (strTemp == "ALSA")
2652 aa.driverType = AudioDriverType_ALSA;
2653 else if (strTemp == "PULSE")
2654 aa.driverType = AudioDriverType_Pulse;
2655 else if (strTemp == "OSS")
2656 aa.driverType = AudioDriverType_OSS;
2657 else if (strTemp == "COREAUDIO")
2658 aa.driverType = AudioDriverType_CoreAudio;
2659 else if (strTemp == "MMPM")
2660 aa.driverType = AudioDriverType_MMPM;
2661 else
2662 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
2663
2664 // now check if this is actually supported on the current host platform;
2665 // people might be opening a file created on a Windows host, and that
2666 // VM should still start on a Linux host
2667 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
2668 aa.driverType = getHostDefaultAudioDriver();
2669 }
2670}
2671
2672/**
2673 * Called from MachineConfigFile::readHardware() to read guest property information.
2674 * @param elmGuestProperties
2675 * @param hw
2676 */
2677void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
2678 Hardware &hw)
2679{
2680 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
2681 const xml::ElementNode *pelmProp;
2682 while ((pelmProp = nl1.forAllNodes()))
2683 {
2684 GuestProperty prop;
2685 pelmProp->getAttributeValue("name", prop.strName);
2686 pelmProp->getAttributeValue("value", prop.strValue);
2687
2688 pelmProp->getAttributeValue("timestamp", prop.timestamp);
2689 pelmProp->getAttributeValue("flags", prop.strFlags);
2690 hw.llGuestProperties.push_back(prop);
2691 }
2692}
2693
2694/**
2695 * Helper function to read attributes that are common to <SATAController> (pre-1.7)
2696 * and <StorageController>.
2697 * @param elmStorageController
2698 * @param strg
2699 */
2700void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
2701 StorageController &sctl)
2702{
2703 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
2704 elmStorageController.getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
2705 elmStorageController.getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
2706 elmStorageController.getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
2707 elmStorageController.getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
2708
2709 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
2710}
2711
2712/**
2713 * Reads in a <Hardware> block and stores it in the given structure. Used
2714 * both directly from readMachine and from readSnapshot, since snapshots
2715 * have their own hardware sections.
2716 *
2717 * For legacy pre-1.7 settings we also need a storage structure because
2718 * the IDE and SATA controllers used to be defined under <Hardware>.
2719 *
2720 * @param elmHardware
2721 * @param hw
2722 */
2723void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
2724 Hardware &hw,
2725 Storage &strg)
2726{
2727 if (!elmHardware.getAttributeValue("version", hw.strVersion))
2728 {
2729 /* KLUDGE ALERT! For a while during the 3.1 development this was not
2730 written because it was thought to have a default value of "2". For
2731 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
2732 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
2733 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
2734 missing the hardware version, then it probably should be "2" instead
2735 of "1". */
2736 if (m->sv < SettingsVersion_v1_7)
2737 hw.strVersion = "1";
2738 else
2739 hw.strVersion = "2";
2740 }
2741 Utf8Str strUUID;
2742 if (elmHardware.getAttributeValue("uuid", strUUID))
2743 parseUUID(hw.uuid, strUUID);
2744
2745 xml::NodesLoop nl1(elmHardware);
2746 const xml::ElementNode *pelmHwChild;
2747 while ((pelmHwChild = nl1.forAllNodes()))
2748 {
2749 if (pelmHwChild->nameEquals("CPU"))
2750 {
2751 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
2752 {
2753 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
2754 const xml::ElementNode *pelmCPUChild;
2755 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
2756 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
2757 }
2758
2759 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
2760 pelmHwChild->getAttributeValue("executionCap", hw.ulCpuExecutionCap);
2761
2762 const xml::ElementNode *pelmCPUChild;
2763 if (hw.fCpuHotPlug)
2764 {
2765 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
2766 readCpuTree(*pelmCPUChild, hw.llCpus);
2767 }
2768
2769 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
2770 {
2771 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
2772 }
2773 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
2774 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
2775 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
2776 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
2777 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
2778 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
2779 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUX")))
2780 pelmCPUChild->getAttributeValue("enabled", hw.fUnrestrictedExecution);
2781 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtForce")))
2782 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirtForce);
2783
2784 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
2785 {
2786 /* The default for pre 3.1 was false, so we must respect that. */
2787 if (m->sv < SettingsVersion_v1_9)
2788 hw.fPAE = false;
2789 }
2790 else
2791 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
2792
2793 bool fLongMode;
2794 if ( (pelmCPUChild = pelmHwChild->findChildElement("LongMode"))
2795 && pelmCPUChild->getAttributeValue("enabled", fLongMode) )
2796 hw.enmLongMode = fLongMode ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled;
2797 else
2798 hw.enmLongMode = Hardware::LongMode_Legacy;
2799
2800 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
2801 {
2802 bool fSyntheticCpu = false;
2803 pelmCPUChild->getAttributeValue("enabled", fSyntheticCpu);
2804 hw.uCpuIdPortabilityLevel = fSyntheticCpu ? 1 : 0;
2805 }
2806 pelmHwChild->getAttributeValue("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
2807
2808 if ((pelmCPUChild = pelmHwChild->findChildElement("TripleFaultReset")))
2809 pelmCPUChild->getAttributeValue("enabled", hw.fTripleFaultReset);
2810
2811 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
2812 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
2813 }
2814 else if (pelmHwChild->nameEquals("Memory"))
2815 {
2816 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
2817 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
2818 }
2819 else if (pelmHwChild->nameEquals("Firmware"))
2820 {
2821 Utf8Str strFirmwareType;
2822 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
2823 {
2824 if ( (strFirmwareType == "BIOS")
2825 || (strFirmwareType == "1") // some trunk builds used the number here
2826 )
2827 hw.firmwareType = FirmwareType_BIOS;
2828 else if ( (strFirmwareType == "EFI")
2829 || (strFirmwareType == "2") // some trunk builds used the number here
2830 )
2831 hw.firmwareType = FirmwareType_EFI;
2832 else if ( strFirmwareType == "EFI32")
2833 hw.firmwareType = FirmwareType_EFI32;
2834 else if ( strFirmwareType == "EFI64")
2835 hw.firmwareType = FirmwareType_EFI64;
2836 else if ( strFirmwareType == "EFIDUAL")
2837 hw.firmwareType = FirmwareType_EFIDUAL;
2838 else
2839 throw ConfigFileError(this,
2840 pelmHwChild,
2841 N_("Invalid value '%s' in Firmware/@type"),
2842 strFirmwareType.c_str());
2843 }
2844 }
2845 else if (pelmHwChild->nameEquals("HID"))
2846 {
2847 Utf8Str strHIDType;
2848 if (pelmHwChild->getAttributeValue("Keyboard", strHIDType))
2849 {
2850 if (strHIDType == "None")
2851 hw.keyboardHIDType = KeyboardHIDType_None;
2852 else if (strHIDType == "USBKeyboard")
2853 hw.keyboardHIDType = KeyboardHIDType_USBKeyboard;
2854 else if (strHIDType == "PS2Keyboard")
2855 hw.keyboardHIDType = KeyboardHIDType_PS2Keyboard;
2856 else if (strHIDType == "ComboKeyboard")
2857 hw.keyboardHIDType = KeyboardHIDType_ComboKeyboard;
2858 else
2859 throw ConfigFileError(this,
2860 pelmHwChild,
2861 N_("Invalid value '%s' in HID/Keyboard/@type"),
2862 strHIDType.c_str());
2863 }
2864 if (pelmHwChild->getAttributeValue("Pointing", strHIDType))
2865 {
2866 if (strHIDType == "None")
2867 hw.pointingHIDType = PointingHIDType_None;
2868 else if (strHIDType == "USBMouse")
2869 hw.pointingHIDType = PointingHIDType_USBMouse;
2870 else if (strHIDType == "USBTablet")
2871 hw.pointingHIDType = PointingHIDType_USBTablet;
2872 else if (strHIDType == "PS2Mouse")
2873 hw.pointingHIDType = PointingHIDType_PS2Mouse;
2874 else if (strHIDType == "ComboMouse")
2875 hw.pointingHIDType = PointingHIDType_ComboMouse;
2876 else if (strHIDType == "USBMultiTouch")
2877 hw.pointingHIDType = PointingHIDType_USBMultiTouch;
2878 else
2879 throw ConfigFileError(this,
2880 pelmHwChild,
2881 N_("Invalid value '%s' in HID/Pointing/@type"),
2882 strHIDType.c_str());
2883 }
2884 }
2885 else if (pelmHwChild->nameEquals("Chipset"))
2886 {
2887 Utf8Str strChipsetType;
2888 if (pelmHwChild->getAttributeValue("type", strChipsetType))
2889 {
2890 if (strChipsetType == "PIIX3")
2891 hw.chipsetType = ChipsetType_PIIX3;
2892 else if (strChipsetType == "ICH9")
2893 hw.chipsetType = ChipsetType_ICH9;
2894 else
2895 throw ConfigFileError(this,
2896 pelmHwChild,
2897 N_("Invalid value '%s' in Chipset/@type"),
2898 strChipsetType.c_str());
2899 }
2900 }
2901 else if (pelmHwChild->nameEquals("Paravirt"))
2902 {
2903 Utf8Str strProvider;
2904 if (pelmHwChild->getAttributeValue("provider", strProvider))
2905 {
2906 if (strProvider == "None")
2907 hw.paravirtProvider = ParavirtProvider_None;
2908 else if (strProvider == "Default")
2909 hw.paravirtProvider = ParavirtProvider_Default;
2910 else if (strProvider == "Legacy")
2911 hw.paravirtProvider = ParavirtProvider_Legacy;
2912 else if (strProvider == "Minimal")
2913 hw.paravirtProvider = ParavirtProvider_Minimal;
2914 else if (strProvider == "HyperV")
2915 hw.paravirtProvider = ParavirtProvider_HyperV;
2916 else if (strProvider == "KVM")
2917 hw.paravirtProvider = ParavirtProvider_KVM;
2918 else
2919 throw ConfigFileError(this,
2920 pelmHwChild,
2921 N_("Invalid value '%s' in Paravirt/@provider attribute"),
2922 strProvider.c_str());
2923 }
2924 }
2925 else if (pelmHwChild->nameEquals("HPET"))
2926 {
2927 pelmHwChild->getAttributeValue("enabled", hw.fHPETEnabled);
2928 }
2929 else if (pelmHwChild->nameEquals("Boot"))
2930 {
2931 hw.mapBootOrder.clear();
2932
2933 xml::NodesLoop nl2(*pelmHwChild, "Order");
2934 const xml::ElementNode *pelmOrder;
2935 while ((pelmOrder = nl2.forAllNodes()))
2936 {
2937 uint32_t ulPos;
2938 Utf8Str strDevice;
2939 if (!pelmOrder->getAttributeValue("position", ulPos))
2940 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
2941
2942 if ( ulPos < 1
2943 || ulPos > SchemaDefs::MaxBootPosition
2944 )
2945 throw ConfigFileError(this,
2946 pelmOrder,
2947 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
2948 ulPos,
2949 SchemaDefs::MaxBootPosition + 1);
2950 // XML is 1-based but internal data is 0-based
2951 --ulPos;
2952
2953 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
2954 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
2955
2956 if (!pelmOrder->getAttributeValue("device", strDevice))
2957 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
2958
2959 DeviceType_T type;
2960 if (strDevice == "None")
2961 type = DeviceType_Null;
2962 else if (strDevice == "Floppy")
2963 type = DeviceType_Floppy;
2964 else if (strDevice == "DVD")
2965 type = DeviceType_DVD;
2966 else if (strDevice == "HardDisk")
2967 type = DeviceType_HardDisk;
2968 else if (strDevice == "Network")
2969 type = DeviceType_Network;
2970 else
2971 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
2972 hw.mapBootOrder[ulPos] = type;
2973 }
2974 }
2975 else if (pelmHwChild->nameEquals("Display"))
2976 {
2977 Utf8Str strGraphicsControllerType;
2978 if (!pelmHwChild->getAttributeValue("controller", strGraphicsControllerType))
2979 hw.graphicsControllerType = GraphicsControllerType_VBoxVGA;
2980 else
2981 {
2982 strGraphicsControllerType.toUpper();
2983 GraphicsControllerType_T type;
2984 if (strGraphicsControllerType == "VBOXVGA")
2985 type = GraphicsControllerType_VBoxVGA;
2986 else if (strGraphicsControllerType == "VMSVGA")
2987 type = GraphicsControllerType_VMSVGA;
2988 else if (strGraphicsControllerType == "NONE")
2989 type = GraphicsControllerType_Null;
2990 else
2991 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Display/@controller attribute"), strGraphicsControllerType.c_str());
2992 hw.graphicsControllerType = type;
2993 }
2994 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
2995 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
2996 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
2997 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
2998 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
2999 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
3000 }
3001 else if (pelmHwChild->nameEquals("VideoCapture"))
3002 {
3003 pelmHwChild->getAttributeValue("enabled", hw.fVideoCaptureEnabled);
3004 pelmHwChild->getAttributeValue("screens", hw.u64VideoCaptureScreens);
3005 pelmHwChild->getAttributeValuePath("file", hw.strVideoCaptureFile);
3006 pelmHwChild->getAttributeValue("horzRes", hw.ulVideoCaptureHorzRes);
3007 pelmHwChild->getAttributeValue("vertRes", hw.ulVideoCaptureVertRes);
3008 pelmHwChild->getAttributeValue("rate", hw.ulVideoCaptureRate);
3009 pelmHwChild->getAttributeValue("fps", hw.ulVideoCaptureFPS);
3010 pelmHwChild->getAttributeValue("maxTime", hw.ulVideoCaptureMaxTime);
3011 pelmHwChild->getAttributeValue("maxSize", hw.ulVideoCaptureMaxSize);
3012 }
3013 else if (pelmHwChild->nameEquals("RemoteDisplay"))
3014 {
3015 pelmHwChild->getAttributeValue("enabled", hw.vrdeSettings.fEnabled);
3016
3017 Utf8Str str;
3018 if (pelmHwChild->getAttributeValue("port", str))
3019 hw.vrdeSettings.mapProperties["TCP/Ports"] = str;
3020 if (pelmHwChild->getAttributeValue("netAddress", str))
3021 hw.vrdeSettings.mapProperties["TCP/Address"] = str;
3022
3023 Utf8Str strAuthType;
3024 if (pelmHwChild->getAttributeValue("authType", strAuthType))
3025 {
3026 // settings before 1.3 used lower case so make sure this is case-insensitive
3027 strAuthType.toUpper();
3028 if (strAuthType == "NULL")
3029 hw.vrdeSettings.authType = AuthType_Null;
3030 else if (strAuthType == "GUEST")
3031 hw.vrdeSettings.authType = AuthType_Guest;
3032 else if (strAuthType == "EXTERNAL")
3033 hw.vrdeSettings.authType = AuthType_External;
3034 else
3035 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
3036 }
3037
3038 pelmHwChild->getAttributeValue("authLibrary", hw.vrdeSettings.strAuthLibrary);
3039 pelmHwChild->getAttributeValue("authTimeout", hw.vrdeSettings.ulAuthTimeout);
3040 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
3041 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
3042
3043 /* 3.2 and 4.0 betas, 4.0 has this information in VRDEProperties. */
3044 const xml::ElementNode *pelmVideoChannel;
3045 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
3046 {
3047 bool fVideoChannel = false;
3048 pelmVideoChannel->getAttributeValue("enabled", fVideoChannel);
3049 hw.vrdeSettings.mapProperties["VideoChannel/Enabled"] = fVideoChannel? "true": "false";
3050
3051 uint32_t ulVideoChannelQuality = 75;
3052 pelmVideoChannel->getAttributeValue("quality", ulVideoChannelQuality);
3053 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
3054 char *pszBuffer = NULL;
3055 if (RTStrAPrintf(&pszBuffer, "%d", ulVideoChannelQuality) >= 0)
3056 {
3057 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = pszBuffer;
3058 RTStrFree(pszBuffer);
3059 }
3060 else
3061 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = "75";
3062 }
3063 pelmHwChild->getAttributeValue("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
3064
3065 const xml::ElementNode *pelmProperties = pelmHwChild->findChildElement("VRDEProperties");
3066 if (pelmProperties != NULL)
3067 {
3068 xml::NodesLoop nl(*pelmProperties);
3069 const xml::ElementNode *pelmProperty;
3070 while ((pelmProperty = nl.forAllNodes()))
3071 {
3072 if (pelmProperty->nameEquals("Property"))
3073 {
3074 /* <Property name="TCP/Ports" value="3000-3002"/> */
3075 Utf8Str strName, strValue;
3076 if ( pelmProperty->getAttributeValue("name", strName)
3077 && pelmProperty->getAttributeValue("value", strValue))
3078 hw.vrdeSettings.mapProperties[strName] = strValue;
3079 else
3080 throw ConfigFileError(this, pelmProperty, N_("Required VRDE Property/@name or @value attribute is missing"));
3081 }
3082 }
3083 }
3084 }
3085 else if (pelmHwChild->nameEquals("BIOS"))
3086 {
3087 const xml::ElementNode *pelmBIOSChild;
3088 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
3089 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
3090 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
3091 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
3092 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
3093 {
3094 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
3095 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
3096 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
3097 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
3098 }
3099 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
3100 {
3101 Utf8Str strBootMenuMode;
3102 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
3103 {
3104 // settings before 1.3 used lower case so make sure this is case-insensitive
3105 strBootMenuMode.toUpper();
3106 if (strBootMenuMode == "DISABLED")
3107 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
3108 else if (strBootMenuMode == "MENUONLY")
3109 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
3110 else if (strBootMenuMode == "MESSAGEANDMENU")
3111 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
3112 else
3113 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
3114 }
3115 }
3116 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
3117 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
3118 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
3119 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
3120
3121 // legacy BIOS/IDEController (pre 1.7)
3122 if ( (m->sv < SettingsVersion_v1_7)
3123 && (pelmBIOSChild = pelmHwChild->findChildElement("IDEController"))
3124 )
3125 {
3126 StorageController sctl;
3127 sctl.strName = "IDE Controller";
3128 sctl.storageBus = StorageBus_IDE;
3129
3130 Utf8Str strType;
3131 if (pelmBIOSChild->getAttributeValue("type", strType))
3132 {
3133 if (strType == "PIIX3")
3134 sctl.controllerType = StorageControllerType_PIIX3;
3135 else if (strType == "PIIX4")
3136 sctl.controllerType = StorageControllerType_PIIX4;
3137 else if (strType == "ICH6")
3138 sctl.controllerType = StorageControllerType_ICH6;
3139 else
3140 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
3141 }
3142 sctl.ulPortCount = 2;
3143 strg.llStorageControllers.push_back(sctl);
3144 }
3145 }
3146 else if ( (m->sv <= SettingsVersion_v1_14)
3147 && pelmHwChild->nameEquals("USBController"))
3148 {
3149 bool fEnabled = false;
3150
3151 pelmHwChild->getAttributeValue("enabled", fEnabled);
3152 if (fEnabled)
3153 {
3154 /* Create OHCI controller with default name. */
3155 USBController ctrl;
3156
3157 ctrl.strName = "OHCI";
3158 ctrl.enmType = USBControllerType_OHCI;
3159 hw.usbSettings.llUSBControllers.push_back(ctrl);
3160 }
3161
3162 pelmHwChild->getAttributeValue("enabledEhci", fEnabled);
3163 if (fEnabled)
3164 {
3165 /* Create OHCI controller with default name. */
3166 USBController ctrl;
3167
3168 ctrl.strName = "EHCI";
3169 ctrl.enmType = USBControllerType_EHCI;
3170 hw.usbSettings.llUSBControllers.push_back(ctrl);
3171 }
3172
3173 readUSBDeviceFilters(*pelmHwChild,
3174 hw.usbSettings.llDeviceFilters);
3175 }
3176 else if (pelmHwChild->nameEquals("USB"))
3177 {
3178 const xml::ElementNode *pelmUSBChild;
3179
3180 if ((pelmUSBChild = pelmHwChild->findChildElement("Controllers")))
3181 {
3182 xml::NodesLoop nl2(*pelmUSBChild, "Controller");
3183 const xml::ElementNode *pelmCtrl;
3184
3185 while ((pelmCtrl = nl2.forAllNodes()))
3186 {
3187 USBController ctrl;
3188 com::Utf8Str strCtrlType;
3189
3190 pelmCtrl->getAttributeValue("name", ctrl.strName);
3191
3192 if (pelmCtrl->getAttributeValue("type", strCtrlType))
3193 {
3194 if (strCtrlType == "OHCI")
3195 ctrl.enmType = USBControllerType_OHCI;
3196 else if (strCtrlType == "EHCI")
3197 ctrl.enmType = USBControllerType_EHCI;
3198 else if (strCtrlType == "XHCI")
3199 ctrl.enmType = USBControllerType_XHCI;
3200 else
3201 throw ConfigFileError(this, pelmCtrl, N_("Invalid value '%s' for Controller/@type attribute"), strCtrlType.c_str());
3202 }
3203
3204 hw.usbSettings.llUSBControllers.push_back(ctrl);
3205 }
3206 }
3207
3208 if ((pelmUSBChild = pelmHwChild->findChildElement("DeviceFilters")))
3209 readUSBDeviceFilters(*pelmUSBChild, hw.usbSettings.llDeviceFilters);
3210 }
3211 else if ( m->sv < SettingsVersion_v1_7
3212 && pelmHwChild->nameEquals("SATAController"))
3213 {
3214 bool f;
3215 if ( pelmHwChild->getAttributeValue("enabled", f)
3216 && f)
3217 {
3218 StorageController sctl;
3219 sctl.strName = "SATA Controller";
3220 sctl.storageBus = StorageBus_SATA;
3221 sctl.controllerType = StorageControllerType_IntelAhci;
3222
3223 readStorageControllerAttributes(*pelmHwChild, sctl);
3224
3225 strg.llStorageControllers.push_back(sctl);
3226 }
3227 }
3228 else if (pelmHwChild->nameEquals("Network"))
3229 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
3230 else if (pelmHwChild->nameEquals("RTC"))
3231 {
3232 Utf8Str strLocalOrUTC;
3233 machineUserData.fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
3234 && strLocalOrUTC == "UTC";
3235 }
3236 else if ( pelmHwChild->nameEquals("UART")
3237 || pelmHwChild->nameEquals("Uart") // used before 1.3
3238 )
3239 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
3240 else if ( pelmHwChild->nameEquals("LPT")
3241 || pelmHwChild->nameEquals("Lpt") // used before 1.3
3242 )
3243 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
3244 else if (pelmHwChild->nameEquals("AudioAdapter"))
3245 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
3246 else if (pelmHwChild->nameEquals("SharedFolders"))
3247 {
3248 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
3249 const xml::ElementNode *pelmFolder;
3250 while ((pelmFolder = nl2.forAllNodes()))
3251 {
3252 SharedFolder sf;
3253 pelmFolder->getAttributeValue("name", sf.strName);
3254 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
3255 pelmFolder->getAttributeValue("writable", sf.fWritable);
3256 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
3257 hw.llSharedFolders.push_back(sf);
3258 }
3259 }
3260 else if (pelmHwChild->nameEquals("Clipboard"))
3261 {
3262 Utf8Str strTemp;
3263 if (pelmHwChild->getAttributeValue("mode", strTemp))
3264 {
3265 if (strTemp == "Disabled")
3266 hw.clipboardMode = ClipboardMode_Disabled;
3267 else if (strTemp == "HostToGuest")
3268 hw.clipboardMode = ClipboardMode_HostToGuest;
3269 else if (strTemp == "GuestToHost")
3270 hw.clipboardMode = ClipboardMode_GuestToHost;
3271 else if (strTemp == "Bidirectional")
3272 hw.clipboardMode = ClipboardMode_Bidirectional;
3273 else
3274 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
3275 }
3276 }
3277 else if (pelmHwChild->nameEquals("DragAndDrop"))
3278 {
3279 Utf8Str strTemp;
3280 if (pelmHwChild->getAttributeValue("mode", strTemp))
3281 {
3282 if (strTemp == "Disabled")
3283 hw.dndMode = DnDMode_Disabled;
3284 else if (strTemp == "HostToGuest")
3285 hw.dndMode = DnDMode_HostToGuest;
3286 else if (strTemp == "GuestToHost")
3287 hw.dndMode = DnDMode_GuestToHost;
3288 else if (strTemp == "Bidirectional")
3289 hw.dndMode = DnDMode_Bidirectional;
3290 else
3291 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in DragAndDrop/@mode attribute"), strTemp.c_str());
3292 }
3293 }
3294 else if (pelmHwChild->nameEquals("Guest"))
3295 {
3296 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
3297 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
3298 }
3299 else if (pelmHwChild->nameEquals("GuestProperties"))
3300 readGuestProperties(*pelmHwChild, hw);
3301 else if (pelmHwChild->nameEquals("IO"))
3302 {
3303 const xml::ElementNode *pelmBwGroups;
3304 const xml::ElementNode *pelmIOChild;
3305
3306 if ((pelmIOChild = pelmHwChild->findChildElement("IoCache")))
3307 {
3308 pelmIOChild->getAttributeValue("enabled", hw.ioSettings.fIOCacheEnabled);
3309 pelmIOChild->getAttributeValue("size", hw.ioSettings.ulIOCacheSize);
3310 }
3311
3312 if ((pelmBwGroups = pelmHwChild->findChildElement("BandwidthGroups")))
3313 {
3314 xml::NodesLoop nl2(*pelmBwGroups, "BandwidthGroup");
3315 const xml::ElementNode *pelmBandwidthGroup;
3316 while ((pelmBandwidthGroup = nl2.forAllNodes()))
3317 {
3318 BandwidthGroup gr;
3319 Utf8Str strTemp;
3320
3321 pelmBandwidthGroup->getAttributeValue("name", gr.strName);
3322
3323 if (pelmBandwidthGroup->getAttributeValue("type", strTemp))
3324 {
3325 if (strTemp == "Disk")
3326 gr.enmType = BandwidthGroupType_Disk;
3327 else if (strTemp == "Network")
3328 gr.enmType = BandwidthGroupType_Network;
3329 else
3330 throw ConfigFileError(this, pelmBandwidthGroup, N_("Invalid value '%s' in BandwidthGroup/@type attribute"), strTemp.c_str());
3331 }
3332 else
3333 throw ConfigFileError(this, pelmBandwidthGroup, N_("Missing BandwidthGroup/@type attribute"));
3334
3335 if (!pelmBandwidthGroup->getAttributeValue("maxBytesPerSec", gr.cMaxBytesPerSec))
3336 {
3337 pelmBandwidthGroup->getAttributeValue("maxMbPerSec", gr.cMaxBytesPerSec);
3338 gr.cMaxBytesPerSec *= _1M;
3339 }
3340 hw.ioSettings.llBandwidthGroups.push_back(gr);
3341 }
3342 }
3343 }
3344 else if (pelmHwChild->nameEquals("HostPci"))
3345 {
3346 const xml::ElementNode *pelmDevices;
3347
3348 if ((pelmDevices = pelmHwChild->findChildElement("Devices")))
3349 {
3350 xml::NodesLoop nl2(*pelmDevices, "Device");
3351 const xml::ElementNode *pelmDevice;
3352 while ((pelmDevice = nl2.forAllNodes()))
3353 {
3354 HostPCIDeviceAttachment hpda;
3355
3356 if (!pelmDevice->getAttributeValue("host", hpda.uHostAddress))
3357 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@host attribute"));
3358
3359 if (!pelmDevice->getAttributeValue("guest", hpda.uGuestAddress))
3360 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@guest attribute"));
3361
3362 /* name is optional */
3363 pelmDevice->getAttributeValue("name", hpda.strDeviceName);
3364
3365 hw.pciAttachments.push_back(hpda);
3366 }
3367 }
3368 }
3369 else if (pelmHwChild->nameEquals("EmulatedUSB"))
3370 {
3371 const xml::ElementNode *pelmCardReader;
3372
3373 if ((pelmCardReader = pelmHwChild->findChildElement("CardReader")))
3374 {
3375 pelmCardReader->getAttributeValue("enabled", hw.fEmulatedUSBCardReader);
3376 }
3377 }
3378 else if (pelmHwChild->nameEquals("Frontend"))
3379 {
3380 const xml::ElementNode *pelmDefault;
3381
3382 if ((pelmDefault = pelmHwChild->findChildElement("Default")))
3383 {
3384 pelmDefault->getAttributeValue("type", hw.strDefaultFrontend);
3385 }
3386 }
3387 }
3388
3389 if (hw.ulMemorySizeMB == (uint32_t)-1)
3390 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
3391}
3392
3393/**
3394 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
3395 * files which have a <HardDiskAttachments> node and storage controller settings
3396 * hidden in the <Hardware> settings. We set the StorageControllers fields just the
3397 * same, just from different sources.
3398 * @param elmHardware <Hardware> XML node.
3399 * @param elmHardDiskAttachments <HardDiskAttachments> XML node.
3400 * @param strg
3401 */
3402void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
3403 Storage &strg)
3404{
3405 StorageController *pIDEController = NULL;
3406 StorageController *pSATAController = NULL;
3407
3408 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
3409 it != strg.llStorageControllers.end();
3410 ++it)
3411 {
3412 StorageController &s = *it;
3413 if (s.storageBus == StorageBus_IDE)
3414 pIDEController = &s;
3415 else if (s.storageBus == StorageBus_SATA)
3416 pSATAController = &s;
3417 }
3418
3419 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
3420 const xml::ElementNode *pelmAttachment;
3421 while ((pelmAttachment = nl1.forAllNodes()))
3422 {
3423 AttachedDevice att;
3424 Utf8Str strUUID, strBus;
3425
3426 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
3427 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
3428 parseUUID(att.uuid, strUUID);
3429
3430 if (!pelmAttachment->getAttributeValue("bus", strBus))
3431 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
3432 // pre-1.7 'channel' is now port
3433 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
3434 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
3435 // pre-1.7 'device' is still device
3436 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
3437 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
3438
3439 att.deviceType = DeviceType_HardDisk;
3440
3441 if (strBus == "IDE")
3442 {
3443 if (!pIDEController)
3444 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
3445 pIDEController->llAttachedDevices.push_back(att);
3446 }
3447 else if (strBus == "SATA")
3448 {
3449 if (!pSATAController)
3450 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
3451 pSATAController->llAttachedDevices.push_back(att);
3452 }
3453 else
3454 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
3455 }
3456}
3457
3458/**
3459 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
3460 * Used both directly from readMachine and from readSnapshot, since snapshots
3461 * have their own storage controllers sections.
3462 *
3463 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
3464 * for earlier versions.
3465 *
3466 * @param elmStorageControllers
3467 */
3468void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
3469 Storage &strg)
3470{
3471 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
3472 const xml::ElementNode *pelmController;
3473 while ((pelmController = nlStorageControllers.forAllNodes()))
3474 {
3475 StorageController sctl;
3476
3477 if (!pelmController->getAttributeValue("name", sctl.strName))
3478 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
3479 // canonicalize storage controller names for configs in the switchover
3480 // period.
3481 if (m->sv < SettingsVersion_v1_9)
3482 {
3483 if (sctl.strName == "IDE")
3484 sctl.strName = "IDE Controller";
3485 else if (sctl.strName == "SATA")
3486 sctl.strName = "SATA Controller";
3487 else if (sctl.strName == "SCSI")
3488 sctl.strName = "SCSI Controller";
3489 }
3490
3491 pelmController->getAttributeValue("Instance", sctl.ulInstance);
3492 // default from constructor is 0
3493
3494 pelmController->getAttributeValue("Bootable", sctl.fBootable);
3495 // default from constructor is true which is true
3496 // for settings below version 1.11 because they allowed only
3497 // one controller per type.
3498
3499 Utf8Str strType;
3500 if (!pelmController->getAttributeValue("type", strType))
3501 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
3502
3503 if (strType == "AHCI")
3504 {
3505 sctl.storageBus = StorageBus_SATA;
3506 sctl.controllerType = StorageControllerType_IntelAhci;
3507 }
3508 else if (strType == "LsiLogic")
3509 {
3510 sctl.storageBus = StorageBus_SCSI;
3511 sctl.controllerType = StorageControllerType_LsiLogic;
3512 }
3513 else if (strType == "BusLogic")
3514 {
3515 sctl.storageBus = StorageBus_SCSI;
3516 sctl.controllerType = StorageControllerType_BusLogic;
3517 }
3518 else if (strType == "PIIX3")
3519 {
3520 sctl.storageBus = StorageBus_IDE;
3521 sctl.controllerType = StorageControllerType_PIIX3;
3522 }
3523 else if (strType == "PIIX4")
3524 {
3525 sctl.storageBus = StorageBus_IDE;
3526 sctl.controllerType = StorageControllerType_PIIX4;
3527 }
3528 else if (strType == "ICH6")
3529 {
3530 sctl.storageBus = StorageBus_IDE;
3531 sctl.controllerType = StorageControllerType_ICH6;
3532 }
3533 else if ( (m->sv >= SettingsVersion_v1_9)
3534 && (strType == "I82078")
3535 )
3536 {
3537 sctl.storageBus = StorageBus_Floppy;
3538 sctl.controllerType = StorageControllerType_I82078;
3539 }
3540 else if (strType == "LsiLogicSas")
3541 {
3542 sctl.storageBus = StorageBus_SAS;
3543 sctl.controllerType = StorageControllerType_LsiLogicSas;
3544 }
3545 else if (strType == "USB")
3546 {
3547 sctl.storageBus = StorageBus_USB;
3548 sctl.controllerType = StorageControllerType_USB;
3549 }
3550 else
3551 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
3552
3553 readStorageControllerAttributes(*pelmController, sctl);
3554
3555 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
3556 const xml::ElementNode *pelmAttached;
3557 while ((pelmAttached = nlAttached.forAllNodes()))
3558 {
3559 AttachedDevice att;
3560 Utf8Str strTemp;
3561 pelmAttached->getAttributeValue("type", strTemp);
3562
3563 att.fDiscard = false;
3564 att.fNonRotational = false;
3565 att.fHotPluggable = false;
3566
3567 if (strTemp == "HardDisk")
3568 {
3569 att.deviceType = DeviceType_HardDisk;
3570 pelmAttached->getAttributeValue("nonrotational", att.fNonRotational);
3571 pelmAttached->getAttributeValue("discard", att.fDiscard);
3572 }
3573 else if (m->sv >= SettingsVersion_v1_9)
3574 {
3575 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
3576 if (strTemp == "DVD")
3577 {
3578 att.deviceType = DeviceType_DVD;
3579 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
3580 pelmAttached->getAttributeValue("tempeject", att.fTempEject);
3581 }
3582 else if (strTemp == "Floppy")
3583 att.deviceType = DeviceType_Floppy;
3584 }
3585
3586 if (att.deviceType != DeviceType_Null)
3587 {
3588 const xml::ElementNode *pelmImage;
3589 // all types can have images attached, but for HardDisk it's required
3590 if (!(pelmImage = pelmAttached->findChildElement("Image")))
3591 {
3592 if (att.deviceType == DeviceType_HardDisk)
3593 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
3594 else
3595 {
3596 // DVDs and floppies can also have <HostDrive> instead of <Image>
3597 const xml::ElementNode *pelmHostDrive;
3598 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
3599 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
3600 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
3601 }
3602 }
3603 else
3604 {
3605 if (!pelmImage->getAttributeValue("uuid", strTemp))
3606 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
3607 parseUUID(att.uuid, strTemp);
3608 }
3609
3610 if (!pelmAttached->getAttributeValue("port", att.lPort))
3611 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
3612 if (!pelmAttached->getAttributeValue("device", att.lDevice))
3613 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
3614
3615 /* AHCI controller ports are hotpluggable by default, keep compatibility with existing settings. */
3616 if (m->sv >= SettingsVersion_v1_15)
3617 pelmAttached->getAttributeValue("hotpluggable", att.fHotPluggable);
3618 else if (sctl.controllerType == StorageControllerType_IntelAhci)
3619 att.fHotPluggable = true;
3620
3621 pelmAttached->getAttributeValue("bandwidthGroup", att.strBwGroup);
3622 sctl.llAttachedDevices.push_back(att);
3623 }
3624 }
3625
3626 strg.llStorageControllers.push_back(sctl);
3627 }
3628}
3629
3630/**
3631 * This gets called for legacy pre-1.9 settings files after having parsed the
3632 * <Hardware> and <StorageControllers> sections to parse <Hardware> once more
3633 * for the <DVDDrive> and <FloppyDrive> sections.
3634 *
3635 * Before settings version 1.9, DVD and floppy drives were specified separately
3636 * under <Hardware>; we then need this extra loop to make sure the storage
3637 * controller structs are already set up so we can add stuff to them.
3638 *
3639 * @param elmHardware
3640 * @param strg
3641 */
3642void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
3643 Storage &strg)
3644{
3645 xml::NodesLoop nl1(elmHardware);
3646 const xml::ElementNode *pelmHwChild;
3647 while ((pelmHwChild = nl1.forAllNodes()))
3648 {
3649 if (pelmHwChild->nameEquals("DVDDrive"))
3650 {
3651 // create a DVD "attached device" and attach it to the existing IDE controller
3652 AttachedDevice att;
3653 att.deviceType = DeviceType_DVD;
3654 // legacy DVD drive is always secondary master (port 1, device 0)
3655 att.lPort = 1;
3656 att.lDevice = 0;
3657 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
3658 pelmHwChild->getAttributeValue("tempeject", att.fTempEject);
3659
3660 const xml::ElementNode *pDriveChild;
3661 Utf8Str strTmp;
3662 if ( (pDriveChild = pelmHwChild->findChildElement("Image")) != NULL
3663 && pDriveChild->getAttributeValue("uuid", strTmp))
3664 parseUUID(att.uuid, strTmp);
3665 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
3666 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
3667
3668 // find the IDE controller and attach the DVD drive
3669 bool fFound = false;
3670 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
3671 it != strg.llStorageControllers.end();
3672 ++it)
3673 {
3674 StorageController &sctl = *it;
3675 if (sctl.storageBus == StorageBus_IDE)
3676 {
3677 sctl.llAttachedDevices.push_back(att);
3678 fFound = true;
3679 break;
3680 }
3681 }
3682
3683 if (!fFound)
3684 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
3685 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
3686 // which should have gotten parsed in <StorageControllers> before this got called
3687 }
3688 else if (pelmHwChild->nameEquals("FloppyDrive"))
3689 {
3690 bool fEnabled;
3691 if ( pelmHwChild->getAttributeValue("enabled", fEnabled)
3692 && fEnabled)
3693 {
3694 // create a new floppy controller and attach a floppy "attached device"
3695 StorageController sctl;
3696 sctl.strName = "Floppy Controller";
3697 sctl.storageBus = StorageBus_Floppy;
3698 sctl.controllerType = StorageControllerType_I82078;
3699 sctl.ulPortCount = 1;
3700
3701 AttachedDevice att;
3702 att.deviceType = DeviceType_Floppy;
3703 att.lPort = 0;
3704 att.lDevice = 0;
3705
3706 const xml::ElementNode *pDriveChild;
3707 Utf8Str strTmp;
3708 if ( (pDriveChild = pelmHwChild->findChildElement("Image"))
3709 && pDriveChild->getAttributeValue("uuid", strTmp) )
3710 parseUUID(att.uuid, strTmp);
3711 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
3712 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
3713
3714 // store attachment with controller
3715 sctl.llAttachedDevices.push_back(att);
3716 // store controller with storage
3717 strg.llStorageControllers.push_back(sctl);
3718 }
3719 }
3720 }
3721}
3722
3723/**
3724 * Called for reading the <Teleporter> element under <Machine>.
3725 */
3726void MachineConfigFile::readTeleporter(const xml::ElementNode *pElmTeleporter,
3727 MachineUserData *pUserData)
3728{
3729 pElmTeleporter->getAttributeValue("enabled", pUserData->fTeleporterEnabled);
3730 pElmTeleporter->getAttributeValue("port", pUserData->uTeleporterPort);
3731 pElmTeleporter->getAttributeValue("address", pUserData->strTeleporterAddress);
3732 pElmTeleporter->getAttributeValue("password", pUserData->strTeleporterPassword);
3733
3734 if ( pUserData->strTeleporterPassword.isNotEmpty()
3735 && !VBoxIsPasswordHashed(&pUserData->strTeleporterPassword))
3736 VBoxHashPassword(&pUserData->strTeleporterPassword);
3737}
3738
3739/**
3740 * Called for reading the <Debugging> element under <Machine> or <Snapshot>.
3741 */
3742void MachineConfigFile::readDebugging(const xml::ElementNode *pElmDebugging, Debugging *pDbg)
3743{
3744 if (!pElmDebugging || m->sv < SettingsVersion_v1_13)
3745 return;
3746
3747 const xml::ElementNode * const pelmTracing = pElmDebugging->findChildElement("Tracing");
3748 if (pelmTracing)
3749 {
3750 pelmTracing->getAttributeValue("enabled", pDbg->fTracingEnabled);
3751 pelmTracing->getAttributeValue("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
3752 pelmTracing->getAttributeValue("config", pDbg->strTracingConfig);
3753 }
3754}
3755
3756/**
3757 * Called for reading the <Autostart> element under <Machine> or <Snapshot>.
3758 */
3759void MachineConfigFile::readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart)
3760{
3761 Utf8Str strAutostop;
3762
3763 if (!pElmAutostart || m->sv < SettingsVersion_v1_13)
3764 return;
3765
3766 pElmAutostart->getAttributeValue("enabled", pAutostart->fAutostartEnabled);
3767 pElmAutostart->getAttributeValue("delay", pAutostart->uAutostartDelay);
3768 pElmAutostart->getAttributeValue("autostop", strAutostop);
3769 if (strAutostop == "Disabled")
3770 pAutostart->enmAutostopType = AutostopType_Disabled;
3771 else if (strAutostop == "SaveState")
3772 pAutostart->enmAutostopType = AutostopType_SaveState;
3773 else if (strAutostop == "PowerOff")
3774 pAutostart->enmAutostopType = AutostopType_PowerOff;
3775 else if (strAutostop == "AcpiShutdown")
3776 pAutostart->enmAutostopType = AutostopType_AcpiShutdown;
3777 else
3778 throw ConfigFileError(this, pElmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str());
3779}
3780
3781/**
3782 * Called for reading the <Groups> element under <Machine>.
3783 */
3784void MachineConfigFile::readGroups(const xml::ElementNode *pElmGroups, StringsList *pllGroups)
3785{
3786 pllGroups->clear();
3787 if (!pElmGroups || m->sv < SettingsVersion_v1_13)
3788 {
3789 pllGroups->push_back("/");
3790 return;
3791 }
3792
3793 xml::NodesLoop nlGroups(*pElmGroups);
3794 const xml::ElementNode *pelmGroup;
3795 while ((pelmGroup = nlGroups.forAllNodes()))
3796 {
3797 if (pelmGroup->nameEquals("Group"))
3798 {
3799 Utf8Str strGroup;
3800 if (!pelmGroup->getAttributeValue("name", strGroup))
3801 throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing"));
3802 pllGroups->push_back(strGroup);
3803 }
3804 }
3805}
3806
3807/**
3808 * Called initially for the <Snapshot> element under <Machine>, if present,
3809 * to store the snapshot's data into the given Snapshot structure (which is
3810 * then the one in the Machine struct). This might then recurse if
3811 * a <Snapshots> (plural) element is found in the snapshot, which should
3812 * contain a list of child snapshots; such lists are maintained in the
3813 * Snapshot structure.
3814 *
3815 * @param curSnapshotUuid
3816 * @param depth
3817 * @param elmSnapshot
3818 * @param snap
3819 * @returns true if curSnapshotUuid is in this snapshot subtree, otherwise false
3820 */
3821bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid,
3822 uint32_t depth,
3823 const xml::ElementNode &elmSnapshot,
3824 Snapshot &snap)
3825{
3826 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
3827 throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
3828
3829 Utf8Str strTemp;
3830
3831 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
3832 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
3833 parseUUID(snap.uuid, strTemp);
3834 bool foundCurrentSnapshot = (snap.uuid == curSnapshotUuid);
3835
3836 if (!elmSnapshot.getAttributeValue("name", snap.strName))
3837 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
3838
3839 // earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
3840 elmSnapshot.getAttributeValue("Description", snap.strDescription);
3841
3842 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
3843 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
3844 parseTimestamp(snap.timestamp, strTemp);
3845
3846 elmSnapshot.getAttributeValuePath("stateFile", snap.strStateFile); // online snapshots only
3847
3848 // parse Hardware before the other elements because other things depend on it
3849 const xml::ElementNode *pelmHardware;
3850 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
3851 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
3852 readHardware(*pelmHardware, snap.hardware, snap.storage);
3853
3854 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
3855 const xml::ElementNode *pelmSnapshotChild;
3856 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
3857 {
3858 if (pelmSnapshotChild->nameEquals("Description"))
3859 snap.strDescription = pelmSnapshotChild->getValue();
3860 else if ( m->sv < SettingsVersion_v1_7
3861 && pelmSnapshotChild->nameEquals("HardDiskAttachments"))
3862 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.storage);
3863 else if ( m->sv >= SettingsVersion_v1_7
3864 && pelmSnapshotChild->nameEquals("StorageControllers"))
3865 readStorageControllers(*pelmSnapshotChild, snap.storage);
3866 else if (pelmSnapshotChild->nameEquals("Snapshots"))
3867 {
3868 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
3869 const xml::ElementNode *pelmChildSnapshot;
3870 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
3871 {
3872 if (pelmChildSnapshot->nameEquals("Snapshot"))
3873 {
3874 // recurse with this element and put the child at the
3875 // end of the list. XPCOM has very small stack, avoid
3876 // big local variables and use the list element.
3877 snap.llChildSnapshots.push_back(g_SnapshotEmpty);
3878 bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, snap.llChildSnapshots.back());
3879 foundCurrentSnapshot = foundCurrentSnapshot || found;
3880 }
3881 }
3882 }
3883 }
3884
3885 if (m->sv < SettingsVersion_v1_9)
3886 // go through Hardware once more to repair the settings controller structures
3887 // with data from old DVDDrive and FloppyDrive elements
3888 readDVDAndFloppies_pre1_9(*pelmHardware, snap.storage);
3889
3890 readDebugging(elmSnapshot.findChildElement("Debugging"), &snap.debugging);
3891 readAutostart(elmSnapshot.findChildElement("Autostart"), &snap.autostart);
3892 // note: Groups exist only for Machine, not for Snapshot
3893
3894 return foundCurrentSnapshot;
3895}
3896
3897const struct {
3898 const char *pcszOld;
3899 const char *pcszNew;
3900} aConvertOSTypes[] =
3901{
3902 { "unknown", "Other" },
3903 { "dos", "DOS" },
3904 { "win31", "Windows31" },
3905 { "win95", "Windows95" },
3906 { "win98", "Windows98" },
3907 { "winme", "WindowsMe" },
3908 { "winnt4", "WindowsNT4" },
3909 { "win2k", "Windows2000" },
3910 { "winxp", "WindowsXP" },
3911 { "win2k3", "Windows2003" },
3912 { "winvista", "WindowsVista" },
3913 { "win2k8", "Windows2008" },
3914 { "os2warp3", "OS2Warp3" },
3915 { "os2warp4", "OS2Warp4" },
3916 { "os2warp45", "OS2Warp45" },
3917 { "ecs", "OS2eCS" },
3918 { "linux22", "Linux22" },
3919 { "linux24", "Linux24" },
3920 { "linux26", "Linux26" },
3921 { "archlinux", "ArchLinux" },
3922 { "debian", "Debian" },
3923 { "opensuse", "OpenSUSE" },
3924 { "fedoracore", "Fedora" },
3925 { "gentoo", "Gentoo" },
3926 { "mandriva", "Mandriva" },
3927 { "redhat", "RedHat" },
3928 { "ubuntu", "Ubuntu" },
3929 { "xandros", "Xandros" },
3930 { "freebsd", "FreeBSD" },
3931 { "openbsd", "OpenBSD" },
3932 { "netbsd", "NetBSD" },
3933 { "netware", "Netware" },
3934 { "solaris", "Solaris" },
3935 { "opensolaris", "OpenSolaris" },
3936 { "l4", "L4" }
3937};
3938
3939void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
3940{
3941 for (unsigned u = 0;
3942 u < RT_ELEMENTS(aConvertOSTypes);
3943 ++u)
3944 {
3945 if (str == aConvertOSTypes[u].pcszOld)
3946 {
3947 str = aConvertOSTypes[u].pcszNew;
3948 break;
3949 }
3950 }
3951}
3952
3953/**
3954 * Called from the constructor to actually read in the <Machine> element
3955 * of a machine config file.
3956 * @param elmMachine
3957 */
3958void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
3959{
3960 Utf8Str strUUID;
3961 if ( elmMachine.getAttributeValue("uuid", strUUID)
3962 && elmMachine.getAttributeValue("name", machineUserData.strName))
3963 {
3964 parseUUID(uuid, strUUID);
3965
3966 elmMachine.getAttributeValue("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
3967 elmMachine.getAttributeValue("nameSync", machineUserData.fNameSync);
3968
3969 Utf8Str str;
3970 elmMachine.getAttributeValue("Description", machineUserData.strDescription);
3971 elmMachine.getAttributeValue("OSType", machineUserData.strOsType);
3972 if (m->sv < SettingsVersion_v1_5)
3973 convertOldOSType_pre1_5(machineUserData.strOsType);
3974
3975 elmMachine.getAttributeValuePath("stateFile", strStateFile);
3976
3977 if (elmMachine.getAttributeValue("currentSnapshot", str))
3978 parseUUID(uuidCurrentSnapshot, str);
3979
3980 elmMachine.getAttributeValuePath("snapshotFolder", machineUserData.strSnapshotFolder);
3981
3982 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
3983 fCurrentStateModified = true;
3984 if (elmMachine.getAttributeValue("lastStateChange", str))
3985 parseTimestamp(timeLastStateChange, str);
3986 // constructor has called RTTimeNow(&timeLastStateChange) before
3987 if (elmMachine.getAttributeValue("aborted", fAborted))
3988 fAborted = true;
3989
3990 elmMachine.getAttributeValue("icon", machineUserData.ovIcon);
3991
3992 // parse Hardware before the other elements because other things depend on it
3993 const xml::ElementNode *pelmHardware;
3994 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
3995 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
3996 readHardware(*pelmHardware, hardwareMachine, storageMachine);
3997
3998 xml::NodesLoop nlRootChildren(elmMachine);
3999 const xml::ElementNode *pelmMachineChild;
4000 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
4001 {
4002 if (pelmMachineChild->nameEquals("ExtraData"))
4003 readExtraData(*pelmMachineChild,
4004 mapExtraDataItems);
4005 else if ( (m->sv < SettingsVersion_v1_7)
4006 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
4007 )
4008 readHardDiskAttachments_pre1_7(*pelmMachineChild, storageMachine);
4009 else if ( (m->sv >= SettingsVersion_v1_7)
4010 && (pelmMachineChild->nameEquals("StorageControllers"))
4011 )
4012 readStorageControllers(*pelmMachineChild, storageMachine);
4013 else if (pelmMachineChild->nameEquals("Snapshot"))
4014 {
4015 if (uuidCurrentSnapshot.isZero())
4016 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing"));
4017 bool foundCurrentSnapshot = false;
4018 Snapshot snap;
4019 // this will recurse into child snapshots, if necessary
4020 foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, 1, *pelmMachineChild, snap);
4021 if (!foundCurrentSnapshot)
4022 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute"));
4023 llFirstSnapshot.push_back(snap);
4024 }
4025 else if (pelmMachineChild->nameEquals("Description"))
4026 machineUserData.strDescription = pelmMachineChild->getValue();
4027 else if (pelmMachineChild->nameEquals("Teleporter"))
4028 readTeleporter(pelmMachineChild, &machineUserData);
4029 else if (pelmMachineChild->nameEquals("FaultTolerance"))
4030 {
4031 Utf8Str strFaultToleranceSate;
4032 if (pelmMachineChild->getAttributeValue("state", strFaultToleranceSate))
4033 {
4034 if (strFaultToleranceSate == "master")
4035 machineUserData.enmFaultToleranceState = FaultToleranceState_Master;
4036 else
4037 if (strFaultToleranceSate == "standby")
4038 machineUserData.enmFaultToleranceState = FaultToleranceState_Standby;
4039 else
4040 machineUserData.enmFaultToleranceState = FaultToleranceState_Inactive;
4041 }
4042 pelmMachineChild->getAttributeValue("port", machineUserData.uFaultTolerancePort);
4043 pelmMachineChild->getAttributeValue("address", machineUserData.strFaultToleranceAddress);
4044 pelmMachineChild->getAttributeValue("interval", machineUserData.uFaultToleranceInterval);
4045 pelmMachineChild->getAttributeValue("password", machineUserData.strFaultTolerancePassword);
4046 }
4047 else if (pelmMachineChild->nameEquals("MediaRegistry"))
4048 readMediaRegistry(*pelmMachineChild, mediaRegistry);
4049 else if (pelmMachineChild->nameEquals("Debugging"))
4050 readDebugging(pelmMachineChild, &debugging);
4051 else if (pelmMachineChild->nameEquals("Autostart"))
4052 readAutostart(pelmMachineChild, &autostart);
4053 else if (pelmMachineChild->nameEquals("Groups"))
4054 readGroups(pelmMachineChild, &machineUserData.llGroups);
4055 }
4056
4057 if (m->sv < SettingsVersion_v1_9)
4058 // go through Hardware once more to repair the settings controller structures
4059 // with data from old DVDDrive and FloppyDrive elements
4060 readDVDAndFloppies_pre1_9(*pelmHardware, storageMachine);
4061 }
4062 else
4063 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
4064}
4065
4066/**
4067 * Creates a <Hardware> node under elmParent and then writes out the XML
4068 * keys under that. Called for both the <Machine> node and for snapshots.
4069 * @param elmParent
4070 * @param st
4071 */
4072void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
4073 const Hardware &hw,
4074 const Storage &strg)
4075{
4076 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
4077
4078 if (m->sv >= SettingsVersion_v1_4)
4079 pelmHardware->setAttribute("version", hw.strVersion);
4080
4081 if ((m->sv >= SettingsVersion_v1_9)
4082 && !hw.uuid.isZero()
4083 && hw.uuid.isValid()
4084 )
4085 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
4086
4087 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
4088
4089 xml::ElementNode *pelmHwVirtEx = pelmCPU->createChild("HardwareVirtEx");
4090 pelmHwVirtEx->setAttribute("enabled", hw.fHardwareVirt);
4091
4092 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
4093 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
4094 pelmCPU->createChild("HardwareVirtExUX")->setAttribute("enabled", hw.fUnrestrictedExecution);
4095 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
4096 if (m->sv >= SettingsVersion_v1_14 && hw.enmLongMode != Hardware::LongMode_Legacy)
4097 pelmCPU->createChild("LongMode")->setAttribute("enabled", hw.enmLongMode == Hardware::LongMode_Enabled);
4098
4099 if (hw.fTripleFaultReset)
4100 pelmCPU->createChild("TripleFaultReset")->setAttribute("enabled", hw.fTripleFaultReset);
4101 pelmCPU->setAttribute("count", hw.cCPUs);
4102 if (hw.ulCpuExecutionCap != 100)
4103 pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap);
4104 if (hw.uCpuIdPortabilityLevel != 0)
4105 pelmCPU->setAttribute("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
4106
4107 /* Always save this setting as we have changed the default in 4.0 (on for large memory 64-bit systems). */
4108 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
4109
4110 if (m->sv >= SettingsVersion_v1_9)
4111 pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce);
4112
4113 if (m->sv >= SettingsVersion_v1_10)
4114 {
4115 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
4116
4117 xml::ElementNode *pelmCpuTree = NULL;
4118 for (CpuList::const_iterator it = hw.llCpus.begin();
4119 it != hw.llCpus.end();
4120 ++it)
4121 {
4122 const Cpu &cpu = *it;
4123
4124 if (pelmCpuTree == NULL)
4125 pelmCpuTree = pelmCPU->createChild("CpuTree");
4126
4127 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
4128 pelmCpu->setAttribute("id", cpu.ulId);
4129 }
4130 }
4131
4132 xml::ElementNode *pelmCpuIdTree = NULL;
4133 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
4134 it != hw.llCpuIdLeafs.end();
4135 ++it)
4136 {
4137 const CpuIdLeaf &leaf = *it;
4138
4139 if (pelmCpuIdTree == NULL)
4140 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
4141
4142 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
4143 pelmCpuIdLeaf->setAttribute("id", leaf.ulId);
4144 pelmCpuIdLeaf->setAttribute("eax", leaf.ulEax);
4145 pelmCpuIdLeaf->setAttribute("ebx", leaf.ulEbx);
4146 pelmCpuIdLeaf->setAttribute("ecx", leaf.ulEcx);
4147 pelmCpuIdLeaf->setAttribute("edx", leaf.ulEdx);
4148 }
4149
4150 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
4151 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
4152 if (m->sv >= SettingsVersion_v1_10)
4153 {
4154 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
4155 }
4156
4157 if ( (m->sv >= SettingsVersion_v1_9)
4158 && (hw.firmwareType >= FirmwareType_EFI)
4159 )
4160 {
4161 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
4162 const char *pcszFirmware;
4163
4164 switch (hw.firmwareType)
4165 {
4166 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
4167 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
4168 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
4169 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
4170 default: pcszFirmware = "None"; break;
4171 }
4172 pelmFirmware->setAttribute("type", pcszFirmware);
4173 }
4174
4175 if ( (m->sv >= SettingsVersion_v1_10)
4176 )
4177 {
4178 xml::ElementNode *pelmHID = pelmHardware->createChild("HID");
4179 const char *pcszHID;
4180
4181 switch (hw.pointingHIDType)
4182 {
4183 case PointingHIDType_USBMouse: pcszHID = "USBMouse"; break;
4184 case PointingHIDType_USBTablet: pcszHID = "USBTablet"; break;
4185 case PointingHIDType_PS2Mouse: pcszHID = "PS2Mouse"; break;
4186 case PointingHIDType_ComboMouse: pcszHID = "ComboMouse"; break;
4187 case PointingHIDType_USBMultiTouch: pcszHID = "USBMultiTouch";break;
4188 case PointingHIDType_None: pcszHID = "None"; break;
4189 default: Assert(false); pcszHID = "PS2Mouse"; break;
4190 }
4191 pelmHID->setAttribute("Pointing", pcszHID);
4192
4193 switch (hw.keyboardHIDType)
4194 {
4195 case KeyboardHIDType_USBKeyboard: pcszHID = "USBKeyboard"; break;
4196 case KeyboardHIDType_PS2Keyboard: pcszHID = "PS2Keyboard"; break;
4197 case KeyboardHIDType_ComboKeyboard: pcszHID = "ComboKeyboard"; break;
4198 case KeyboardHIDType_None: pcszHID = "None"; break;
4199 default: Assert(false); pcszHID = "PS2Keyboard"; break;
4200 }
4201 pelmHID->setAttribute("Keyboard", pcszHID);
4202 }
4203
4204 if ( (m->sv >= SettingsVersion_v1_10)
4205 )
4206 {
4207 xml::ElementNode *pelmHPET = pelmHardware->createChild("HPET");
4208 pelmHPET->setAttribute("enabled", hw.fHPETEnabled);
4209 }
4210
4211 if ( (m->sv >= SettingsVersion_v1_11)
4212 )
4213 {
4214 xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset");
4215 const char *pcszChipset;
4216
4217 switch (hw.chipsetType)
4218 {
4219 case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break;
4220 case ChipsetType_ICH9: pcszChipset = "ICH9"; break;
4221 default: Assert(false); pcszChipset = "PIIX3"; break;
4222 }
4223 pelmChipset->setAttribute("type", pcszChipset);
4224 }
4225
4226 if ( (m->sv >= SettingsVersion_v1_15)
4227 && !hw.areParavirtDefaultSettings()
4228 )
4229 {
4230 const char *pcszParavirtProvider;
4231 switch (hw.paravirtProvider)
4232 {
4233 case ParavirtProvider_None: pcszParavirtProvider = "None"; break;
4234 case ParavirtProvider_Default: pcszParavirtProvider = "Default"; break;
4235 case ParavirtProvider_Legacy: pcszParavirtProvider = "Legacy"; break;
4236 case ParavirtProvider_Minimal: pcszParavirtProvider = "Minimal"; break;
4237 case ParavirtProvider_HyperV: pcszParavirtProvider = "HyperV"; break;
4238 case ParavirtProvider_KVM: pcszParavirtProvider = "KVM"; break;
4239 default: Assert(false); pcszParavirtProvider = "None"; break;
4240 }
4241
4242 xml::ElementNode *pelmParavirt = pelmHardware->createChild("Paravirt");
4243 pelmParavirt->setAttribute("provider", pcszParavirtProvider);
4244 }
4245
4246 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
4247 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
4248 it != hw.mapBootOrder.end();
4249 ++it)
4250 {
4251 uint32_t i = it->first;
4252 DeviceType_T type = it->second;
4253 const char *pcszDevice;
4254
4255 switch (type)
4256 {
4257 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
4258 case DeviceType_DVD: pcszDevice = "DVD"; break;
4259 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
4260 case DeviceType_Network: pcszDevice = "Network"; break;
4261 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
4262 }
4263
4264 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
4265 pelmOrder->setAttribute("position",
4266 i + 1); // XML is 1-based but internal data is 0-based
4267 pelmOrder->setAttribute("device", pcszDevice);
4268 }
4269
4270 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
4271 if (hw.graphicsControllerType != GraphicsControllerType_VBoxVGA)
4272 {
4273 const char *pcszGraphics;
4274 switch (hw.graphicsControllerType)
4275 {
4276 case GraphicsControllerType_VBoxVGA: pcszGraphics = "VBoxVGA"; break;
4277 case GraphicsControllerType_VMSVGA: pcszGraphics = "VMSVGA"; break;
4278 default: /*case GraphicsControllerType_Null:*/ pcszGraphics = "None"; break;
4279 }
4280 pelmDisplay->setAttribute("controller", pcszGraphics);
4281 }
4282 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
4283 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
4284 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
4285
4286 if (m->sv >= SettingsVersion_v1_8)
4287 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
4288 xml::ElementNode *pelmVideoCapture = pelmHardware->createChild("VideoCapture");
4289
4290 if (m->sv >= SettingsVersion_v1_14)
4291 {
4292 pelmVideoCapture->setAttribute("enabled", hw.fVideoCaptureEnabled);
4293 pelmVideoCapture->setAttribute("screens", hw.u64VideoCaptureScreens);
4294 if (!hw.strVideoCaptureFile.isEmpty())
4295 pelmVideoCapture->setAttributePath("file", hw.strVideoCaptureFile);
4296 pelmVideoCapture->setAttribute("horzRes", hw.ulVideoCaptureHorzRes);
4297 pelmVideoCapture->setAttribute("vertRes", hw.ulVideoCaptureVertRes);
4298 pelmVideoCapture->setAttribute("rate", hw.ulVideoCaptureRate);
4299 pelmVideoCapture->setAttribute("fps", hw.ulVideoCaptureFPS);
4300 pelmVideoCapture->setAttribute("maxTime", hw.ulVideoCaptureMaxTime);
4301 pelmVideoCapture->setAttribute("maxSize", hw.ulVideoCaptureMaxSize);
4302 }
4303
4304 xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay");
4305 pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled);
4306 if (m->sv < SettingsVersion_v1_11)
4307 {
4308 /* In VBox 4.0 these attributes are replaced with "Properties". */
4309 Utf8Str strPort;
4310 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports");
4311 if (it != hw.vrdeSettings.mapProperties.end())
4312 strPort = it->second;
4313 if (!strPort.length())
4314 strPort = "3389";
4315 pelmVRDE->setAttribute("port", strPort);
4316
4317 Utf8Str strAddress;
4318 it = hw.vrdeSettings.mapProperties.find("TCP/Address");
4319 if (it != hw.vrdeSettings.mapProperties.end())
4320 strAddress = it->second;
4321 if (strAddress.length())
4322 pelmVRDE->setAttribute("netAddress", strAddress);
4323 }
4324 const char *pcszAuthType;
4325 switch (hw.vrdeSettings.authType)
4326 {
4327 case AuthType_Guest: pcszAuthType = "Guest"; break;
4328 case AuthType_External: pcszAuthType = "External"; break;
4329 default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break;
4330 }
4331 pelmVRDE->setAttribute("authType", pcszAuthType);
4332
4333 if (hw.vrdeSettings.ulAuthTimeout != 0)
4334 pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout);
4335 if (hw.vrdeSettings.fAllowMultiConnection)
4336 pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
4337 if (hw.vrdeSettings.fReuseSingleConnection)
4338 pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
4339
4340 if (m->sv == SettingsVersion_v1_10)
4341 {
4342 xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel");
4343
4344 /* In 4.0 videochannel settings were replaced with properties, so look at properties. */
4345 Utf8Str str;
4346 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
4347 if (it != hw.vrdeSettings.mapProperties.end())
4348 str = it->second;
4349 bool fVideoChannel = RTStrICmp(str.c_str(), "true") == 0
4350 || RTStrCmp(str.c_str(), "1") == 0;
4351 pelmVideoChannel->setAttribute("enabled", fVideoChannel);
4352
4353 it = hw.vrdeSettings.mapProperties.find("VideoChannel/Quality");
4354 if (it != hw.vrdeSettings.mapProperties.end())
4355 str = it->second;
4356 uint32_t ulVideoChannelQuality = RTStrToUInt32(str.c_str()); /* This returns 0 on invalid string which is ok. */
4357 if (ulVideoChannelQuality == 0)
4358 ulVideoChannelQuality = 75;
4359 else
4360 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
4361 pelmVideoChannel->setAttribute("quality", ulVideoChannelQuality);
4362 }
4363 if (m->sv >= SettingsVersion_v1_11)
4364 {
4365 if (hw.vrdeSettings.strAuthLibrary.length())
4366 pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary);
4367 if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty())
4368 pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
4369 if (hw.vrdeSettings.mapProperties.size() > 0)
4370 {
4371 xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties");
4372 for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin();
4373 it != hw.vrdeSettings.mapProperties.end();
4374 ++it)
4375 {
4376 const Utf8Str &strName = it->first;
4377 const Utf8Str &strValue = it->second;
4378 xml::ElementNode *pelm = pelmProperties->createChild("Property");
4379 pelm->setAttribute("name", strName);
4380 pelm->setAttribute("value", strValue);
4381 }
4382 }
4383 }
4384
4385 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
4386 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
4387 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
4388
4389 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
4390 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
4391 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
4392 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
4393 if (hw.biosSettings.strLogoImagePath.length())
4394 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
4395
4396 const char *pcszBootMenu;
4397 switch (hw.biosSettings.biosBootMenuMode)
4398 {
4399 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
4400 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
4401 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
4402 }
4403 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
4404 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
4405 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
4406
4407 if (m->sv < SettingsVersion_v1_9)
4408 {
4409 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
4410 // run thru the storage controllers to see if we have a DVD or floppy drives
4411 size_t cDVDs = 0;
4412 size_t cFloppies = 0;
4413
4414 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
4415 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
4416
4417 for (StorageControllersList::const_iterator it = strg.llStorageControllers.begin();
4418 it != strg.llStorageControllers.end();
4419 ++it)
4420 {
4421 const StorageController &sctl = *it;
4422 // in old settings format, the DVD drive could only have been under the IDE controller
4423 if (sctl.storageBus == StorageBus_IDE)
4424 {
4425 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
4426 it2 != sctl.llAttachedDevices.end();
4427 ++it2)
4428 {
4429 const AttachedDevice &att = *it2;
4430 if (att.deviceType == DeviceType_DVD)
4431 {
4432 if (cDVDs > 0)
4433 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
4434
4435 ++cDVDs;
4436
4437 pelmDVD->setAttribute("passthrough", att.fPassThrough);
4438 if (att.fTempEject)
4439 pelmDVD->setAttribute("tempeject", att.fTempEject);
4440
4441 if (!att.uuid.isZero() && att.uuid.isValid())
4442 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
4443 else if (att.strHostDriveSrc.length())
4444 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
4445 }
4446 }
4447 }
4448 else if (sctl.storageBus == StorageBus_Floppy)
4449 {
4450 size_t cFloppiesHere = sctl.llAttachedDevices.size();
4451 if (cFloppiesHere > 1)
4452 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
4453 if (cFloppiesHere)
4454 {
4455 const AttachedDevice &att = sctl.llAttachedDevices.front();
4456 pelmFloppy->setAttribute("enabled", true);
4457
4458 if (!att.uuid.isZero() && att.uuid.isValid())
4459 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
4460 else if (att.strHostDriveSrc.length())
4461 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
4462 }
4463
4464 cFloppies += cFloppiesHere;
4465 }
4466 }
4467
4468 if (cFloppies == 0)
4469 pelmFloppy->setAttribute("enabled", false);
4470 else if (cFloppies > 1)
4471 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
4472 }
4473
4474 if (m->sv < SettingsVersion_v1_14)
4475 {
4476 bool fOhciEnabled = false;
4477 bool fEhciEnabled = false;
4478 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
4479
4480 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
4481 it != hardwareMachine.usbSettings.llUSBControllers.end();
4482 ++it)
4483 {
4484 const USBController &ctrl = *it;
4485
4486 switch (ctrl.enmType)
4487 {
4488 case USBControllerType_OHCI:
4489 fOhciEnabled = true;
4490 break;
4491 case USBControllerType_EHCI:
4492 fEhciEnabled = true;
4493 break;
4494 default:
4495 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
4496 }
4497 }
4498
4499 pelmUSB->setAttribute("enabled", fOhciEnabled);
4500 pelmUSB->setAttribute("enabledEhci", fEhciEnabled);
4501
4502 buildUSBDeviceFilters(*pelmUSB, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
4503 }
4504 else
4505 {
4506 xml::ElementNode *pelmUSB = pelmHardware->createChild("USB");
4507 xml::ElementNode *pelmCtrls = pelmUSB->createChild("Controllers");
4508
4509 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
4510 it != hardwareMachine.usbSettings.llUSBControllers.end();
4511 ++it)
4512 {
4513 const USBController &ctrl = *it;
4514 com::Utf8Str strType;
4515 xml::ElementNode *pelmCtrl = pelmCtrls->createChild("Controller");
4516
4517 switch (ctrl.enmType)
4518 {
4519 case USBControllerType_OHCI:
4520 strType = "OHCI";
4521 break;
4522 case USBControllerType_EHCI:
4523 strType = "EHCI";
4524 break;
4525 case USBControllerType_XHCI:
4526 strType = "XHCI";
4527 break;
4528 default:
4529 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
4530 }
4531
4532 pelmCtrl->setAttribute("name", ctrl.strName);
4533 pelmCtrl->setAttribute("type", strType);
4534 }
4535
4536 xml::ElementNode *pelmFilters = pelmUSB->createChild("DeviceFilters");
4537 buildUSBDeviceFilters(*pelmFilters, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
4538 }
4539
4540 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
4541 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
4542 it != hw.llNetworkAdapters.end();
4543 ++it)
4544 {
4545 const NetworkAdapter &nic = *it;
4546
4547 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
4548 pelmAdapter->setAttribute("slot", nic.ulSlot);
4549 pelmAdapter->setAttribute("enabled", nic.fEnabled);
4550 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
4551 pelmAdapter->setAttribute("cable", nic.fCableConnected);
4552 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
4553 if (nic.ulBootPriority != 0)
4554 {
4555 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
4556 }
4557 if (nic.fTraceEnabled)
4558 {
4559 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
4560 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
4561 }
4562 if (nic.strBandwidthGroup.isNotEmpty())
4563 pelmAdapter->setAttribute("bandwidthGroup", nic.strBandwidthGroup);
4564
4565 const char *pszPolicy;
4566 switch (nic.enmPromiscModePolicy)
4567 {
4568 case NetworkAdapterPromiscModePolicy_Deny: pszPolicy = NULL; break;
4569 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPolicy = "AllowNetwork"; break;
4570 case NetworkAdapterPromiscModePolicy_AllowAll: pszPolicy = "AllowAll"; break;
4571 default: pszPolicy = NULL; AssertFailed(); break;
4572 }
4573 if (pszPolicy)
4574 pelmAdapter->setAttribute("promiscuousModePolicy", pszPolicy);
4575
4576 const char *pcszType;
4577 switch (nic.type)
4578 {
4579 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
4580 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
4581 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
4582 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
4583 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
4584 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
4585 }
4586 pelmAdapter->setAttribute("type", pcszType);
4587
4588 xml::ElementNode *pelmNAT;
4589 if (m->sv < SettingsVersion_v1_10)
4590 {
4591 switch (nic.mode)
4592 {
4593 case NetworkAttachmentType_NAT:
4594 pelmNAT = pelmAdapter->createChild("NAT");
4595 if (nic.nat.strNetwork.length())
4596 pelmNAT->setAttribute("network", nic.nat.strNetwork);
4597 break;
4598
4599 case NetworkAttachmentType_Bridged:
4600 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
4601 break;
4602
4603 case NetworkAttachmentType_Internal:
4604 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
4605 break;
4606
4607 case NetworkAttachmentType_HostOnly:
4608 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
4609 break;
4610
4611 default: /*case NetworkAttachmentType_Null:*/
4612 break;
4613 }
4614 }
4615 else
4616 {
4617 /* m->sv >= SettingsVersion_v1_10 */
4618 xml::ElementNode *pelmDisabledNode = NULL;
4619 pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
4620 if (nic.mode != NetworkAttachmentType_NAT)
4621 buildNetworkXML(NetworkAttachmentType_NAT, *pelmDisabledNode, false, nic);
4622 if (nic.mode != NetworkAttachmentType_Bridged)
4623 buildNetworkXML(NetworkAttachmentType_Bridged, *pelmDisabledNode, false, nic);
4624 if (nic.mode != NetworkAttachmentType_Internal)
4625 buildNetworkXML(NetworkAttachmentType_Internal, *pelmDisabledNode, false, nic);
4626 if (nic.mode != NetworkAttachmentType_HostOnly)
4627 buildNetworkXML(NetworkAttachmentType_HostOnly, *pelmDisabledNode, false, nic);
4628 if (nic.mode != NetworkAttachmentType_Generic)
4629 buildNetworkXML(NetworkAttachmentType_Generic, *pelmDisabledNode, false, nic);
4630 if (nic.mode != NetworkAttachmentType_NATNetwork)
4631 buildNetworkXML(NetworkAttachmentType_NATNetwork, *pelmDisabledNode, false, nic);
4632 buildNetworkXML(nic.mode, *pelmAdapter, true, nic);
4633 }
4634 }
4635
4636 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
4637 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
4638 it != hw.llSerialPorts.end();
4639 ++it)
4640 {
4641 const SerialPort &port = *it;
4642 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
4643 pelmPort->setAttribute("slot", port.ulSlot);
4644 pelmPort->setAttribute("enabled", port.fEnabled);
4645 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
4646 pelmPort->setAttribute("IRQ", port.ulIRQ);
4647
4648 const char *pcszHostMode;
4649 switch (port.portMode)
4650 {
4651 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
4652 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
4653 case PortMode_TCP: pcszHostMode = "TCP"; break;
4654 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
4655 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
4656 }
4657 switch (port.portMode)
4658 {
4659 case PortMode_TCP:
4660 case PortMode_HostPipe:
4661 pelmPort->setAttribute("server", port.fServer);
4662 /* no break */
4663 case PortMode_HostDevice:
4664 case PortMode_RawFile:
4665 pelmPort->setAttribute("path", port.strPath);
4666 break;
4667
4668 default:
4669 break;
4670 }
4671 pelmPort->setAttribute("hostMode", pcszHostMode);
4672 }
4673
4674 pelmPorts = pelmHardware->createChild("LPT");
4675 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
4676 it != hw.llParallelPorts.end();
4677 ++it)
4678 {
4679 const ParallelPort &port = *it;
4680 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
4681 pelmPort->setAttribute("slot", port.ulSlot);
4682 pelmPort->setAttribute("enabled", port.fEnabled);
4683 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
4684 pelmPort->setAttribute("IRQ", port.ulIRQ);
4685 if (port.strPath.length())
4686 pelmPort->setAttribute("path", port.strPath);
4687 }
4688
4689 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
4690 const char *pcszController;
4691 switch (hw.audioAdapter.controllerType)
4692 {
4693 case AudioControllerType_SB16:
4694 pcszController = "SB16";
4695 break;
4696 case AudioControllerType_HDA:
4697 if (m->sv >= SettingsVersion_v1_11)
4698 {
4699 pcszController = "HDA";
4700 break;
4701 }
4702 /* fall through */
4703 case AudioControllerType_AC97:
4704 default:
4705 pcszController = "AC97";
4706 break;
4707 }
4708 pelmAudio->setAttribute("controller", pcszController);
4709
4710 if (m->sv >= SettingsVersion_v1_10)
4711 {
4712 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
4713 pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local");
4714 }
4715
4716 const char *pcszDriver;
4717 switch (hw.audioAdapter.driverType)
4718 {
4719 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
4720 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
4721 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
4722 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
4723 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
4724 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
4725 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
4726 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
4727 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
4728 }
4729 pelmAudio->setAttribute("driver", pcszDriver);
4730
4731 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
4732
4733 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
4734 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
4735 it != hw.llSharedFolders.end();
4736 ++it)
4737 {
4738 const SharedFolder &sf = *it;
4739 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
4740 pelmThis->setAttribute("name", sf.strName);
4741 pelmThis->setAttribute("hostPath", sf.strHostPath);
4742 pelmThis->setAttribute("writable", sf.fWritable);
4743 pelmThis->setAttribute("autoMount", sf.fAutoMount);
4744 }
4745
4746 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
4747 const char *pcszClip;
4748 switch (hw.clipboardMode)
4749 {
4750 default: /*case ClipboardMode_Disabled:*/ pcszClip = "Disabled"; break;
4751 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
4752 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
4753 case ClipboardMode_Bidirectional: pcszClip = "Bidirectional"; break;
4754 }
4755 pelmClip->setAttribute("mode", pcszClip);
4756
4757 xml::ElementNode *pelmDragAndDrop = pelmHardware->createChild("DragAndDrop");
4758 const char *pcszDragAndDrop;
4759 switch (hw.dndMode)
4760 {
4761 default: /*case DnDMode_Disabled:*/ pcszDragAndDrop = "Disabled"; break;
4762 case DnDMode_HostToGuest: pcszDragAndDrop = "HostToGuest"; break;
4763 case DnDMode_GuestToHost: pcszDragAndDrop = "GuestToHost"; break;
4764 case DnDMode_Bidirectional: pcszDragAndDrop = "Bidirectional"; break;
4765 }
4766 pelmDragAndDrop->setAttribute("mode", pcszDragAndDrop);
4767
4768 if (m->sv >= SettingsVersion_v1_10)
4769 {
4770 xml::ElementNode *pelmIO = pelmHardware->createChild("IO");
4771 xml::ElementNode *pelmIOCache;
4772
4773 pelmIOCache = pelmIO->createChild("IoCache");
4774 pelmIOCache->setAttribute("enabled", hw.ioSettings.fIOCacheEnabled);
4775 pelmIOCache->setAttribute("size", hw.ioSettings.ulIOCacheSize);
4776
4777 if (m->sv >= SettingsVersion_v1_11)
4778 {
4779 xml::ElementNode *pelmBandwidthGroups = pelmIO->createChild("BandwidthGroups");
4780 for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin();
4781 it != hw.ioSettings.llBandwidthGroups.end();
4782 ++it)
4783 {
4784 const BandwidthGroup &gr = *it;
4785 const char *pcszType;
4786 xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup");
4787 pelmThis->setAttribute("name", gr.strName);
4788 switch (gr.enmType)
4789 {
4790 case BandwidthGroupType_Network: pcszType = "Network"; break;
4791 default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break;
4792 }
4793 pelmThis->setAttribute("type", pcszType);
4794 if (m->sv >= SettingsVersion_v1_13)
4795 pelmThis->setAttribute("maxBytesPerSec", gr.cMaxBytesPerSec);
4796 else
4797 pelmThis->setAttribute("maxMbPerSec", gr.cMaxBytesPerSec / _1M);
4798 }
4799 }
4800 }
4801
4802 if (m->sv >= SettingsVersion_v1_12)
4803 {
4804 xml::ElementNode *pelmPCI = pelmHardware->createChild("HostPci");
4805 xml::ElementNode *pelmPCIDevices = pelmPCI->createChild("Devices");
4806
4807 for (HostPCIDeviceAttachmentList::const_iterator it = hw.pciAttachments.begin();
4808 it != hw.pciAttachments.end();
4809 ++it)
4810 {
4811 const HostPCIDeviceAttachment &hpda = *it;
4812
4813 xml::ElementNode *pelmThis = pelmPCIDevices->createChild("Device");
4814
4815 pelmThis->setAttribute("host", hpda.uHostAddress);
4816 pelmThis->setAttribute("guest", hpda.uGuestAddress);
4817 pelmThis->setAttribute("name", hpda.strDeviceName);
4818 }
4819 }
4820
4821 if (m->sv >= SettingsVersion_v1_12)
4822 {
4823 xml::ElementNode *pelmEmulatedUSB = pelmHardware->createChild("EmulatedUSB");
4824
4825 xml::ElementNode *pelmCardReader = pelmEmulatedUSB->createChild("CardReader");
4826 pelmCardReader->setAttribute("enabled", hw.fEmulatedUSBCardReader);
4827 }
4828
4829 if ( m->sv >= SettingsVersion_v1_14
4830 && !hw.strDefaultFrontend.isEmpty())
4831 {
4832 xml::ElementNode *pelmFrontend = pelmHardware->createChild("Frontend");
4833 xml::ElementNode *pelmDefault = pelmFrontend->createChild("Default");
4834 pelmDefault->setAttribute("type", hw.strDefaultFrontend);
4835 }
4836
4837 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
4838 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
4839
4840 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
4841 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
4842 it != hw.llGuestProperties.end();
4843 ++it)
4844 {
4845 const GuestProperty &prop = *it;
4846 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
4847 pelmProp->setAttribute("name", prop.strName);
4848 pelmProp->setAttribute("value", prop.strValue);
4849 pelmProp->setAttribute("timestamp", prop.timestamp);
4850 pelmProp->setAttribute("flags", prop.strFlags);
4851 }
4852}
4853
4854/**
4855 * Fill a <Network> node. Only relevant for XML version >= v1_10.
4856 * @param mode
4857 * @param elmParent
4858 * @param fEnabled
4859 * @param nic
4860 */
4861void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
4862 xml::ElementNode &elmParent,
4863 bool fEnabled,
4864 const NetworkAdapter &nic)
4865{
4866 switch (mode)
4867 {
4868 case NetworkAttachmentType_NAT:
4869 xml::ElementNode *pelmNAT;
4870 pelmNAT = elmParent.createChild("NAT");
4871
4872 if (nic.nat.strNetwork.length())
4873 pelmNAT->setAttribute("network", nic.nat.strNetwork);
4874 if (nic.nat.strBindIP.length())
4875 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
4876 if (nic.nat.u32Mtu)
4877 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
4878 if (nic.nat.u32SockRcv)
4879 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
4880 if (nic.nat.u32SockSnd)
4881 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
4882 if (nic.nat.u32TcpRcv)
4883 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
4884 if (nic.nat.u32TcpSnd)
4885 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
4886 xml::ElementNode *pelmDNS;
4887 pelmDNS = pelmNAT->createChild("DNS");
4888 pelmDNS->setAttribute("pass-domain", nic.nat.fDNSPassDomain);
4889 pelmDNS->setAttribute("use-proxy", nic.nat.fDNSProxy);
4890 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDNSUseHostResolver);
4891
4892 xml::ElementNode *pelmAlias;
4893 pelmAlias = pelmNAT->createChild("Alias");
4894 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
4895 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
4896 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
4897
4898 if ( nic.nat.strTFTPPrefix.length()
4899 || nic.nat.strTFTPBootFile.length()
4900 || nic.nat.strTFTPNextServer.length())
4901 {
4902 xml::ElementNode *pelmTFTP;
4903 pelmTFTP = pelmNAT->createChild("TFTP");
4904 if (nic.nat.strTFTPPrefix.length())
4905 pelmTFTP->setAttribute("prefix", nic.nat.strTFTPPrefix);
4906 if (nic.nat.strTFTPBootFile.length())
4907 pelmTFTP->setAttribute("boot-file", nic.nat.strTFTPBootFile);
4908 if (nic.nat.strTFTPNextServer.length())
4909 pelmTFTP->setAttribute("next-server", nic.nat.strTFTPNextServer);
4910 }
4911 buildNATForwardRuleList(*pelmNAT, nic.nat.llRules);
4912 break;
4913
4914 case NetworkAttachmentType_Bridged:
4915 if (fEnabled || !nic.strBridgedName.isEmpty())
4916 elmParent.createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
4917 break;
4918
4919 case NetworkAttachmentType_Internal:
4920 if (fEnabled || !nic.strInternalNetworkName.isEmpty())
4921 elmParent.createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
4922 break;
4923
4924 case NetworkAttachmentType_HostOnly:
4925 if (fEnabled || !nic.strHostOnlyName.isEmpty())
4926 elmParent.createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
4927 break;
4928
4929 case NetworkAttachmentType_Generic:
4930 if (fEnabled || !nic.strGenericDriver.isEmpty() || nic.genericProperties.size())
4931 {
4932 xml::ElementNode *pelmMode = elmParent.createChild("GenericInterface");
4933 pelmMode->setAttribute("driver", nic.strGenericDriver);
4934 for (StringsMap::const_iterator it = nic.genericProperties.begin();
4935 it != nic.genericProperties.end();
4936 ++it)
4937 {
4938 xml::ElementNode *pelmProp = pelmMode->createChild("Property");
4939 pelmProp->setAttribute("name", it->first);
4940 pelmProp->setAttribute("value", it->second);
4941 }
4942 }
4943 break;
4944
4945 case NetworkAttachmentType_NATNetwork:
4946 if (fEnabled || !nic.strNATNetworkName.isEmpty())
4947 elmParent.createChild("NATNetwork")->setAttribute("name", nic.strNATNetworkName);
4948 break;
4949
4950 default: /*case NetworkAttachmentType_Null:*/
4951 break;
4952 }
4953}
4954
4955/**
4956 * Creates a <StorageControllers> node under elmParent and then writes out the XML
4957 * keys under that. Called for both the <Machine> node and for snapshots.
4958 * @param elmParent
4959 * @param st
4960 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
4961 * an empty drive is always written instead. This is for the OVF export case.
4962 * This parameter is ignored unless the settings version is at least v1.9, which
4963 * is always the case when this gets called for OVF export.
4964 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
4965 * pointers to which we will append all elements that we created here that contain
4966 * UUID attributes. This allows the OVF export code to quickly replace the internal
4967 * media UUIDs with the UUIDs of the media that were exported.
4968 */
4969void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
4970 const Storage &st,
4971 bool fSkipRemovableMedia,
4972 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
4973{
4974 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
4975
4976 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
4977 it != st.llStorageControllers.end();
4978 ++it)
4979 {
4980 const StorageController &sc = *it;
4981
4982 if ( (m->sv < SettingsVersion_v1_9)
4983 && (sc.controllerType == StorageControllerType_I82078)
4984 )
4985 // floppy controller already got written into <Hardware>/<FloppyController> in buildHardwareXML()
4986 // for pre-1.9 settings
4987 continue;
4988
4989 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
4990 com::Utf8Str name = sc.strName;
4991 if (m->sv < SettingsVersion_v1_8)
4992 {
4993 // pre-1.8 settings use shorter controller names, they are
4994 // expanded when reading the settings
4995 if (name == "IDE Controller")
4996 name = "IDE";
4997 else if (name == "SATA Controller")
4998 name = "SATA";
4999 else if (name == "SCSI Controller")
5000 name = "SCSI";
5001 }
5002 pelmController->setAttribute("name", sc.strName);
5003
5004 const char *pcszType;
5005 switch (sc.controllerType)
5006 {
5007 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
5008 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
5009 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
5010 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
5011 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
5012 case StorageControllerType_I82078: pcszType = "I82078"; break;
5013 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
5014 case StorageControllerType_USB: pcszType = "USB"; break;
5015 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
5016 }
5017 pelmController->setAttribute("type", pcszType);
5018
5019 pelmController->setAttribute("PortCount", sc.ulPortCount);
5020
5021 if (m->sv >= SettingsVersion_v1_9)
5022 if (sc.ulInstance)
5023 pelmController->setAttribute("Instance", sc.ulInstance);
5024
5025 if (m->sv >= SettingsVersion_v1_10)
5026 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
5027
5028 if (m->sv >= SettingsVersion_v1_11)
5029 pelmController->setAttribute("Bootable", sc.fBootable);
5030
5031 if (sc.controllerType == StorageControllerType_IntelAhci)
5032 {
5033 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
5034 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
5035 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
5036 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
5037 }
5038
5039 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
5040 it2 != sc.llAttachedDevices.end();
5041 ++it2)
5042 {
5043 const AttachedDevice &att = *it2;
5044
5045 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
5046 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
5047 // the floppy controller at the top of the loop
5048 if ( att.deviceType == DeviceType_DVD
5049 && m->sv < SettingsVersion_v1_9
5050 )
5051 continue;
5052
5053 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
5054
5055 pcszType = NULL;
5056
5057 switch (att.deviceType)
5058 {
5059 case DeviceType_HardDisk:
5060 pcszType = "HardDisk";
5061 if (att.fNonRotational)
5062 pelmDevice->setAttribute("nonrotational", att.fNonRotational);
5063 if (att.fDiscard)
5064 pelmDevice->setAttribute("discard", att.fDiscard);
5065 break;
5066
5067 case DeviceType_DVD:
5068 pcszType = "DVD";
5069 pelmDevice->setAttribute("passthrough", att.fPassThrough);
5070 if (att.fTempEject)
5071 pelmDevice->setAttribute("tempeject", att.fTempEject);
5072 break;
5073
5074 case DeviceType_Floppy:
5075 pcszType = "Floppy";
5076 break;
5077 }
5078
5079 pelmDevice->setAttribute("type", pcszType);
5080
5081 if (m->sv >= SettingsVersion_v1_15)
5082 pelmDevice->setAttribute("hotpluggable", att.fHotPluggable);
5083
5084 pelmDevice->setAttribute("port", att.lPort);
5085 pelmDevice->setAttribute("device", att.lDevice);
5086
5087 if (att.strBwGroup.length())
5088 pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup);
5089
5090 // attached image, if any
5091 if (!att.uuid.isZero()
5092 && att.uuid.isValid()
5093 && (att.deviceType == DeviceType_HardDisk
5094 || !fSkipRemovableMedia
5095 )
5096 )
5097 {
5098 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
5099 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
5100
5101 // if caller wants a list of UUID elements, give it to them
5102 if (pllElementsWithUuidAttributes)
5103 pllElementsWithUuidAttributes->push_back(pelmImage);
5104 }
5105 else if ( (m->sv >= SettingsVersion_v1_9)
5106 && (att.strHostDriveSrc.length())
5107 )
5108 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
5109 }
5110 }
5111}
5112
5113/**
5114 * Creates a <Debugging> node under elmParent and then writes out the XML
5115 * keys under that. Called for both the <Machine> node and for snapshots.
5116 *
5117 * @param pElmParent Pointer to the parent element.
5118 * @param pDbg Pointer to the debugging settings.
5119 */
5120void MachineConfigFile::buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg)
5121{
5122 if (m->sv < SettingsVersion_v1_13 || pDbg->areDefaultSettings())
5123 return;
5124
5125 xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");
5126 xml::ElementNode *pElmTracing = pElmDebugging->createChild("Tracing");
5127 pElmTracing->setAttribute("enabled", pDbg->fTracingEnabled);
5128 pElmTracing->setAttribute("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
5129 pElmTracing->setAttribute("config", pDbg->strTracingConfig);
5130}
5131
5132/**
5133 * Creates a <Autostart> node under elmParent and then writes out the XML
5134 * keys under that. Called for both the <Machine> node and for snapshots.
5135 *
5136 * @param pElmParent Pointer to the parent element.
5137 * @param pAutostart Pointer to the autostart settings.
5138 */
5139void MachineConfigFile::buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart)
5140{
5141 const char *pcszAutostop = NULL;
5142
5143 if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())
5144 return;
5145
5146 xml::ElementNode *pElmAutostart = pElmParent->createChild("Autostart");
5147 pElmAutostart->setAttribute("enabled", pAutostart->fAutostartEnabled);
5148 pElmAutostart->setAttribute("delay", pAutostart->uAutostartDelay);
5149
5150 switch (pAutostart->enmAutostopType)
5151 {
5152 case AutostopType_Disabled: pcszAutostop = "Disabled"; break;
5153 case AutostopType_SaveState: pcszAutostop = "SaveState"; break;
5154 case AutostopType_PowerOff: pcszAutostop = "PowerOff"; break;
5155 case AutostopType_AcpiShutdown: pcszAutostop = "AcpiShutdown"; break;
5156 default: Assert(false); pcszAutostop = "Disabled"; break;
5157 }
5158 pElmAutostart->setAttribute("autostop", pcszAutostop);
5159}
5160
5161/**
5162 * Creates a <Groups> node under elmParent and then writes out the XML
5163 * keys under that. Called for the <Machine> node only.
5164 *
5165 * @param pElmParent Pointer to the parent element.
5166 * @param pllGroups Pointer to the groups list.
5167 */
5168void MachineConfigFile::buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups)
5169{
5170 if ( m->sv < SettingsVersion_v1_13 || pllGroups->size() == 0
5171 || (pllGroups->size() == 1 && pllGroups->front() == "/"))
5172 return;
5173
5174 xml::ElementNode *pElmGroups = pElmParent->createChild("Groups");
5175 for (StringsList::const_iterator it = pllGroups->begin();
5176 it != pllGroups->end();
5177 ++it)
5178 {
5179 const Utf8Str &group = *it;
5180 xml::ElementNode *pElmGroup = pElmGroups->createChild("Group");
5181 pElmGroup->setAttribute("name", group);
5182 }
5183}
5184
5185/**
5186 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
5187 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
5188 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
5189 *
5190 * @param depth
5191 * @param elmParent
5192 * @param snap
5193 */
5194void MachineConfigFile::buildSnapshotXML(uint32_t depth,
5195 xml::ElementNode &elmParent,
5196 const Snapshot &snap)
5197{
5198 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
5199 throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
5200
5201 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
5202
5203 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
5204 pelmSnapshot->setAttribute("name", snap.strName);
5205 pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(snap.timestamp));
5206
5207 if (snap.strStateFile.length())
5208 pelmSnapshot->setAttributePath("stateFile", snap.strStateFile);
5209
5210 if (snap.strDescription.length())
5211 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
5212
5213 buildHardwareXML(*pelmSnapshot, snap.hardware, snap.storage);
5214 buildStorageControllersXML(*pelmSnapshot,
5215 snap.storage,
5216 false /* fSkipRemovableMedia */,
5217 NULL); /* pllElementsWithUuidAttributes */
5218 // we only skip removable media for OVF, but we never get here for OVF
5219 // since snapshots never get written then
5220 buildDebuggingXML(pelmSnapshot, &snap.debugging);
5221 buildAutostartXML(pelmSnapshot, &snap.autostart);
5222 // note: Groups exist only for Machine, not for Snapshot
5223
5224 if (snap.llChildSnapshots.size())
5225 {
5226 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
5227 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
5228 it != snap.llChildSnapshots.end();
5229 ++it)
5230 {
5231 const Snapshot &child = *it;
5232 buildSnapshotXML(depth + 1, *pelmChildren, child);
5233 }
5234 }
5235}
5236
5237/**
5238 * Builds the XML DOM tree for the machine config under the given XML element.
5239 *
5240 * This has been separated out from write() so it can be called from elsewhere,
5241 * such as the OVF code, to build machine XML in an existing XML tree.
5242 *
5243 * As a result, this gets called from two locations:
5244 *
5245 * -- MachineConfigFile::write();
5246 *
5247 * -- Appliance::buildXMLForOneVirtualSystem()
5248 *
5249 * In fl, the following flag bits are recognized:
5250 *
5251 * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will
5252 * be written, if present. This is not set when called from OVF because OVF
5253 * has its own variant of a media registry. This flag is ignored unless the
5254 * settings version is at least v1.11 (VirtualBox 4.0).
5255 *
5256 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
5257 * of the machine and write out <Snapshot> and possibly more snapshots under
5258 * that, if snapshots are present. Otherwise all snapshots are suppressed
5259 * (when called from OVF).
5260 *
5261 * -- BuildMachineXML_WriteVBoxVersionAttribute: If set, add a settingsVersion
5262 * attribute to the machine tag with the vbox settings version. This is for
5263 * the OVF export case in which we don't have the settings version set in
5264 * the root element.
5265 *
5266 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
5267 * (DVDs, floppies) are silently skipped. This is for the OVF export case
5268 * until we support copying ISO and RAW media as well. This flag is ignored
5269 * unless the settings version is at least v1.9, which is always the case
5270 * when this gets called for OVF export.
5271 *
5272 * -- BuildMachineXML_SuppressSavedState: If set, the Machine/@stateFile
5273 * attribute is never set. This is also for the OVF export case because we
5274 * cannot save states with OVF.
5275 *
5276 * @param elmMachine XML <Machine> element to add attributes and elements to.
5277 * @param fl Flags.
5278 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
5279 * see buildStorageControllersXML() for details.
5280 */
5281void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
5282 uint32_t fl,
5283 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
5284{
5285 if (fl & BuildMachineXML_WriteVBoxVersionAttribute)
5286 // add settings version attribute to machine element
5287 setVersionAttribute(elmMachine);
5288
5289 elmMachine.setAttribute("uuid", uuid.toStringCurly());
5290 elmMachine.setAttribute("name", machineUserData.strName);
5291 if (machineUserData.fDirectoryIncludesUUID)
5292 elmMachine.setAttribute("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
5293 if (!machineUserData.fNameSync)
5294 elmMachine.setAttribute("nameSync", machineUserData.fNameSync);
5295 if (machineUserData.strDescription.length())
5296 elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
5297 elmMachine.setAttribute("OSType", machineUserData.strOsType);
5298 if ( strStateFile.length()
5299 && !(fl & BuildMachineXML_SuppressSavedState)
5300 )
5301 elmMachine.setAttributePath("stateFile", strStateFile);
5302
5303 if ((fl & BuildMachineXML_IncludeSnapshots)
5304 && !uuidCurrentSnapshot.isZero()
5305 && uuidCurrentSnapshot.isValid())
5306 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
5307
5308 if (machineUserData.strSnapshotFolder.length())
5309 elmMachine.setAttributePath("snapshotFolder", machineUserData.strSnapshotFolder);
5310 if (!fCurrentStateModified)
5311 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
5312 elmMachine.setAttribute("lastStateChange", stringifyTimestamp(timeLastStateChange));
5313 if (fAborted)
5314 elmMachine.setAttribute("aborted", fAborted);
5315 // Please keep the icon last so that one doesn't have to check if there
5316 // is anything in the line after this very long attribute in the XML.
5317 if (machineUserData.ovIcon.length())
5318 elmMachine.setAttribute("icon", machineUserData.ovIcon);
5319 if ( m->sv >= SettingsVersion_v1_9
5320 && ( machineUserData.fTeleporterEnabled
5321 || machineUserData.uTeleporterPort
5322 || !machineUserData.strTeleporterAddress.isEmpty()
5323 || !machineUserData.strTeleporterPassword.isEmpty()
5324 )
5325 )
5326 {
5327 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
5328 pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled);
5329 pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort);
5330 pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress);
5331 pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword);
5332 }
5333
5334 if ( m->sv >= SettingsVersion_v1_11
5335 && ( machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
5336 || machineUserData.uFaultTolerancePort
5337 || machineUserData.uFaultToleranceInterval
5338 || !machineUserData.strFaultToleranceAddress.isEmpty()
5339 )
5340 )
5341 {
5342 xml::ElementNode *pelmFaultTolerance = elmMachine.createChild("FaultTolerance");
5343 switch (machineUserData.enmFaultToleranceState)
5344 {
5345 case FaultToleranceState_Inactive:
5346 pelmFaultTolerance->setAttribute("state", "inactive");
5347 break;
5348 case FaultToleranceState_Master:
5349 pelmFaultTolerance->setAttribute("state", "master");
5350 break;
5351 case FaultToleranceState_Standby:
5352 pelmFaultTolerance->setAttribute("state", "standby");
5353 break;
5354 }
5355
5356 pelmFaultTolerance->setAttribute("port", machineUserData.uFaultTolerancePort);
5357 pelmFaultTolerance->setAttribute("address", machineUserData.strFaultToleranceAddress);
5358 pelmFaultTolerance->setAttribute("interval", machineUserData.uFaultToleranceInterval);
5359 pelmFaultTolerance->setAttribute("password", machineUserData.strFaultTolerancePassword);
5360 }
5361
5362 if ( (fl & BuildMachineXML_MediaRegistry)
5363 && (m->sv >= SettingsVersion_v1_11)
5364 )
5365 buildMediaRegistry(elmMachine, mediaRegistry);
5366
5367 buildExtraData(elmMachine, mapExtraDataItems);
5368
5369 if ( (fl & BuildMachineXML_IncludeSnapshots)
5370 && llFirstSnapshot.size())
5371 buildSnapshotXML(1, elmMachine, llFirstSnapshot.front());
5372
5373 buildHardwareXML(elmMachine, hardwareMachine, storageMachine);
5374 buildStorageControllersXML(elmMachine,
5375 storageMachine,
5376 !!(fl & BuildMachineXML_SkipRemovableMedia),
5377 pllElementsWithUuidAttributes);
5378 buildDebuggingXML(&elmMachine, &debugging);
5379 buildAutostartXML(&elmMachine, &autostart);
5380 buildGroupsXML(&elmMachine, &machineUserData.llGroups);
5381}
5382
5383/**
5384 * Returns true only if the given AudioDriverType is supported on
5385 * the current host platform. For example, this would return false
5386 * for AudioDriverType_DirectSound when compiled on a Linux host.
5387 * @param drv AudioDriverType_* enum to test.
5388 * @return true only if the current host supports that driver.
5389 */
5390/*static*/
5391bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
5392{
5393 switch (drv)
5394 {
5395 case AudioDriverType_Null:
5396#ifdef RT_OS_WINDOWS
5397# ifdef VBOX_WITH_WINMM
5398 case AudioDriverType_WinMM:
5399# endif
5400 case AudioDriverType_DirectSound:
5401#endif /* RT_OS_WINDOWS */
5402#ifdef RT_OS_SOLARIS
5403 case AudioDriverType_SolAudio:
5404#endif
5405#ifdef RT_OS_LINUX
5406# ifdef VBOX_WITH_ALSA
5407 case AudioDriverType_ALSA:
5408# endif
5409# ifdef VBOX_WITH_PULSE
5410 case AudioDriverType_Pulse:
5411# endif
5412#endif /* RT_OS_LINUX */
5413#if defined (RT_OS_LINUX) || defined (RT_OS_FREEBSD) || defined(VBOX_WITH_SOLARIS_OSS)
5414 case AudioDriverType_OSS:
5415#endif
5416#ifdef RT_OS_FREEBSD
5417# ifdef VBOX_WITH_PULSE
5418 case AudioDriverType_Pulse:
5419# endif
5420#endif
5421#ifdef RT_OS_DARWIN
5422 case AudioDriverType_CoreAudio:
5423#endif
5424#ifdef RT_OS_OS2
5425 case AudioDriverType_MMPM:
5426#endif
5427 return true;
5428 }
5429
5430 return false;
5431}
5432
5433/**
5434 * Returns the AudioDriverType_* which should be used by default on this
5435 * host platform. On Linux, this will check at runtime whether PulseAudio
5436 * or ALSA are actually supported on the first call.
5437 * @return
5438 */
5439/*static*/
5440AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
5441{
5442#if defined(RT_OS_WINDOWS)
5443# ifdef VBOX_WITH_WINMM
5444 return AudioDriverType_WinMM;
5445# else /* VBOX_WITH_WINMM */
5446 return AudioDriverType_DirectSound;
5447# endif /* !VBOX_WITH_WINMM */
5448#elif defined(RT_OS_SOLARIS)
5449 return AudioDriverType_SolAudio;
5450#elif defined(RT_OS_LINUX)
5451 // on Linux, we need to check at runtime what's actually supported...
5452 static RTCLockMtx s_mtx;
5453 static AudioDriverType_T s_linuxDriver = -1;
5454 RTCLock lock(s_mtx);
5455 if (s_linuxDriver == (AudioDriverType_T)-1)
5456 {
5457# if defined(VBOX_WITH_PULSE)
5458 /* Check for the pulse library & that the pulse audio daemon is running. */
5459 if (RTProcIsRunningByName("pulseaudio") &&
5460 RTLdrIsLoadable("libpulse.so.0"))
5461 s_linuxDriver = AudioDriverType_Pulse;
5462 else
5463# endif /* VBOX_WITH_PULSE */
5464# if defined(VBOX_WITH_ALSA)
5465 /* Check if we can load the ALSA library */
5466 if (RTLdrIsLoadable("libasound.so.2"))
5467 s_linuxDriver = AudioDriverType_ALSA;
5468 else
5469# endif /* VBOX_WITH_ALSA */
5470 s_linuxDriver = AudioDriverType_OSS;
5471 }
5472 return s_linuxDriver;
5473// end elif defined(RT_OS_LINUX)
5474#elif defined(RT_OS_DARWIN)
5475 return AudioDriverType_CoreAudio;
5476#elif defined(RT_OS_OS2)
5477 return AudioDriverType_MMPM;
5478#elif defined(RT_OS_FREEBSD)
5479 return AudioDriverType_OSS;
5480#else
5481 return AudioDriverType_Null;
5482#endif
5483}
5484
5485/**
5486 * Called from write() before calling ConfigFileBase::createStubDocument().
5487 * This adjusts the settings version in m->sv if incompatible settings require
5488 * a settings bump, whereas otherwise we try to preserve the settings version
5489 * to avoid breaking compatibility with older versions.
5490 *
5491 * We do the checks in here in reverse order: newest first, oldest last, so
5492 * that we avoid unnecessary checks since some of these are expensive.
5493 */
5494void MachineConfigFile::bumpSettingsVersionIfNeeded()
5495{
5496 if (m->sv < SettingsVersion_v1_15)
5497 {
5498 // VirtualBox 5.0 adds paravirt providers, explicit AHCI port hotplug
5499 // setting, USB storage controller, xHCI and serial port TCP backend.
5500
5501 /*
5502 * Check simple configuration bits first, loopy stuff afterwards.
5503 */
5504 if ( hardwareMachine.paravirtProvider != ParavirtProvider_Legacy
5505 || hardwareMachine.uCpuIdPortabilityLevel != 0)
5506 {
5507 m->sv = SettingsVersion_v1_15;
5508 return;
5509 }
5510
5511 /*
5512 * Check whether the hotpluggable flag of all storage devices differs
5513 * from the default for old settings.
5514 * AHCI ports are hotpluggable by default every other device is not.
5515 * Also check if there are USB storage controllers.
5516 */
5517 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
5518 it != storageMachine.llStorageControllers.end();
5519 ++it)
5520 {
5521 const StorageController &sctl = *it;
5522
5523 if (sctl.controllerType == StorageControllerType_USB)
5524 {
5525 m->sv = SettingsVersion_v1_15;
5526 return;
5527 }
5528
5529 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
5530 it2 != sctl.llAttachedDevices.end();
5531 ++it2)
5532 {
5533 const AttachedDevice &att = *it2;
5534
5535 if ( ( att.fHotPluggable
5536 && sctl.controllerType != StorageControllerType_IntelAhci)
5537 || ( !att.fHotPluggable
5538 && sctl.controllerType == StorageControllerType_IntelAhci))
5539 {
5540 m->sv = SettingsVersion_v1_15;
5541 return;
5542 }
5543 }
5544 }
5545
5546 /*
5547 * Check if there is an xHCI (USB3) USB controller.
5548 */
5549 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
5550 it != hardwareMachine.usbSettings.llUSBControllers.end();
5551 ++it)
5552 {
5553 const USBController &ctrl = *it;
5554 if (ctrl.enmType == USBControllerType_XHCI)
5555 {
5556 m->sv = SettingsVersion_v1_15;
5557 return;
5558 }
5559 }
5560
5561 /*
5562 * Check if any serial port uses the TCP backend.
5563 */
5564 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
5565 it != hardwareMachine.llSerialPorts.end();
5566 ++it)
5567 {
5568 const SerialPort &port = *it;
5569 if (port.portMode == PortMode_TCP)
5570 {
5571 m->sv = SettingsVersion_v1_15;
5572 return;
5573 }
5574 }
5575 }
5576
5577 if (m->sv < SettingsVersion_v1_14)
5578 {
5579 // VirtualBox 4.3 adds default frontend setting, graphics controller
5580 // setting, explicit long mode setting, video capturing and NAT networking.
5581 if ( !hardwareMachine.strDefaultFrontend.isEmpty()
5582 || hardwareMachine.graphicsControllerType != GraphicsControllerType_VBoxVGA
5583 || hardwareMachine.enmLongMode != Hardware::LongMode_Legacy
5584 || machineUserData.ovIcon.length() > 0
5585 || hardwareMachine.fVideoCaptureEnabled)
5586 {
5587 m->sv = SettingsVersion_v1_14;
5588 return;
5589 }
5590 NetworkAdaptersList::const_iterator netit;
5591 for (netit = hardwareMachine.llNetworkAdapters.begin();
5592 netit != hardwareMachine.llNetworkAdapters.end();
5593 ++netit)
5594 {
5595 if (netit->mode == NetworkAttachmentType_NATNetwork)
5596 {
5597 m->sv = SettingsVersion_v1_14;
5598 break;
5599 }
5600 }
5601 }
5602
5603 if (m->sv < SettingsVersion_v1_14)
5604 {
5605 unsigned cOhciCtrls = 0;
5606 unsigned cEhciCtrls = 0;
5607 bool fNonStdName = false;
5608
5609 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
5610 it != hardwareMachine.usbSettings.llUSBControllers.end();
5611 ++it)
5612 {
5613 const USBController &ctrl = *it;
5614
5615 switch (ctrl.enmType)
5616 {
5617 case USBControllerType_OHCI:
5618 cOhciCtrls++;
5619 if (ctrl.strName != "OHCI")
5620 fNonStdName = true;
5621 break;
5622 case USBControllerType_EHCI:
5623 cEhciCtrls++;
5624 if (ctrl.strName != "EHCI")
5625 fNonStdName = true;
5626 break;
5627 default:
5628 /* Anything unknown forces a bump. */
5629 fNonStdName = true;
5630 }
5631
5632 /* Skip checking other controllers if the settings bump is necessary. */
5633 if (cOhciCtrls > 1 || cEhciCtrls > 1 || fNonStdName)
5634 {
5635 m->sv = SettingsVersion_v1_14;
5636 break;
5637 }
5638 }
5639 }
5640
5641 if (m->sv < SettingsVersion_v1_13)
5642 {
5643 // VirtualBox 4.2 adds tracing, autostart, UUID in directory and groups.
5644 if ( !debugging.areDefaultSettings()
5645 || !autostart.areDefaultSettings()
5646 || machineUserData.fDirectoryIncludesUUID
5647 || machineUserData.llGroups.size() > 1
5648 || machineUserData.llGroups.front() != "/")
5649 m->sv = SettingsVersion_v1_13;
5650 }
5651
5652 if (m->sv < SettingsVersion_v1_13)
5653 {
5654 // VirtualBox 4.2 changes the units for bandwidth group limits.
5655 for (BandwidthGroupList::const_iterator it = hardwareMachine.ioSettings.llBandwidthGroups.begin();
5656 it != hardwareMachine.ioSettings.llBandwidthGroups.end();
5657 ++it)
5658 {
5659 const BandwidthGroup &gr = *it;
5660 if (gr.cMaxBytesPerSec % _1M)
5661 {
5662 // Bump version if a limit cannot be expressed in megabytes
5663 m->sv = SettingsVersion_v1_13;
5664 break;
5665 }
5666 }
5667 }
5668
5669 if (m->sv < SettingsVersion_v1_12)
5670 {
5671 // VirtualBox 4.1 adds PCI passthrough and emulated USB Smart Card reader
5672 if ( hardwareMachine.pciAttachments.size()
5673 || hardwareMachine.fEmulatedUSBCardReader)
5674 m->sv = SettingsVersion_v1_12;
5675 }
5676
5677 if (m->sv < SettingsVersion_v1_12)
5678 {
5679 // VirtualBox 4.1 adds a promiscuous mode policy to the network
5680 // adapters and a generic network driver transport.
5681 NetworkAdaptersList::const_iterator netit;
5682 for (netit = hardwareMachine.llNetworkAdapters.begin();
5683 netit != hardwareMachine.llNetworkAdapters.end();
5684 ++netit)
5685 {
5686 if ( netit->enmPromiscModePolicy != NetworkAdapterPromiscModePolicy_Deny
5687 || netit->mode == NetworkAttachmentType_Generic
5688 || !netit->strGenericDriver.isEmpty()
5689 || netit->genericProperties.size()
5690 )
5691 {
5692 m->sv = SettingsVersion_v1_12;
5693 break;
5694 }
5695 }
5696 }
5697
5698 if (m->sv < SettingsVersion_v1_11)
5699 {
5700 // VirtualBox 4.0 adds HD audio, CPU priorities, fault tolerance,
5701 // per-machine media registries, VRDE, JRockitVE, bandwidth groups,
5702 // ICH9 chipset
5703 if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
5704 || hardwareMachine.ulCpuExecutionCap != 100
5705 || machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
5706 || machineUserData.uFaultTolerancePort
5707 || machineUserData.uFaultToleranceInterval
5708 || !machineUserData.strFaultToleranceAddress.isEmpty()
5709 || mediaRegistry.llHardDisks.size()
5710 || mediaRegistry.llDvdImages.size()
5711 || mediaRegistry.llFloppyImages.size()
5712 || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty()
5713 || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty()
5714 || machineUserData.strOsType == "JRockitVE"
5715 || hardwareMachine.ioSettings.llBandwidthGroups.size()
5716 || hardwareMachine.chipsetType == ChipsetType_ICH9
5717 )
5718 m->sv = SettingsVersion_v1_11;
5719 }
5720
5721 if (m->sv < SettingsVersion_v1_10)
5722 {
5723 /* If the properties contain elements other than "TCP/Ports" and "TCP/Address",
5724 * then increase the version to at least VBox 3.2, which can have video channel properties.
5725 */
5726 unsigned cOldProperties = 0;
5727
5728 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
5729 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5730 cOldProperties++;
5731 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
5732 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5733 cOldProperties++;
5734
5735 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
5736 m->sv = SettingsVersion_v1_10;
5737 }
5738
5739 if (m->sv < SettingsVersion_v1_11)
5740 {
5741 /* If the properties contain elements other than "TCP/Ports", "TCP/Address",
5742 * "VideoChannel/Enabled" and "VideoChannel/Quality" then increase the version to VBox 4.0.
5743 */
5744 unsigned cOldProperties = 0;
5745
5746 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
5747 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5748 cOldProperties++;
5749 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
5750 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5751 cOldProperties++;
5752 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
5753 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5754 cOldProperties++;
5755 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Quality");
5756 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5757 cOldProperties++;
5758
5759 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
5760 m->sv = SettingsVersion_v1_11;
5761 }
5762
5763 // settings version 1.9 is required if there is not exactly one DVD
5764 // or more than one floppy drive present or the DVD is not at the secondary
5765 // master; this check is a bit more complicated
5766 //
5767 // settings version 1.10 is required if the host cache should be disabled
5768 //
5769 // settings version 1.11 is required for bandwidth limits and if more than
5770 // one controller of each type is present.
5771 if (m->sv < SettingsVersion_v1_11)
5772 {
5773 // count attached DVDs and floppies (only if < v1.9)
5774 size_t cDVDs = 0;
5775 size_t cFloppies = 0;
5776
5777 // count storage controllers (if < v1.11)
5778 size_t cSata = 0;
5779 size_t cScsiLsi = 0;
5780 size_t cScsiBuslogic = 0;
5781 size_t cSas = 0;
5782 size_t cIde = 0;
5783 size_t cFloppy = 0;
5784
5785 // need to run thru all the storage controllers and attached devices to figure this out
5786 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
5787 it != storageMachine.llStorageControllers.end();
5788 ++it)
5789 {
5790 const StorageController &sctl = *it;
5791
5792 // count storage controllers of each type; 1.11 is required if more than one
5793 // controller of one type is present
5794 switch (sctl.storageBus)
5795 {
5796 case StorageBus_IDE:
5797 cIde++;
5798 break;
5799 case StorageBus_SATA:
5800 cSata++;
5801 break;
5802 case StorageBus_SAS:
5803 cSas++;
5804 break;
5805 case StorageBus_SCSI:
5806 if (sctl.controllerType == StorageControllerType_LsiLogic)
5807 cScsiLsi++;
5808 else
5809 cScsiBuslogic++;
5810 break;
5811 case StorageBus_Floppy:
5812 cFloppy++;
5813 break;
5814 default:
5815 // Do nothing
5816 break;
5817 }
5818
5819 if ( cSata > 1
5820 || cScsiLsi > 1
5821 || cScsiBuslogic > 1
5822 || cSas > 1
5823 || cIde > 1
5824 || cFloppy > 1)
5825 {
5826 m->sv = SettingsVersion_v1_11;
5827 break; // abort the loop -- we will not raise the version further
5828 }
5829
5830 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
5831 it2 != sctl.llAttachedDevices.end();
5832 ++it2)
5833 {
5834 const AttachedDevice &att = *it2;
5835
5836 // Bandwidth limitations are new in VirtualBox 4.0 (1.11)
5837 if (m->sv < SettingsVersion_v1_11)
5838 {
5839 if (att.strBwGroup.length() != 0)
5840 {
5841 m->sv = SettingsVersion_v1_11;
5842 break; // abort the loop -- we will not raise the version further
5843 }
5844 }
5845
5846 // disabling the host IO cache requires settings version 1.10
5847 if ( (m->sv < SettingsVersion_v1_10)
5848 && (!sctl.fUseHostIOCache)
5849 )
5850 m->sv = SettingsVersion_v1_10;
5851
5852 // we can only write the StorageController/@Instance attribute with v1.9
5853 if ( (m->sv < SettingsVersion_v1_9)
5854 && (sctl.ulInstance != 0)
5855 )
5856 m->sv = SettingsVersion_v1_9;
5857
5858 if (m->sv < SettingsVersion_v1_9)
5859 {
5860 if (att.deviceType == DeviceType_DVD)
5861 {
5862 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
5863 || (att.lPort != 1) // DVDs not at secondary master?
5864 || (att.lDevice != 0)
5865 )
5866 m->sv = SettingsVersion_v1_9;
5867
5868 ++cDVDs;
5869 }
5870 else if (att.deviceType == DeviceType_Floppy)
5871 ++cFloppies;
5872 }
5873 }
5874
5875 if (m->sv >= SettingsVersion_v1_11)
5876 break; // abort the loop -- we will not raise the version further
5877 }
5878
5879 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
5880 // so any deviation from that will require settings version 1.9
5881 if ( (m->sv < SettingsVersion_v1_9)
5882 && ( (cDVDs != 1)
5883 || (cFloppies > 1)
5884 )
5885 )
5886 m->sv = SettingsVersion_v1_9;
5887 }
5888
5889 // VirtualBox 3.2: Check for non default I/O settings
5890 if (m->sv < SettingsVersion_v1_10)
5891 {
5892 if ( (hardwareMachine.ioSettings.fIOCacheEnabled != true)
5893 || (hardwareMachine.ioSettings.ulIOCacheSize != 5)
5894 // and page fusion
5895 || (hardwareMachine.fPageFusionEnabled)
5896 // and CPU hotplug, RTC timezone control, HID type and HPET
5897 || machineUserData.fRTCUseUTC
5898 || hardwareMachine.fCpuHotPlug
5899 || hardwareMachine.pointingHIDType != PointingHIDType_PS2Mouse
5900 || hardwareMachine.keyboardHIDType != KeyboardHIDType_PS2Keyboard
5901 || hardwareMachine.fHPETEnabled
5902 )
5903 m->sv = SettingsVersion_v1_10;
5904 }
5905
5906 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main
5907 // VirtualBox 4.0 adds network bandwitdth
5908 if (m->sv < SettingsVersion_v1_11)
5909 {
5910 NetworkAdaptersList::const_iterator netit;
5911 for (netit = hardwareMachine.llNetworkAdapters.begin();
5912 netit != hardwareMachine.llNetworkAdapters.end();
5913 ++netit)
5914 {
5915 if ( (m->sv < SettingsVersion_v1_12)
5916 && (netit->strBandwidthGroup.isNotEmpty())
5917 )
5918 {
5919 /* New in VirtualBox 4.1 */
5920 m->sv = SettingsVersion_v1_12;
5921 break;
5922 }
5923 else if ( (m->sv < SettingsVersion_v1_10)
5924 && (netit->fEnabled)
5925 && (netit->mode == NetworkAttachmentType_NAT)
5926 && ( netit->nat.u32Mtu != 0
5927 || netit->nat.u32SockRcv != 0
5928 || netit->nat.u32SockSnd != 0
5929 || netit->nat.u32TcpRcv != 0
5930 || netit->nat.u32TcpSnd != 0
5931 || !netit->nat.fDNSPassDomain
5932 || netit->nat.fDNSProxy
5933 || netit->nat.fDNSUseHostResolver
5934 || netit->nat.fAliasLog
5935 || netit->nat.fAliasProxyOnly
5936 || netit->nat.fAliasUseSamePorts
5937 || netit->nat.strTFTPPrefix.length()
5938 || netit->nat.strTFTPBootFile.length()
5939 || netit->nat.strTFTPNextServer.length()
5940 || netit->nat.llRules.size()
5941 )
5942 )
5943 {
5944 m->sv = SettingsVersion_v1_10;
5945 // no break because we still might need v1.11 above
5946 }
5947 else if ( (m->sv < SettingsVersion_v1_10)
5948 && (netit->fEnabled)
5949 && (netit->ulBootPriority != 0)
5950 )
5951 {
5952 m->sv = SettingsVersion_v1_10;
5953 // no break because we still might need v1.11 above
5954 }
5955 }
5956 }
5957
5958 // all the following require settings version 1.9
5959 if ( (m->sv < SettingsVersion_v1_9)
5960 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
5961 || machineUserData.fTeleporterEnabled
5962 || machineUserData.uTeleporterPort
5963 || !machineUserData.strTeleporterAddress.isEmpty()
5964 || !machineUserData.strTeleporterPassword.isEmpty()
5965 || (!hardwareMachine.uuid.isZero() && hardwareMachine.uuid.isValid())
5966 )
5967 )
5968 m->sv = SettingsVersion_v1_9;
5969
5970 // "accelerate 2d video" requires settings version 1.8
5971 if ( (m->sv < SettingsVersion_v1_8)
5972 && (hardwareMachine.fAccelerate2DVideo)
5973 )
5974 m->sv = SettingsVersion_v1_8;
5975
5976 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
5977 if ( m->sv < SettingsVersion_v1_4
5978 && hardwareMachine.strVersion != "1"
5979 )
5980 m->sv = SettingsVersion_v1_4;
5981}
5982
5983/**
5984 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
5985 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
5986 * in particular if the file cannot be written.
5987 */
5988void MachineConfigFile::write(const com::Utf8Str &strFilename)
5989{
5990 try
5991 {
5992 // createStubDocument() sets the settings version to at least 1.7; however,
5993 // we might need to enfore a later settings version if incompatible settings
5994 // are present:
5995 bumpSettingsVersionIfNeeded();
5996
5997 m->strFilename = strFilename;
5998 createStubDocument();
5999
6000 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
6001 buildMachineXML(*pelmMachine,
6002 MachineConfigFile::BuildMachineXML_IncludeSnapshots
6003 | MachineConfigFile::BuildMachineXML_MediaRegistry,
6004 // but not BuildMachineXML_WriteVBoxVersionAttribute
6005 NULL); /* pllElementsWithUuidAttributes */
6006
6007 // now go write the XML
6008 xml::XmlFileWriter writer(*m->pDoc);
6009 writer.write(m->strFilename.c_str(), true /*fSafe*/);
6010
6011 m->fFileExists = true;
6012 clearDocument();
6013 }
6014 catch (...)
6015 {
6016 clearDocument();
6017 throw;
6018 }
6019}
Note: See TracBrowser for help on using the repository browser.

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