VirtualBox

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

Last change on this file since 76900 was 76678, checked in by vboxsync, 6 years ago

Port r124260, r124263, r124271, r124273, r124277, r124278, r124279, r124284, r124285, r124286, r124287, r124288, r124289 and r124290 (Ported fixes over from 5.2, see bugref:9179 for more information)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 315.1 KB
Line 
1/* $Id: Settings.cpp 76678 2019-01-07 13:48:16Z 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 * 5) You _must_ update xml/VirtualBox-settings.xsd to contain the new tags and attributes.
56 * Check that settings file from before and after your change are validating properly.
57 * Use "kmk testvalidsettings", it should not find any files which don't validate.
58 */
59
60/*
61 * Copyright (C) 2007-2019 Oracle Corporation
62 *
63 * This file is part of VirtualBox Open Source Edition (OSE), as
64 * available from http://www.virtualbox.org. This file is free software;
65 * you can redistribute it and/or modify it under the terms of the GNU
66 * General Public License (GPL) as published by the Free Software
67 * Foundation, in version 2 as it comes in the "COPYING" file of the
68 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
69 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
70 */
71
72#define LOG_GROUP LOG_GROUP_MAIN
73#include "VBox/com/string.h"
74#include "VBox/settings.h"
75#include <iprt/base64.h>
76#include <iprt/cpp/lock.h>
77#include <iprt/cpp/xml.h>
78#include <iprt/ctype.h>
79#include <iprt/err.h>
80#include <iprt/file.h>
81#include <iprt/ldr.h>
82#include <iprt/process.h>
83#include <iprt/stream.h>
84#include <iprt/uri.h>
85
86// generated header
87#include "SchemaDefs.h"
88
89#include "HashedPw.h"
90#include "LoggingNew.h"
91
92using namespace com;
93using namespace settings;
94
95////////////////////////////////////////////////////////////////////////////////
96//
97// Defines
98//
99////////////////////////////////////////////////////////////////////////////////
100
101/** VirtualBox XML settings namespace */
102#define VBOX_XML_NAMESPACE "http://www.virtualbox.org/"
103
104/** VirtualBox XML schema location (relative URI) */
105#define VBOX_XML_SCHEMA "VirtualBox-settings.xsd"
106
107/** VirtualBox XML settings version number substring ("x.y") */
108#define VBOX_XML_VERSION "1.12"
109
110/** VirtualBox OVF settings import default version number substring ("x.y").
111 *
112 * Think twice before changing this, as all VirtualBox versions before 5.1
113 * wrote the settings version when exporting, but totally ignored it on
114 * importing (while it should have been a mandatory attribute), so 3rd party
115 * software out there creates OVF files with the VirtualBox specific settings
116 * but lacking the version attribute. This shouldn't happen any more, but
117 * breaking existing OVF files isn't nice. */
118#define VBOX_XML_IMPORT_VERSION "1.15"
119
120/** VirtualBox XML settings version platform substring */
121#if defined (RT_OS_DARWIN)
122# define VBOX_XML_PLATFORM "macosx"
123#elif defined (RT_OS_FREEBSD)
124# define VBOX_XML_PLATFORM "freebsd"
125#elif defined (RT_OS_LINUX)
126# define VBOX_XML_PLATFORM "linux"
127#elif defined (RT_OS_NETBSD)
128# define VBOX_XML_PLATFORM "netbsd"
129#elif defined (RT_OS_OPENBSD)
130# define VBOX_XML_PLATFORM "openbsd"
131#elif defined (RT_OS_OS2)
132# define VBOX_XML_PLATFORM "os2"
133#elif defined (RT_OS_SOLARIS)
134# define VBOX_XML_PLATFORM "solaris"
135#elif defined (RT_OS_WINDOWS)
136# define VBOX_XML_PLATFORM "windows"
137#else
138# error Unsupported platform!
139#endif
140
141/** VirtualBox XML settings full version string ("x.y-platform") */
142#define VBOX_XML_VERSION_FULL VBOX_XML_VERSION "-" VBOX_XML_PLATFORM
143
144/** VirtualBox OVF import default settings full version string ("x.y-platform") */
145#define VBOX_XML_IMPORT_VERSION_FULL VBOX_XML_IMPORT_VERSION "-" VBOX_XML_PLATFORM
146
147////////////////////////////////////////////////////////////////////////////////
148//
149// Internal data
150//
151////////////////////////////////////////////////////////////////////////////////
152
153/**
154 * Opaque data structore for ConfigFileBase (only declared
155 * in header, defined only here).
156 */
157
158struct ConfigFileBase::Data
159{
160 Data()
161 : pDoc(NULL),
162 pelmRoot(NULL),
163 sv(SettingsVersion_Null),
164 svRead(SettingsVersion_Null)
165 {}
166
167 ~Data()
168 {
169 cleanup();
170 }
171
172 RTCString strFilename;
173 bool fFileExists;
174
175 xml::Document *pDoc;
176 xml::ElementNode *pelmRoot;
177
178 com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux"
179 SettingsVersion_T sv; // e.g. SettingsVersion_v1_7
180
181 SettingsVersion_T svRead; // settings version that the original file had when it was read,
182 // or SettingsVersion_Null if none
183
184 void copyFrom(const Data &d)
185 {
186 strFilename = d.strFilename;
187 fFileExists = d.fFileExists;
188 strSettingsVersionFull = d.strSettingsVersionFull;
189 sv = d.sv;
190 svRead = d.svRead;
191 }
192
193 void cleanup()
194 {
195 if (pDoc)
196 {
197 delete pDoc;
198 pDoc = NULL;
199 pelmRoot = NULL;
200 }
201 }
202};
203
204/**
205 * Private exception class (not in the header file) that makes
206 * throwing xml::LogicError instances easier. That class is public
207 * and should be caught by client code.
208 */
209class settings::ConfigFileError : public xml::LogicError
210{
211public:
212 ConfigFileError(const ConfigFileBase *file,
213 const xml::Node *pNode,
214 const char *pcszFormat, ...)
215 : xml::LogicError()
216 {
217 va_list args;
218 va_start(args, pcszFormat);
219 Utf8Str strWhat(pcszFormat, args);
220 va_end(args);
221
222 Utf8Str strLine;
223 if (pNode)
224 strLine = Utf8StrFmt(" (line %RU32)", pNode->getLineNumber());
225
226 const char *pcsz = strLine.c_str();
227 Utf8StrFmt str(N_("Error in %s%s -- %s"),
228 file->m->strFilename.c_str(),
229 (pcsz) ? pcsz : "",
230 strWhat.c_str());
231
232 setWhat(str.c_str());
233 }
234};
235
236////////////////////////////////////////////////////////////////////////////////
237//
238// ConfigFileBase
239//
240////////////////////////////////////////////////////////////////////////////////
241
242/**
243 * Constructor. Allocates the XML internals, parses the XML file if
244 * pstrFilename is != NULL and reads the settings version from it.
245 * @param pstrFilename
246 */
247ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
248 : m(new Data)
249{
250 m->fFileExists = false;
251
252 if (pstrFilename)
253 {
254 // reading existing settings file:
255 m->strFilename = *pstrFilename;
256
257 xml::XmlFileParser parser;
258 m->pDoc = new xml::Document;
259 parser.read(*pstrFilename,
260 *m->pDoc);
261
262 m->fFileExists = true;
263
264 m->pelmRoot = m->pDoc->getRootElement();
265 if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
266 throw ConfigFileError(this, m->pelmRoot, N_("Root element in VirtualBox settings files must be \"VirtualBox\""));
267
268 if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
269 throw ConfigFileError(this, m->pelmRoot, N_("Required VirtualBox/@version attribute is missing"));
270
271 LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
272
273 m->sv = parseVersion(m->strSettingsVersionFull, m->pelmRoot);
274
275 // remember the settings version we read in case it gets upgraded later,
276 // so we know when to make backups
277 m->svRead = m->sv;
278 }
279 else
280 {
281 // creating new settings file:
282 m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
283 m->sv = SettingsVersion_v1_12;
284 }
285}
286
287ConfigFileBase::ConfigFileBase(const ConfigFileBase &other)
288 : m(new Data)
289{
290 copyBaseFrom(other);
291 m->strFilename = "";
292 m->fFileExists = false;
293}
294
295/**
296 * Clean up.
297 */
298ConfigFileBase::~ConfigFileBase()
299{
300 if (m)
301 {
302 delete m;
303 m = NULL;
304 }
305}
306
307/**
308 * Helper function to convert a MediaType enum value into string from.
309 * @param t
310 */
311/*static*/
312const char *ConfigFileBase::stringifyMediaType(MediaType t)
313{
314 switch (t)
315 {
316 case HardDisk:
317 return "hard disk";
318 case DVDImage:
319 return "DVD";
320 case FloppyImage:
321 return "floppy";
322 default:
323 AssertMsgFailed(("media type %d\n", t));
324 return "UNKNOWN";
325 }
326}
327
328/**
329 * Helper function that parses a full version number.
330 *
331 * Allow future versions but fail if file is older than 1.6. Throws on errors.
332 * @returns settings version
333 * @param strVersion
334 * @param pElm
335 */
336SettingsVersion_T ConfigFileBase::parseVersion(const Utf8Str &strVersion, const xml::ElementNode *pElm)
337{
338 SettingsVersion_T sv = SettingsVersion_Null;
339 if (strVersion.length() > 3)
340 {
341 uint32_t ulMajor = 0;
342 uint32_t ulMinor = 0;
343
344 const char *pcsz = strVersion.c_str();
345 char c;
346
347 while ( (c = *pcsz)
348 && RT_C_IS_DIGIT(c)
349 )
350 {
351 ulMajor *= 10;
352 ulMajor += c - '0';
353 ++pcsz;
354 }
355
356 if (*pcsz++ == '.')
357 {
358 while ( (c = *pcsz)
359 && RT_C_IS_DIGIT(c)
360 )
361 {
362 ulMinor *= 10;
363 ulMinor += c - '0';
364 ++pcsz;
365 }
366 }
367
368 if (ulMajor == 1)
369 {
370 if (ulMinor == 3)
371 sv = SettingsVersion_v1_3;
372 else if (ulMinor == 4)
373 sv = SettingsVersion_v1_4;
374 else if (ulMinor == 5)
375 sv = SettingsVersion_v1_5;
376 else if (ulMinor == 6)
377 sv = SettingsVersion_v1_6;
378 else if (ulMinor == 7)
379 sv = SettingsVersion_v1_7;
380 else if (ulMinor == 8)
381 sv = SettingsVersion_v1_8;
382 else if (ulMinor == 9)
383 sv = SettingsVersion_v1_9;
384 else if (ulMinor == 10)
385 sv = SettingsVersion_v1_10;
386 else if (ulMinor == 11)
387 sv = SettingsVersion_v1_11;
388 else if (ulMinor == 12)
389 sv = SettingsVersion_v1_12;
390 else if (ulMinor == 13)
391 sv = SettingsVersion_v1_13;
392 else if (ulMinor == 14)
393 sv = SettingsVersion_v1_14;
394 else if (ulMinor == 15)
395 sv = SettingsVersion_v1_15;
396 else if (ulMinor == 16)
397 sv = SettingsVersion_v1_16;
398 else if (ulMinor == 17)
399 sv = SettingsVersion_v1_17;
400 else if (ulMinor > 17)
401 sv = SettingsVersion_Future;
402 }
403 else if (ulMajor > 1)
404 sv = SettingsVersion_Future;
405
406 Log(("Parsed settings version %d.%d to enum value %d\n", ulMajor, ulMinor, sv));
407 }
408
409 if (sv == SettingsVersion_Null)
410 throw ConfigFileError(this, pElm, N_("Cannot handle settings version '%s'"), strVersion.c_str());
411
412 return sv;
413}
414
415/**
416 * Helper function that parses a UUID in string form into
417 * a com::Guid item. Accepts UUIDs both with and without
418 * "{}" brackets. Throws on errors.
419 * @param guid
420 * @param strUUID
421 * @param pElm
422 */
423void ConfigFileBase::parseUUID(Guid &guid,
424 const Utf8Str &strUUID,
425 const xml::ElementNode *pElm) const
426{
427 guid = strUUID.c_str();
428 if (guid.isZero())
429 throw ConfigFileError(this, pElm, N_("UUID \"%s\" has zero format"), strUUID.c_str());
430 else if (!guid.isValid())
431 throw ConfigFileError(this, pElm, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
432}
433
434/**
435 * Parses the given string in str and attempts to treat it as an ISO
436 * date/time stamp to put into timestamp. Throws on errors.
437 * @param timestamp
438 * @param str
439 * @param pElm
440 */
441void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
442 const com::Utf8Str &str,
443 const xml::ElementNode *pElm) const
444{
445 const char *pcsz = str.c_str();
446 // yyyy-mm-ddThh:mm:ss
447 // "2009-07-10T11:54:03Z"
448 // 01234567890123456789
449 // 1
450 if (str.length() > 19)
451 {
452 // timezone must either be unspecified or 'Z' for UTC
453 if ( (pcsz[19])
454 && (pcsz[19] != 'Z')
455 )
456 throw ConfigFileError(this, pElm, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
457
458 int32_t yyyy;
459 uint32_t mm, dd, hh, min, secs;
460 if ( (pcsz[4] == '-')
461 && (pcsz[7] == '-')
462 && (pcsz[10] == 'T')
463 && (pcsz[13] == ':')
464 && (pcsz[16] == ':')
465 )
466 {
467 int rc;
468 if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
469 // could theoretically be negative but let's assume that nobody
470 // created virtual machines before the Christian era
471 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
472 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
473 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
474 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
475 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
476 )
477 {
478 RTTIME time =
479 {
480 yyyy,
481 (uint8_t)mm,
482 0,
483 0,
484 (uint8_t)dd,
485 (uint8_t)hh,
486 (uint8_t)min,
487 (uint8_t)secs,
488 0,
489 RTTIME_FLAGS_TYPE_UTC,
490 0
491 };
492 if (RTTimeNormalize(&time))
493 if (RTTimeImplode(&timestamp, &time))
494 return;
495 }
496
497 throw ConfigFileError(this, pElm, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
498 }
499
500 throw ConfigFileError(this, pElm, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
501 }
502}
503
504/**
505 * Helper function that parses a Base64 formatted string into a binary blob.
506 * @param binary
507 * @param str
508 * @param pElm
509 */
510void ConfigFileBase::parseBase64(IconBlob &binary,
511 const Utf8Str &str,
512 const xml::ElementNode *pElm) const
513{
514#define DECODE_STR_MAX _1M
515 const char* psz = str.c_str();
516 ssize_t cbOut = RTBase64DecodedSize(psz, NULL);
517 if (cbOut > DECODE_STR_MAX)
518 throw ConfigFileError(this, pElm, N_("Base64 encoded data too long (%d > %d)"), cbOut, DECODE_STR_MAX);
519 else if (cbOut < 0)
520 throw ConfigFileError(this, pElm, N_("Base64 encoded data '%s' invalid"), psz);
521 binary.resize(cbOut);
522 int vrc = VINF_SUCCESS;
523 if (cbOut)
524 vrc = RTBase64Decode(psz, &binary.front(), cbOut, NULL, NULL);
525 if (RT_FAILURE(vrc))
526 {
527 binary.resize(0);
528 throw ConfigFileError(this, pElm, N_("Base64 encoded data could not be decoded (%Rrc)"), vrc);
529 }
530}
531
532/**
533 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
534 * @param stamp
535 * @return
536 */
537com::Utf8Str ConfigFileBase::stringifyTimestamp(const RTTIMESPEC &stamp) const
538{
539 RTTIME time;
540 if (!RTTimeExplode(&time, &stamp))
541 throw ConfigFileError(this, NULL, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
542
543 return Utf8StrFmt("%04u-%02u-%02uT%02u:%02u:%02uZ",
544 time.i32Year, time.u8Month, time.u8MonthDay,
545 time.u8Hour, time.u8Minute, time.u8Second);
546}
547
548/**
549 * Helper to create a base64 encoded string out of a binary blob.
550 * @param str
551 * @param binary
552 */
553void ConfigFileBase::toBase64(com::Utf8Str &str, const IconBlob &binary) const
554{
555 ssize_t cb = binary.size();
556 if (cb > 0)
557 {
558 ssize_t cchOut = RTBase64EncodedLength(cb);
559 str.reserve(cchOut+1);
560 int vrc = RTBase64Encode(&binary.front(), cb,
561 str.mutableRaw(), str.capacity(),
562 NULL);
563 if (RT_FAILURE(vrc))
564 throw ConfigFileError(this, NULL, N_("Failed to convert binary data to base64 format (%Rrc)"), vrc);
565 str.jolt();
566 }
567}
568
569/**
570 * Helper method to read in an ExtraData subtree and stores its contents
571 * in the given map of extradata items. Used for both main and machine
572 * extradata (MainConfigFile and MachineConfigFile).
573 * @param elmExtraData
574 * @param map
575 */
576void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
577 StringsMap &map)
578{
579 xml::NodesLoop nlLevel4(elmExtraData);
580 const xml::ElementNode *pelmExtraDataItem;
581 while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
582 {
583 if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
584 {
585 // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
586 Utf8Str strName, strValue;
587 if ( pelmExtraDataItem->getAttributeValue("name", strName)
588 && pelmExtraDataItem->getAttributeValue("value", strValue) )
589 map[strName] = strValue;
590 else
591 throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing"));
592 }
593 }
594}
595
596/**
597 * Reads \<USBDeviceFilter\> entries from under the given elmDeviceFilters node and
598 * stores them in the given linklist. This is in ConfigFileBase because it's used
599 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
600 * filters).
601 * @param elmDeviceFilters
602 * @param ll
603 */
604void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
605 USBDeviceFiltersList &ll)
606{
607 xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
608 const xml::ElementNode *pelmLevel4Child;
609 while ((pelmLevel4Child = nl1.forAllNodes()))
610 {
611 USBDeviceFilter flt;
612 flt.action = USBDeviceFilterAction_Ignore;
613 Utf8Str strAction;
614 if ( pelmLevel4Child->getAttributeValue("name", flt.strName)
615 && pelmLevel4Child->getAttributeValue("active", flt.fActive))
616 {
617 if (!pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId))
618 pelmLevel4Child->getAttributeValue("vendorid", flt.strVendorId); // used before 1.3
619 if (!pelmLevel4Child->getAttributeValue("productId", flt.strProductId))
620 pelmLevel4Child->getAttributeValue("productid", flt.strProductId); // used before 1.3
621 pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
622 pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
623 pelmLevel4Child->getAttributeValue("product", flt.strProduct);
624 if (!pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber))
625 pelmLevel4Child->getAttributeValue("serialnumber", flt.strSerialNumber); // used before 1.3
626 pelmLevel4Child->getAttributeValue("port", flt.strPort);
627
628 // the next 2 are irrelevant for host USB objects
629 pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
630 pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
631
632 // action is only used with host USB objects
633 if (pelmLevel4Child->getAttributeValue("action", strAction))
634 {
635 if (strAction == "Ignore")
636 flt.action = USBDeviceFilterAction_Ignore;
637 else if (strAction == "Hold")
638 flt.action = USBDeviceFilterAction_Hold;
639 else
640 throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str());
641 }
642
643 ll.push_back(flt);
644 }
645 }
646}
647
648/**
649 * Reads a media registry entry from the main VirtualBox.xml file.
650 *
651 * Whereas the current media registry code is fairly straightforward, it was quite a mess
652 * with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements
653 * in the media registry were much more inconsistent, and different elements were used
654 * depending on the type of device and image.
655 *
656 * @param t
657 * @param elmMedium
658 * @param med
659 */
660void ConfigFileBase::readMediumOne(MediaType t,
661 const xml::ElementNode &elmMedium,
662 Medium &med)
663{
664 // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
665
666 Utf8Str strUUID;
667 if (!elmMedium.getAttributeValue("uuid", strUUID))
668 throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName());
669
670 parseUUID(med.uuid, strUUID, &elmMedium);
671
672 bool fNeedsLocation = true;
673
674 if (t == HardDisk)
675 {
676 if (m->sv < SettingsVersion_v1_4)
677 {
678 // here the system is:
679 // <HardDisk uuid="{....}" type="normal">
680 // <VirtualDiskImage filePath="/path/to/xxx.vdi"/>
681 // </HardDisk>
682
683 fNeedsLocation = false;
684 bool fNeedsFilePath = true;
685 const xml::ElementNode *pelmImage;
686 if ((pelmImage = elmMedium.findChildElement("VirtualDiskImage")))
687 med.strFormat = "VDI";
688 else if ((pelmImage = elmMedium.findChildElement("VMDKImage")))
689 med.strFormat = "VMDK";
690 else if ((pelmImage = elmMedium.findChildElement("VHDImage")))
691 med.strFormat = "VHD";
692 else if ((pelmImage = elmMedium.findChildElement("ISCSIHardDisk")))
693 {
694 med.strFormat = "iSCSI";
695
696 fNeedsFilePath = false;
697 // location is special here: current settings specify an "iscsi://user@server:port/target/lun"
698 // string for the location and also have several disk properties for these, whereas this used
699 // to be hidden in several sub-elements before 1.4, so compose a location string and set up
700 // the properties:
701 med.strLocation = "iscsi://";
702 Utf8Str strUser, strServer, strPort, strTarget, strLun;
703 if (pelmImage->getAttributeValue("userName", strUser))
704 {
705 med.strLocation.append(strUser);
706 med.strLocation.append("@");
707 }
708 Utf8Str strServerAndPort;
709 if (pelmImage->getAttributeValue("server", strServer))
710 {
711 strServerAndPort = strServer;
712 }
713 if (pelmImage->getAttributeValue("port", strPort))
714 {
715 if (strServerAndPort.length())
716 strServerAndPort.append(":");
717 strServerAndPort.append(strPort);
718 }
719 med.strLocation.append(strServerAndPort);
720 if (pelmImage->getAttributeValue("target", strTarget))
721 {
722 med.strLocation.append("/");
723 med.strLocation.append(strTarget);
724 }
725 if (pelmImage->getAttributeValue("lun", strLun))
726 {
727 med.strLocation.append("/");
728 med.strLocation.append(strLun);
729 }
730
731 if (strServer.length() && strPort.length())
732 med.properties["TargetAddress"] = strServerAndPort;
733 if (strTarget.length())
734 med.properties["TargetName"] = strTarget;
735 if (strUser.length())
736 med.properties["InitiatorUsername"] = strUser;
737 Utf8Str strPassword;
738 if (pelmImage->getAttributeValue("password", strPassword))
739 med.properties["InitiatorSecret"] = strPassword;
740 if (strLun.length())
741 med.properties["LUN"] = strLun;
742 }
743 else if ((pelmImage = elmMedium.findChildElement("CustomHardDisk")))
744 {
745 fNeedsFilePath = false;
746 fNeedsLocation = true;
747 // also requires @format attribute, which will be queried below
748 }
749 else
750 throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName());
751
752 if (fNeedsFilePath)
753 {
754 if (!(pelmImage->getAttributeValuePath("filePath", med.strLocation)))
755 throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName());
756 }
757 }
758
759 if (med.strFormat.isEmpty()) // not set with 1.4 format above, or 1.4 Custom format?
760 if (!elmMedium.getAttributeValue("format", med.strFormat))
761 throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName());
762
763 if (!elmMedium.getAttributeValue("autoReset", med.fAutoReset))
764 med.fAutoReset = false;
765
766 Utf8Str strType;
767 if (elmMedium.getAttributeValue("type", strType))
768 {
769 // pre-1.4 used lower case, so make this case-insensitive
770 strType.toUpper();
771 if (strType == "NORMAL")
772 med.hdType = MediumType_Normal;
773 else if (strType == "IMMUTABLE")
774 med.hdType = MediumType_Immutable;
775 else if (strType == "WRITETHROUGH")
776 med.hdType = MediumType_Writethrough;
777 else if (strType == "SHAREABLE")
778 med.hdType = MediumType_Shareable;
779 else if (strType == "READONLY")
780 med.hdType = MediumType_Readonly;
781 else if (strType == "MULTIATTACH")
782 med.hdType = MediumType_MultiAttach;
783 else
784 throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable, Writethrough, Shareable, Readonly or MultiAttach"));
785 }
786 }
787 else
788 {
789 if (m->sv < SettingsVersion_v1_4)
790 {
791 // DVD and floppy images before 1.4 had "src" attribute instead of "location"
792 if (!elmMedium.getAttributeValue("src", med.strLocation))
793 throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName());
794
795 fNeedsLocation = false;
796 }
797
798 if (!elmMedium.getAttributeValue("format", med.strFormat))
799 {
800 // DVD and floppy images before 1.11 had no format attribute. assign the default.
801 med.strFormat = "RAW";
802 }
803
804 if (t == DVDImage)
805 med.hdType = MediumType_Readonly;
806 else if (t == FloppyImage)
807 med.hdType = MediumType_Writethrough;
808 }
809
810 if (fNeedsLocation)
811 // current files and 1.4 CustomHardDisk elements must have a location attribute
812 if (!elmMedium.getAttributeValue("location", med.strLocation))
813 throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName());
814
815 // 3.2 builds added Description as an attribute, read it silently
816 // and write it back as an element starting with 5.1.26
817 elmMedium.getAttributeValue("Description", med.strDescription);
818
819 xml::NodesLoop nlMediumChildren(elmMedium);
820 const xml::ElementNode *pelmMediumChild;
821 while ((pelmMediumChild = nlMediumChildren.forAllNodes()))
822 {
823 if (pelmMediumChild->nameEquals("Description"))
824 med.strDescription = pelmMediumChild->getValue();
825 else if (pelmMediumChild->nameEquals("Property"))
826 {
827 // handle medium properties
828 Utf8Str strPropName, strPropValue;
829 if ( pelmMediumChild->getAttributeValue("name", strPropName)
830 && pelmMediumChild->getAttributeValue("value", strPropValue) )
831 med.properties[strPropName] = strPropValue;
832 else
833 throw ConfigFileError(this, pelmMediumChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
834 }
835 }
836}
837
838/**
839 * Reads a media registry entry from the main VirtualBox.xml file and recurses
840 * into children where applicable.
841 *
842 * @param t
843 * @param depth
844 * @param elmMedium
845 * @param med
846 */
847void ConfigFileBase::readMedium(MediaType t,
848 uint32_t depth,
849 const xml::ElementNode &elmMedium, // HardDisk node if root; if recursing,
850 // child HardDisk node or DiffHardDisk node for pre-1.4
851 Medium &med) // medium settings to fill out
852{
853 if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
854 throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
855
856 // Do not inline this method call, as the purpose of having this separate
857 // is to save on stack size. Less local variables are the key for reaching
858 // deep recursion levels with small stack (XPCOM/g++ without optimization).
859 readMediumOne(t, elmMedium, med);
860
861 if (t != HardDisk)
862 return;
863
864 // recurse to handle children
865 MediaList &llSettingsChildren = med.llChildren;
866 xml::NodesLoop nl2(elmMedium, m->sv >= SettingsVersion_v1_4 ? "HardDisk" : "DiffHardDisk");
867 const xml::ElementNode *pelmHDChild;
868 while ((pelmHDChild = nl2.forAllNodes()))
869 {
870 // recurse with this element and put the child at the end of the list.
871 // XPCOM has very small stack, avoid big local variables and use the
872 // list element.
873 llSettingsChildren.push_back(Medium::Empty);
874 readMedium(t,
875 depth + 1,
876 *pelmHDChild,
877 llSettingsChildren.back());
878 }
879}
880
881/**
882 * Reads in the entire \<MediaRegistry\> chunk and stores its media in the lists
883 * of the given MediaRegistry structure.
884 *
885 * This is used in both MainConfigFile and MachineConfigFile since starting with
886 * VirtualBox 4.0, we can have media registries in both.
887 *
888 * For pre-1.4 files, this gets called with the \<DiskRegistry\> chunk instead.
889 *
890 * @param elmMediaRegistry
891 * @param mr
892 */
893void ConfigFileBase::readMediaRegistry(const xml::ElementNode &elmMediaRegistry,
894 MediaRegistry &mr)
895{
896 xml::NodesLoop nl1(elmMediaRegistry);
897 const xml::ElementNode *pelmChild1;
898 while ((pelmChild1 = nl1.forAllNodes()))
899 {
900 MediaType t = Error;
901 if (pelmChild1->nameEquals("HardDisks"))
902 t = HardDisk;
903 else if (pelmChild1->nameEquals("DVDImages"))
904 t = DVDImage;
905 else if (pelmChild1->nameEquals("FloppyImages"))
906 t = FloppyImage;
907 else
908 continue;
909
910 xml::NodesLoop nl2(*pelmChild1);
911 const xml::ElementNode *pelmMedium;
912 while ((pelmMedium = nl2.forAllNodes()))
913 {
914 if ( t == HardDisk
915 && (pelmMedium->nameEquals("HardDisk")))
916 {
917 mr.llHardDisks.push_back(Medium::Empty);
918 readMedium(t, 1, *pelmMedium, mr.llHardDisks.back());
919 }
920 else if ( t == DVDImage
921 && (pelmMedium->nameEquals("Image")))
922 {
923 mr.llDvdImages.push_back(Medium::Empty);
924 readMedium(t, 1, *pelmMedium, mr.llDvdImages.back());
925 }
926 else if ( t == FloppyImage
927 && (pelmMedium->nameEquals("Image")))
928 {
929 mr.llFloppyImages.push_back(Medium::Empty);
930 readMedium(t, 1, *pelmMedium, mr.llFloppyImages.back());
931 }
932 }
933 }
934}
935
936/**
937 * This is common version for reading NAT port forward rule in per-_machine's_adapter_ and
938 * per-network approaches.
939 * Note: this function doesn't in fill given list from xml::ElementNodesList, because there is conflicting
940 * declaration in ovmfreader.h.
941 */
942void ConfigFileBase::readNATForwardRulesMap(const xml::ElementNode &elmParent, NATRulesMap &mapRules)
943{
944 xml::ElementNodesList plstRules;
945 elmParent.getChildElements(plstRules, "Forwarding");
946 for (xml::ElementNodesList::iterator pf = plstRules.begin(); pf != plstRules.end(); ++pf)
947 {
948 NATRule rule;
949 uint32_t port = 0;
950 (*pf)->getAttributeValue("name", rule.strName);
951 (*pf)->getAttributeValue("proto", (uint32_t&)rule.proto);
952 (*pf)->getAttributeValue("hostip", rule.strHostIP);
953 (*pf)->getAttributeValue("hostport", port);
954 rule.u16HostPort = (uint16_t)port;
955 (*pf)->getAttributeValue("guestip", rule.strGuestIP);
956 (*pf)->getAttributeValue("guestport", port);
957 rule.u16GuestPort = (uint16_t)port;
958 mapRules.insert(std::make_pair(rule.strName, rule));
959 }
960}
961
962void ConfigFileBase::readNATLoopbacks(const xml::ElementNode &elmParent, NATLoopbackOffsetList &llLoopbacks)
963{
964 xml::ElementNodesList plstLoopbacks;
965 elmParent.getChildElements(plstLoopbacks, "Loopback4");
966 for (xml::ElementNodesList::iterator lo = plstLoopbacks.begin();
967 lo != plstLoopbacks.end(); ++lo)
968 {
969 NATHostLoopbackOffset loopback;
970 (*lo)->getAttributeValue("address", loopback.strLoopbackHostAddress);
971 (*lo)->getAttributeValue("offset", (uint32_t&)loopback.u32Offset);
972 llLoopbacks.push_back(loopback);
973 }
974}
975
976
977/**
978 * Adds a "version" attribute to the given XML element with the
979 * VirtualBox settings version (e.g. "1.10-linux"). Used by
980 * the XML format for the root element and by the OVF export
981 * for the vbox:Machine element.
982 * @param elm
983 */
984void ConfigFileBase::setVersionAttribute(xml::ElementNode &elm)
985{
986 const char *pcszVersion = NULL;
987 switch (m->sv)
988 {
989 case SettingsVersion_v1_8:
990 pcszVersion = "1.8";
991 break;
992
993 case SettingsVersion_v1_9:
994 pcszVersion = "1.9";
995 break;
996
997 case SettingsVersion_v1_10:
998 pcszVersion = "1.10";
999 break;
1000
1001 case SettingsVersion_v1_11:
1002 pcszVersion = "1.11";
1003 break;
1004
1005 case SettingsVersion_v1_12:
1006 pcszVersion = "1.12";
1007 break;
1008
1009 case SettingsVersion_v1_13:
1010 pcszVersion = "1.13";
1011 break;
1012
1013 case SettingsVersion_v1_14:
1014 pcszVersion = "1.14";
1015 break;
1016
1017 case SettingsVersion_v1_15:
1018 pcszVersion = "1.15";
1019 break;
1020
1021 case SettingsVersion_v1_16:
1022 pcszVersion = "1.16";
1023 break;
1024
1025 case SettingsVersion_v1_17:
1026 pcszVersion = "1.17";
1027 break;
1028
1029 default:
1030 // catch human error: the assertion below will trigger in debug
1031 // or dbgopt builds, so hopefully this will get noticed sooner in
1032 // the future, because it's easy to forget top update something.
1033 AssertMsg(m->sv <= SettingsVersion_v1_7, ("Settings.cpp: unexpected settings version %d, unhandled future version?\n", m->sv));
1034 // silently upgrade if this is less than 1.7 because that's the oldest we can write
1035 if (m->sv <= SettingsVersion_v1_7)
1036 {
1037 pcszVersion = "1.7";
1038 m->sv = SettingsVersion_v1_7;
1039 }
1040 else
1041 {
1042 // This is reached for SettingsVersion_Future and forgotten
1043 // settings version after SettingsVersion_v1_7, which should
1044 // not happen (see assertion above). Set the version to the
1045 // latest known version, to minimize loss of information, but
1046 // as we can't predict the future we have to use some format
1047 // we know, and latest should be the best choice. Note that
1048 // for "forgotten settings" this may not be the best choice,
1049 // but as it's an omission of someone who changed this file
1050 // it's the only generic possibility.
1051 pcszVersion = "1.17";
1052 m->sv = SettingsVersion_v1_17;
1053 }
1054 break;
1055 }
1056
1057 m->strSettingsVersionFull = Utf8StrFmt("%s-%s",
1058 pcszVersion,
1059 VBOX_XML_PLATFORM); // e.g. "linux"
1060 elm.setAttribute("version", m->strSettingsVersionFull);
1061}
1062
1063
1064/**
1065 * Creates a special backup file in case there is a version
1066 * bump, so that it is possible to go back to the previous
1067 * state. This is done only once (not for every settings
1068 * version bump), when the settings version is newer than
1069 * the version read from the config file. Must be called
1070 * before ConfigFileBase::createStubDocument, because that
1071 * method may alter information which this method needs.
1072 */
1073void ConfigFileBase::specialBackupIfFirstBump()
1074{
1075 // Since this gets called before the XML document is actually written out,
1076 // this is where we must check whether we're upgrading the settings version
1077 // and need to make a backup, so the user can go back to an earlier
1078 // VirtualBox version and recover his old settings files.
1079 if ( (m->svRead != SettingsVersion_Null) // old file exists?
1080 && (m->svRead < m->sv) // we're upgrading?
1081 )
1082 {
1083 // compose new filename: strip off trailing ".xml"/".vbox"
1084 Utf8Str strFilenameNew;
1085 Utf8Str strExt = ".xml";
1086 if (m->strFilename.endsWith(".xml"))
1087 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 4);
1088 else if (m->strFilename.endsWith(".vbox"))
1089 {
1090 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 5);
1091 strExt = ".vbox";
1092 }
1093
1094 // and append something like "-1.3-linux.xml"
1095 strFilenameNew.append("-");
1096 strFilenameNew.append(m->strSettingsVersionFull); // e.g. "1.3-linux"
1097 strFilenameNew.append(strExt); // .xml for main config, .vbox for machine config
1098
1099 // Copying the file cannot be avoided, as doing tricks with renaming
1100 // causes trouble on OS X with aliases (which follow the rename), and
1101 // on all platforms there is a risk of "losing" the VM config when
1102 // running out of space, as a rename here couldn't be rolled back.
1103 // Ignoring all errors besides running out of space is intentional, as
1104 // we don't want to do anything if the file already exists.
1105 int vrc = RTFileCopy(m->strFilename.c_str(), strFilenameNew.c_str());
1106 if (RT_UNLIKELY(vrc == VERR_DISK_FULL))
1107 throw ConfigFileError(this, NULL, N_("Cannot create settings backup file when upgrading to a newer settings format"));
1108
1109 // do this only once
1110 m->svRead = SettingsVersion_Null;
1111 }
1112}
1113
1114/**
1115 * Creates a new stub xml::Document in the m->pDoc member with the
1116 * root "VirtualBox" element set up. This is used by both
1117 * MainConfigFile and MachineConfigFile at the beginning of writing
1118 * out their XML.
1119 *
1120 * Before calling this, it is the responsibility of the caller to
1121 * set the "sv" member to the required settings version that is to
1122 * be written. For newly created files, the settings version will be
1123 * recent (1.12 or later if necessary); for files read in from disk
1124 * earlier, it will be the settings version indicated in the file.
1125 * However, this method will silently make sure that the settings
1126 * version is always at least 1.7 and change it if necessary, since
1127 * there is no write support for earlier settings versions.
1128 */
1129void ConfigFileBase::createStubDocument()
1130{
1131 Assert(m->pDoc == NULL);
1132 m->pDoc = new xml::Document;
1133
1134 m->pelmRoot = m->pDoc->createRootElement("VirtualBox",
1135 "\n"
1136 "** DO NOT EDIT THIS FILE.\n"
1137 "** If you make changes to this file while any VirtualBox related application\n"
1138 "** is running, your changes will be overwritten later, without taking effect.\n"
1139 "** Use VBoxManage or the VirtualBox Manager GUI to make changes.\n"
1140);
1141 m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
1142 // Have the code for producing a proper schema reference. Not used by most
1143 // tools, so don't bother doing it. The schema is not on the server anyway.
1144#ifdef VBOX_WITH_SETTINGS_SCHEMA
1145 m->pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1146 m->pelmRoot->setAttribute("xsi:schemaLocation", VBOX_XML_NAMESPACE " " VBOX_XML_SCHEMA);
1147#endif
1148
1149 // add settings version attribute to root element, update m->strSettingsVersionFull
1150 setVersionAttribute(*m->pelmRoot);
1151
1152 LogRel(("Saving settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
1153}
1154
1155/**
1156 * Creates an \<ExtraData\> node under the given parent element with
1157 * \<ExtraDataItem\> childern according to the contents of the given
1158 * map.
1159 *
1160 * This is in ConfigFileBase because it's used in both MainConfigFile
1161 * and MachineConfigFile, which both can have extradata.
1162 *
1163 * @param elmParent
1164 * @param me
1165 */
1166void ConfigFileBase::buildExtraData(xml::ElementNode &elmParent,
1167 const StringsMap &me)
1168{
1169 if (me.size())
1170 {
1171 xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
1172 for (StringsMap::const_iterator it = me.begin();
1173 it != me.end();
1174 ++it)
1175 {
1176 const Utf8Str &strName = it->first;
1177 const Utf8Str &strValue = it->second;
1178 xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
1179 pelmThis->setAttribute("name", strName);
1180 pelmThis->setAttribute("value", strValue);
1181 }
1182 }
1183}
1184
1185/**
1186 * Creates \<DeviceFilter\> nodes under the given parent element according to
1187 * the contents of the given USBDeviceFiltersList. This is in ConfigFileBase
1188 * because it's used in both MainConfigFile (for host filters) and
1189 * MachineConfigFile (for machine filters).
1190 *
1191 * If fHostMode is true, this means that we're supposed to write filters
1192 * for the IHost interface (respect "action", omit "strRemote" and
1193 * "ulMaskedInterfaces" in struct USBDeviceFilter).
1194 *
1195 * @param elmParent
1196 * @param ll
1197 * @param fHostMode
1198 */
1199void ConfigFileBase::buildUSBDeviceFilters(xml::ElementNode &elmParent,
1200 const USBDeviceFiltersList &ll,
1201 bool fHostMode)
1202{
1203 for (USBDeviceFiltersList::const_iterator it = ll.begin();
1204 it != ll.end();
1205 ++it)
1206 {
1207 const USBDeviceFilter &flt = *it;
1208 xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
1209 pelmFilter->setAttribute("name", flt.strName);
1210 pelmFilter->setAttribute("active", flt.fActive);
1211 if (flt.strVendorId.length())
1212 pelmFilter->setAttribute("vendorId", flt.strVendorId);
1213 if (flt.strProductId.length())
1214 pelmFilter->setAttribute("productId", flt.strProductId);
1215 if (flt.strRevision.length())
1216 pelmFilter->setAttribute("revision", flt.strRevision);
1217 if (flt.strManufacturer.length())
1218 pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
1219 if (flt.strProduct.length())
1220 pelmFilter->setAttribute("product", flt.strProduct);
1221 if (flt.strSerialNumber.length())
1222 pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
1223 if (flt.strPort.length())
1224 pelmFilter->setAttribute("port", flt.strPort);
1225
1226 if (fHostMode)
1227 {
1228 const char *pcsz =
1229 (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
1230 : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
1231 pelmFilter->setAttribute("action", pcsz);
1232 }
1233 else
1234 {
1235 if (flt.strRemote.length())
1236 pelmFilter->setAttribute("remote", flt.strRemote);
1237 if (flt.ulMaskedInterfaces)
1238 pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
1239 }
1240 }
1241}
1242
1243/**
1244 * Creates a single \<HardDisk\> element for the given Medium structure
1245 * and recurses to write the child hard disks underneath. Called from
1246 * MainConfigFile::write().
1247 *
1248 * @param t
1249 * @param depth
1250 * @param elmMedium
1251 * @param mdm
1252 */
1253void ConfigFileBase::buildMedium(MediaType t,
1254 uint32_t depth,
1255 xml::ElementNode &elmMedium,
1256 const Medium &mdm)
1257{
1258 if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
1259 throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
1260
1261 xml::ElementNode *pelmMedium;
1262
1263 if (t == HardDisk)
1264 pelmMedium = elmMedium.createChild("HardDisk");
1265 else
1266 pelmMedium = elmMedium.createChild("Image");
1267
1268 pelmMedium->setAttribute("uuid", mdm.uuid.toStringCurly());
1269
1270 pelmMedium->setAttributePath("location", mdm.strLocation);
1271
1272 if (t == HardDisk || RTStrICmp(mdm.strFormat.c_str(), "RAW"))
1273 pelmMedium->setAttribute("format", mdm.strFormat);
1274 if ( t == HardDisk
1275 && mdm.fAutoReset)
1276 pelmMedium->setAttribute("autoReset", mdm.fAutoReset);
1277 if (mdm.strDescription.length())
1278 pelmMedium->createChild("Description")->addContent(mdm.strDescription);
1279
1280 for (StringsMap::const_iterator it = mdm.properties.begin();
1281 it != mdm.properties.end();
1282 ++it)
1283 {
1284 xml::ElementNode *pelmProp = pelmMedium->createChild("Property");
1285 pelmProp->setAttribute("name", it->first);
1286 pelmProp->setAttribute("value", it->second);
1287 }
1288
1289 // only for base hard disks, save the type
1290 if (depth == 1)
1291 {
1292 // no need to save the usual DVD/floppy medium types
1293 if ( ( t != DVDImage
1294 || ( mdm.hdType != MediumType_Writethrough // shouldn't happen
1295 && mdm.hdType != MediumType_Readonly))
1296 && ( t != FloppyImage
1297 || mdm.hdType != MediumType_Writethrough))
1298 {
1299 const char *pcszType =
1300 mdm.hdType == MediumType_Normal ? "Normal" :
1301 mdm.hdType == MediumType_Immutable ? "Immutable" :
1302 mdm.hdType == MediumType_Writethrough ? "Writethrough" :
1303 mdm.hdType == MediumType_Shareable ? "Shareable" :
1304 mdm.hdType == MediumType_Readonly ? "Readonly" :
1305 mdm.hdType == MediumType_MultiAttach ? "MultiAttach" :
1306 "INVALID";
1307 pelmMedium->setAttribute("type", pcszType);
1308 }
1309 }
1310
1311 for (MediaList::const_iterator it = mdm.llChildren.begin();
1312 it != mdm.llChildren.end();
1313 ++it)
1314 {
1315 // recurse for children
1316 buildMedium(t, // device type
1317 depth + 1, // depth
1318 *pelmMedium, // parent
1319 *it); // settings::Medium
1320 }
1321}
1322
1323/**
1324 * Creates a \<MediaRegistry\> node under the given parent and writes out all
1325 * hard disks and DVD and floppy images from the lists in the given MediaRegistry
1326 * structure under it.
1327 *
1328 * This is used in both MainConfigFile and MachineConfigFile since starting with
1329 * VirtualBox 4.0, we can have media registries in both.
1330 *
1331 * @param elmParent
1332 * @param mr
1333 */
1334void ConfigFileBase::buildMediaRegistry(xml::ElementNode &elmParent,
1335 const MediaRegistry &mr)
1336{
1337 if (mr.llHardDisks.size() == 0 && mr.llDvdImages.size() == 0 && mr.llFloppyImages.size() == 0)
1338 return;
1339
1340 xml::ElementNode *pelmMediaRegistry = elmParent.createChild("MediaRegistry");
1341
1342 if (mr.llHardDisks.size())
1343 {
1344 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
1345 for (MediaList::const_iterator it = mr.llHardDisks.begin();
1346 it != mr.llHardDisks.end();
1347 ++it)
1348 {
1349 buildMedium(HardDisk, 1, *pelmHardDisks, *it);
1350 }
1351 }
1352
1353 if (mr.llDvdImages.size())
1354 {
1355 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
1356 for (MediaList::const_iterator it = mr.llDvdImages.begin();
1357 it != mr.llDvdImages.end();
1358 ++it)
1359 {
1360 buildMedium(DVDImage, 1, *pelmDVDImages, *it);
1361 }
1362 }
1363
1364 if (mr.llFloppyImages.size())
1365 {
1366 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
1367 for (MediaList::const_iterator it = mr.llFloppyImages.begin();
1368 it != mr.llFloppyImages.end();
1369 ++it)
1370 {
1371 buildMedium(FloppyImage, 1, *pelmFloppyImages, *it);
1372 }
1373 }
1374}
1375
1376/**
1377 * Serialize NAT port-forwarding rules in parent container.
1378 * Note: it's responsibility of caller to create parent of the list tag.
1379 * because this method used for serializing per-_mahine's_adapter_ and per-network approaches.
1380 */
1381void ConfigFileBase::buildNATForwardRulesMap(xml::ElementNode &elmParent, const NATRulesMap &mapRules)
1382{
1383 for (NATRulesMap::const_iterator r = mapRules.begin();
1384 r != mapRules.end(); ++r)
1385 {
1386 xml::ElementNode *pelmPF;
1387 pelmPF = elmParent.createChild("Forwarding");
1388 const NATRule &nr = r->second;
1389 if (nr.strName.length())
1390 pelmPF->setAttribute("name", nr.strName);
1391 pelmPF->setAttribute("proto", nr.proto);
1392 if (nr.strHostIP.length())
1393 pelmPF->setAttribute("hostip", nr.strHostIP);
1394 if (nr.u16HostPort)
1395 pelmPF->setAttribute("hostport", nr.u16HostPort);
1396 if (nr.strGuestIP.length())
1397 pelmPF->setAttribute("guestip", nr.strGuestIP);
1398 if (nr.u16GuestPort)
1399 pelmPF->setAttribute("guestport", nr.u16GuestPort);
1400 }
1401}
1402
1403
1404void ConfigFileBase::buildNATLoopbacks(xml::ElementNode &elmParent, const NATLoopbackOffsetList &natLoopbackOffsetList)
1405{
1406 for (NATLoopbackOffsetList::const_iterator lo = natLoopbackOffsetList.begin();
1407 lo != natLoopbackOffsetList.end(); ++lo)
1408 {
1409 xml::ElementNode *pelmLo;
1410 pelmLo = elmParent.createChild("Loopback4");
1411 pelmLo->setAttribute("address", (*lo).strLoopbackHostAddress);
1412 pelmLo->setAttribute("offset", (*lo).u32Offset);
1413 }
1414}
1415
1416/**
1417 * Cleans up memory allocated by the internal XML parser. To be called by
1418 * descendant classes when they're done analyzing the DOM tree to discard it.
1419 */
1420void ConfigFileBase::clearDocument()
1421{
1422 m->cleanup();
1423}
1424
1425/**
1426 * Returns true only if the underlying config file exists on disk;
1427 * either because the file has been loaded from disk, or it's been written
1428 * to disk, or both.
1429 * @return
1430 */
1431bool ConfigFileBase::fileExists()
1432{
1433 return m->fFileExists;
1434}
1435
1436/**
1437 * Copies the base variables from another instance. Used by Machine::saveSettings
1438 * so that the settings version does not get lost when a copy of the Machine settings
1439 * file is made to see if settings have actually changed.
1440 * @param b
1441 */
1442void ConfigFileBase::copyBaseFrom(const ConfigFileBase &b)
1443{
1444 m->copyFrom(*b.m);
1445}
1446
1447////////////////////////////////////////////////////////////////////////////////
1448//
1449// Structures shared between Machine XML and VirtualBox.xml
1450//
1451////////////////////////////////////////////////////////////////////////////////
1452
1453
1454/**
1455 * Constructor. Needs to set sane defaults which stand the test of time.
1456 */
1457USBDeviceFilter::USBDeviceFilter() :
1458 fActive(false),
1459 action(USBDeviceFilterAction_Null),
1460 ulMaskedInterfaces(0)
1461{
1462}
1463
1464/**
1465 * Comparison operator. This gets called from MachineConfigFile::operator==,
1466 * which in turn gets called from Machine::saveSettings to figure out whether
1467 * machine settings have really changed and thus need to be written out to disk.
1468 */
1469bool USBDeviceFilter::operator==(const USBDeviceFilter &u) const
1470{
1471 return (this == &u)
1472 || ( strName == u.strName
1473 && fActive == u.fActive
1474 && strVendorId == u.strVendorId
1475 && strProductId == u.strProductId
1476 && strRevision == u.strRevision
1477 && strManufacturer == u.strManufacturer
1478 && strProduct == u.strProduct
1479 && strSerialNumber == u.strSerialNumber
1480 && strPort == u.strPort
1481 && action == u.action
1482 && strRemote == u.strRemote
1483 && ulMaskedInterfaces == u.ulMaskedInterfaces);
1484}
1485
1486/**
1487 * Constructor. Needs to set sane defaults which stand the test of time.
1488 */
1489settings::Medium::Medium() :
1490 fAutoReset(false),
1491 hdType(MediumType_Normal)
1492{
1493}
1494
1495/**
1496 * Comparison operator. This gets called from MachineConfigFile::operator==,
1497 * which in turn gets called from Machine::saveSettings to figure out whether
1498 * machine settings have really changed and thus need to be written out to disk.
1499 */
1500bool settings::Medium::operator==(const settings::Medium &m) const
1501{
1502 return (this == &m)
1503 || ( uuid == m.uuid
1504 && strLocation == m.strLocation
1505 && strDescription == m.strDescription
1506 && strFormat == m.strFormat
1507 && fAutoReset == m.fAutoReset
1508 && properties == m.properties
1509 && hdType == m.hdType
1510 && llChildren == m.llChildren); // this is deep and recurses
1511}
1512
1513const struct settings::Medium settings::Medium::Empty; /* default ctor is OK */
1514
1515/**
1516 * Comparison operator. This gets called from MachineConfigFile::operator==,
1517 * which in turn gets called from Machine::saveSettings to figure out whether
1518 * machine settings have really changed and thus need to be written out to disk.
1519 */
1520bool MediaRegistry::operator==(const MediaRegistry &m) const
1521{
1522 return (this == &m)
1523 || ( llHardDisks == m.llHardDisks
1524 && llDvdImages == m.llDvdImages
1525 && llFloppyImages == m.llFloppyImages);
1526}
1527
1528/**
1529 * Constructor. Needs to set sane defaults which stand the test of time.
1530 */
1531NATRule::NATRule() :
1532 proto(NATProtocol_TCP),
1533 u16HostPort(0),
1534 u16GuestPort(0)
1535{
1536}
1537
1538/**
1539 * Comparison operator. This gets called from MachineConfigFile::operator==,
1540 * which in turn gets called from Machine::saveSettings to figure out whether
1541 * machine settings have really changed and thus need to be written out to disk.
1542 */
1543bool NATRule::operator==(const NATRule &r) const
1544{
1545 return (this == &r)
1546 || ( strName == r.strName
1547 && proto == r.proto
1548 && u16HostPort == r.u16HostPort
1549 && strHostIP == r.strHostIP
1550 && u16GuestPort == r.u16GuestPort
1551 && strGuestIP == r.strGuestIP);
1552}
1553
1554/**
1555 * Constructor. Needs to set sane defaults which stand the test of time.
1556 */
1557NATHostLoopbackOffset::NATHostLoopbackOffset() :
1558 u32Offset(0)
1559{
1560}
1561
1562/**
1563 * Comparison operator. This gets called from MachineConfigFile::operator==,
1564 * which in turn gets called from Machine::saveSettings to figure out whether
1565 * machine settings have really changed and thus need to be written out to disk.
1566 */
1567bool NATHostLoopbackOffset::operator==(const NATHostLoopbackOffset &o) const
1568{
1569 return (this == &o)
1570 || ( strLoopbackHostAddress == o.strLoopbackHostAddress
1571 && u32Offset == o.u32Offset);
1572}
1573
1574
1575////////////////////////////////////////////////////////////////////////////////
1576//
1577// VirtualBox.xml structures
1578//
1579////////////////////////////////////////////////////////////////////////////////
1580
1581/**
1582 * Constructor. Needs to set sane defaults which stand the test of time.
1583 */
1584SystemProperties::SystemProperties()
1585 : uProxyMode(ProxyMode_System)
1586 , uLogHistoryCount(3)
1587 , fExclusiveHwVirt(true)
1588{
1589#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS)
1590 fExclusiveHwVirt = false;
1591#endif
1592}
1593
1594/**
1595 * Constructor. Needs to set sane defaults which stand the test of time.
1596 */
1597DhcpOptValue::DhcpOptValue() :
1598 text(),
1599 encoding(DhcpOptEncoding_Legacy)
1600{
1601}
1602
1603/**
1604 * Non-standard constructor.
1605 */
1606DhcpOptValue::DhcpOptValue(const com::Utf8Str &aText, DhcpOptEncoding_T aEncoding) :
1607 text(aText),
1608 encoding(aEncoding)
1609{
1610}
1611
1612/**
1613 * Non-standard constructor.
1614 */
1615VmNameSlotKey::VmNameSlotKey(const com::Utf8Str& aVmName, LONG aSlot) :
1616 VmName(aVmName),
1617 Slot(aSlot)
1618{
1619}
1620
1621/**
1622 * Non-standard comparison operator.
1623 */
1624bool VmNameSlotKey::operator< (const VmNameSlotKey& that) const
1625{
1626 if (VmName == that.VmName)
1627 return Slot < that.Slot;
1628 else
1629 return VmName < that.VmName;
1630}
1631
1632/**
1633 * Constructor. Needs to set sane defaults which stand the test of time.
1634 */
1635DHCPServer::DHCPServer() :
1636 fEnabled(false)
1637{
1638}
1639
1640/**
1641 * Constructor. Needs to set sane defaults which stand the test of time.
1642 */
1643NATNetwork::NATNetwork() :
1644 fEnabled(true),
1645 fIPv6Enabled(false),
1646 fAdvertiseDefaultIPv6Route(false),
1647 fNeedDhcpServer(true),
1648 u32HostLoopback6Offset(0)
1649{
1650}
1651
1652
1653
1654////////////////////////////////////////////////////////////////////////////////
1655//
1656// MainConfigFile
1657//
1658////////////////////////////////////////////////////////////////////////////////
1659
1660/**
1661 * Reads one \<MachineEntry\> from the main VirtualBox.xml file.
1662 * @param elmMachineRegistry
1663 */
1664void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
1665{
1666 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
1667 xml::NodesLoop nl1(elmMachineRegistry);
1668 const xml::ElementNode *pelmChild1;
1669 while ((pelmChild1 = nl1.forAllNodes()))
1670 {
1671 if (pelmChild1->nameEquals("MachineEntry"))
1672 {
1673 MachineRegistryEntry mre;
1674 Utf8Str strUUID;
1675 if ( pelmChild1->getAttributeValue("uuid", strUUID)
1676 && pelmChild1->getAttributeValue("src", mre.strSettingsFile) )
1677 {
1678 parseUUID(mre.uuid, strUUID, pelmChild1);
1679 llMachines.push_back(mre);
1680 }
1681 else
1682 throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing"));
1683 }
1684 }
1685}
1686
1687/**
1688 * Reads in the \<DHCPServers\> chunk.
1689 * @param elmDHCPServers
1690 */
1691void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
1692{
1693 xml::NodesLoop nl1(elmDHCPServers);
1694 const xml::ElementNode *pelmServer;
1695 while ((pelmServer = nl1.forAllNodes()))
1696 {
1697 if (pelmServer->nameEquals("DHCPServer"))
1698 {
1699 DHCPServer srv;
1700 if ( pelmServer->getAttributeValue("networkName", srv.strNetworkName)
1701 && pelmServer->getAttributeValue("IPAddress", srv.strIPAddress)
1702 && pelmServer->getAttributeValue("networkMask", srv.GlobalDhcpOptions[DhcpOpt_SubnetMask].text)
1703 && pelmServer->getAttributeValue("lowerIP", srv.strIPLower)
1704 && pelmServer->getAttributeValue("upperIP", srv.strIPUpper)
1705 && pelmServer->getAttributeValue("enabled", srv.fEnabled) )
1706 {
1707 xml::NodesLoop nlOptions(*pelmServer, "Options");
1708 const xml::ElementNode *options;
1709 /* XXX: Options are in 1:1 relation to DHCPServer */
1710
1711 while ((options = nlOptions.forAllNodes()))
1712 {
1713 readDhcpOptions(srv.GlobalDhcpOptions, *options);
1714 } /* end of forall("Options") */
1715 xml::NodesLoop nlConfig(*pelmServer, "Config");
1716 const xml::ElementNode *cfg;
1717 while ((cfg = nlConfig.forAllNodes()))
1718 {
1719 com::Utf8Str strVmName;
1720 uint32_t u32Slot;
1721 cfg->getAttributeValue("vm-name", strVmName);
1722 cfg->getAttributeValue("slot", u32Slot);
1723 readDhcpOptions(srv.VmSlot2OptionsM[VmNameSlotKey(strVmName, u32Slot)], *cfg);
1724 }
1725 llDhcpServers.push_back(srv);
1726 }
1727 else
1728 throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing"));
1729 }
1730 }
1731}
1732
1733void MainConfigFile::readDhcpOptions(DhcpOptionMap& map,
1734 const xml::ElementNode& options)
1735{
1736 xml::NodesLoop nl2(options, "Option");
1737 const xml::ElementNode *opt;
1738 while ((opt = nl2.forAllNodes()))
1739 {
1740 DhcpOpt_T OptName;
1741 com::Utf8Str OptText;
1742 int32_t OptEnc = DhcpOptEncoding_Legacy;
1743
1744 opt->getAttributeValue("name", (uint32_t&)OptName);
1745
1746 if (OptName == DhcpOpt_SubnetMask)
1747 continue;
1748
1749 opt->getAttributeValue("value", OptText);
1750 opt->getAttributeValue("encoding", OptEnc);
1751
1752 map[OptName] = DhcpOptValue(OptText, (DhcpOptEncoding_T)OptEnc);
1753 } /* end of forall("Option") */
1754
1755}
1756
1757/**
1758 * Reads in the \<NATNetworks\> chunk.
1759 * @param elmNATNetworks
1760 */
1761void MainConfigFile::readNATNetworks(const xml::ElementNode &elmNATNetworks)
1762{
1763 xml::NodesLoop nl1(elmNATNetworks);
1764 const xml::ElementNode *pelmNet;
1765 while ((pelmNet = nl1.forAllNodes()))
1766 {
1767 if (pelmNet->nameEquals("NATNetwork"))
1768 {
1769 NATNetwork net;
1770 if ( pelmNet->getAttributeValue("networkName", net.strNetworkName)
1771 && pelmNet->getAttributeValue("enabled", net.fEnabled)
1772 && pelmNet->getAttributeValue("network", net.strIPv4NetworkCidr)
1773 && pelmNet->getAttributeValue("ipv6", net.fIPv6Enabled)
1774 && pelmNet->getAttributeValue("ipv6prefix", net.strIPv6Prefix)
1775 && pelmNet->getAttributeValue("advertiseDefaultIPv6Route", net.fAdvertiseDefaultIPv6Route)
1776 && pelmNet->getAttributeValue("needDhcp", net.fNeedDhcpServer) )
1777 {
1778 pelmNet->getAttributeValue("loopback6", net.u32HostLoopback6Offset);
1779 const xml::ElementNode *pelmMappings;
1780 if ((pelmMappings = pelmNet->findChildElement("Mappings")))
1781 readNATLoopbacks(*pelmMappings, net.llHostLoopbackOffsetList);
1782
1783 const xml::ElementNode *pelmPortForwardRules4;
1784 if ((pelmPortForwardRules4 = pelmNet->findChildElement("PortForwarding4")))
1785 readNATForwardRulesMap(*pelmPortForwardRules4,
1786 net.mapPortForwardRules4);
1787
1788 const xml::ElementNode *pelmPortForwardRules6;
1789 if ((pelmPortForwardRules6 = pelmNet->findChildElement("PortForwarding6")))
1790 readNATForwardRulesMap(*pelmPortForwardRules6,
1791 net.mapPortForwardRules6);
1792
1793 llNATNetworks.push_back(net);
1794 }
1795 else
1796 throw ConfigFileError(this, pelmNet, N_("Required NATNetwork/@networkName, @gateway, @network,@advertiseDefaultIpv6Route , @needDhcp or @enabled attribute is missing"));
1797 }
1798 }
1799}
1800
1801/**
1802 * Creates \<USBDeviceSource\> nodes under the given parent element according to
1803 * the contents of the given USBDeviceSourcesList.
1804 *
1805 * @param elmParent
1806 * @param ll
1807 */
1808void MainConfigFile::buildUSBDeviceSources(xml::ElementNode &elmParent,
1809 const USBDeviceSourcesList &ll)
1810{
1811 for (USBDeviceSourcesList::const_iterator it = ll.begin();
1812 it != ll.end();
1813 ++it)
1814 {
1815 const USBDeviceSource &src = *it;
1816 xml::ElementNode *pelmSource = elmParent.createChild("USBDeviceSource");
1817 pelmSource->setAttribute("name", src.strName);
1818 pelmSource->setAttribute("backend", src.strBackend);
1819 pelmSource->setAttribute("address", src.strAddress);
1820
1821 /* Write the properties. */
1822 for (StringsMap::const_iterator itProp = src.properties.begin();
1823 itProp != src.properties.end();
1824 ++itProp)
1825 {
1826 xml::ElementNode *pelmProp = pelmSource->createChild("Property");
1827 pelmProp->setAttribute("name", itProp->first);
1828 pelmProp->setAttribute("value", itProp->second);
1829 }
1830 }
1831}
1832
1833/**
1834 * Reads \<USBDeviceFilter\> entries from under the given elmDeviceFilters node and
1835 * stores them in the given linklist. This is in ConfigFileBase because it's used
1836 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
1837 * filters).
1838 * @param elmDeviceSources
1839 * @param ll
1840 */
1841void MainConfigFile::readUSBDeviceSources(const xml::ElementNode &elmDeviceSources,
1842 USBDeviceSourcesList &ll)
1843{
1844 xml::NodesLoop nl1(elmDeviceSources, "USBDeviceSource");
1845 const xml::ElementNode *pelmChild;
1846 while ((pelmChild = nl1.forAllNodes()))
1847 {
1848 USBDeviceSource src;
1849
1850 if ( pelmChild->getAttributeValue("name", src.strName)
1851 && pelmChild->getAttributeValue("backend", src.strBackend)
1852 && pelmChild->getAttributeValue("address", src.strAddress))
1853 {
1854 // handle medium properties
1855 xml::NodesLoop nl2(*pelmChild, "Property");
1856 const xml::ElementNode *pelmSrcChild;
1857 while ((pelmSrcChild = nl2.forAllNodes()))
1858 {
1859 Utf8Str strPropName, strPropValue;
1860 if ( pelmSrcChild->getAttributeValue("name", strPropName)
1861 && pelmSrcChild->getAttributeValue("value", strPropValue) )
1862 src.properties[strPropName] = strPropValue;
1863 else
1864 throw ConfigFileError(this, pelmSrcChild, N_("Required USBDeviceSource/Property/@name or @value attribute is missing"));
1865 }
1866
1867 ll.push_back(src);
1868 }
1869 }
1870}
1871
1872/**
1873 * Converts old style Proxy settings from ExtraData/UI section.
1874 *
1875 * Saves proxy settings directly to systemProperties structure.
1876 *
1877 * @returns true if conversion was successfull, false if not.
1878 * @param strUIProxySettings The GUI settings string to convert.
1879 */
1880bool MainConfigFile::convertGuiProxySettings(const com::Utf8Str &strUIProxySettings)
1881{
1882 /*
1883 * Possible variants:
1884 * - "ProxyAuto,proxyserver.url,1080,authDisabled,,"
1885 * - "ProxyDisabled,proxyserver.url,1080,authDisabled,,"
1886 * - "ProxyEnabled,proxyserver.url,1080,authDisabled,,"
1887 *
1888 * Note! We only need to bother with the first three fields as the last
1889 * three was never really used or ever actually passed to the HTTP
1890 * client code.
1891 */
1892 /* First field: The proxy mode. */
1893 const char *psz = RTStrStripL(strUIProxySettings.c_str());
1894 static const struct { const char *psz; size_t cch; ProxyMode_T enmMode; } s_aModes[] =
1895 {
1896 { RT_STR_TUPLE("ProxyAuto"), ProxyMode_System },
1897 { RT_STR_TUPLE("ProxyDisabled"), ProxyMode_NoProxy },
1898 { RT_STR_TUPLE("ProxyEnabled"), ProxyMode_Manual },
1899 };
1900 for (size_t i = 0; i < RT_ELEMENTS(s_aModes); i++)
1901 if (RTStrNICmpAscii(psz, s_aModes[i].psz, s_aModes[i].cch) == 0)
1902 {
1903 systemProperties.uProxyMode = s_aModes[i].enmMode;
1904 psz = RTStrStripL(psz + s_aModes[i].cch);
1905 if (*psz == ',')
1906 {
1907 /* Second field: The proxy host, possibly fully fledged proxy URL. */
1908 psz = RTStrStripL(psz + 1);
1909 if (*psz != '\0' && *psz != ',')
1910 {
1911 const char *pszEnd = strchr(psz, ',');
1912 size_t cchHost = pszEnd ? pszEnd - psz : strlen(psz);
1913 while (cchHost > 0 && RT_C_IS_SPACE(psz[cchHost - 1]))
1914 cchHost--;
1915 systemProperties.strProxyUrl.assign(psz, cchHost);
1916 if (systemProperties.strProxyUrl.find("://") == RTCString::npos)
1917 systemProperties.strProxyUrl.replace(0, 0, "http://");
1918
1919 /* Third field: The proxy port. Defaulted to 1080 for all proxies.
1920 The new settings has type specific default ports. */
1921 uint16_t uPort = 1080;
1922 if (pszEnd)
1923 {
1924 int rc = RTStrToUInt16Ex(RTStrStripL(pszEnd + 1), NULL, 10, &uPort);
1925 if (RT_FAILURE(rc))
1926 uPort = 1080;
1927 }
1928 RTURIPARSED Parsed;
1929 int rc = RTUriParse(systemProperties.strProxyUrl.c_str(), &Parsed);
1930 if (RT_SUCCESS(rc))
1931 {
1932 if (Parsed.uAuthorityPort == UINT32_MAX)
1933 systemProperties.strProxyUrl.appendPrintf(systemProperties.strProxyUrl.endsWith(":")
1934 ? "%u" : ":%u", uPort);
1935 }
1936 else
1937 {
1938 LogRelFunc(("Dropping invalid proxy URL for %u: %s\n",
1939 systemProperties.uProxyMode, systemProperties.strProxyUrl.c_str()));
1940 systemProperties.strProxyUrl.setNull();
1941 }
1942 }
1943 /* else: don't bother with the rest if we haven't got a host. */
1944 }
1945 if ( systemProperties.strProxyUrl.isEmpty()
1946 && systemProperties.uProxyMode == ProxyMode_Manual)
1947 {
1948 systemProperties.uProxyMode = ProxyMode_System;
1949 return false;
1950 }
1951 return true;
1952 }
1953 LogRelFunc(("Unknown proxy type: %s\n", psz));
1954 return false;
1955}
1956
1957/**
1958 * Constructor.
1959 *
1960 * If pstrFilename is != NULL, this reads the given settings file into the member
1961 * variables and various substructures and lists. Otherwise, the member variables
1962 * are initialized with default values.
1963 *
1964 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1965 * the caller should catch; if this constructor does not throw, then the member
1966 * variables contain meaningful values (either from the file or defaults).
1967 *
1968 * @param pstrFilename
1969 */
1970MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
1971 : ConfigFileBase(pstrFilename)
1972{
1973 if (pstrFilename)
1974 {
1975 // the ConfigFileBase constructor has loaded the XML file, so now
1976 // we need only analyze what is in there
1977 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1978 const xml::ElementNode *pelmRootChild;
1979 bool fCopyProxySettingsFromExtraData = false;
1980 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1981 {
1982 if (pelmRootChild->nameEquals("Global"))
1983 {
1984 xml::NodesLoop nlGlobalChildren(*pelmRootChild);
1985 const xml::ElementNode *pelmGlobalChild;
1986 while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
1987 {
1988 if (pelmGlobalChild->nameEquals("SystemProperties"))
1989 {
1990 pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1991 pelmGlobalChild->getAttributeValue("LoggingLevel", systemProperties.strLoggingLevel);
1992 pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1993 if (!pelmGlobalChild->getAttributeValue("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary))
1994 // pre-1.11 used @remoteDisplayAuthLibrary instead
1995 pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strVRDEAuthLibrary);
1996 pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1997 pelmGlobalChild->getAttributeValue("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
1998 pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.uLogHistoryCount);
1999 pelmGlobalChild->getAttributeValue("autostartDatabasePath", systemProperties.strAutostartDatabasePath);
2000 pelmGlobalChild->getAttributeValue("defaultFrontend", systemProperties.strDefaultFrontend);
2001 pelmGlobalChild->getAttributeValue("exclusiveHwVirt", systemProperties.fExclusiveHwVirt);
2002 if (!pelmGlobalChild->getAttributeValue("proxyMode", systemProperties.uProxyMode))
2003 fCopyProxySettingsFromExtraData = true;
2004 pelmGlobalChild->getAttributeValue("proxyUrl", systemProperties.strProxyUrl);
2005 }
2006 else if (pelmGlobalChild->nameEquals("ExtraData"))
2007 readExtraData(*pelmGlobalChild, mapExtraDataItems);
2008 else if (pelmGlobalChild->nameEquals("MachineRegistry"))
2009 readMachineRegistry(*pelmGlobalChild);
2010 else if ( (pelmGlobalChild->nameEquals("MediaRegistry"))
2011 || ( (m->sv < SettingsVersion_v1_4)
2012 && (pelmGlobalChild->nameEquals("DiskRegistry"))
2013 )
2014 )
2015 readMediaRegistry(*pelmGlobalChild, mediaRegistry);
2016 else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
2017 {
2018 xml::NodesLoop nlLevel4(*pelmGlobalChild);
2019 const xml::ElementNode *pelmLevel4Child;
2020 while ((pelmLevel4Child = nlLevel4.forAllNodes()))
2021 {
2022 if (pelmLevel4Child->nameEquals("DHCPServers"))
2023 readDHCPServers(*pelmLevel4Child);
2024 if (pelmLevel4Child->nameEquals("NATNetworks"))
2025 readNATNetworks(*pelmLevel4Child);
2026 }
2027 }
2028 else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
2029 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
2030 else if (pelmGlobalChild->nameEquals("USBDeviceSources"))
2031 readUSBDeviceSources(*pelmGlobalChild, host.llUSBDeviceSources);
2032 }
2033 } // end if (pelmRootChild->nameEquals("Global"))
2034 }
2035
2036 if (fCopyProxySettingsFromExtraData)
2037 for (StringsMap::const_iterator it = mapExtraDataItems.begin(); it != mapExtraDataItems.end(); ++it)
2038 if (it->first.equals("GUI/ProxySettings"))
2039 {
2040 convertGuiProxySettings(it->second);
2041 break;
2042 }
2043
2044 clearDocument();
2045 }
2046
2047 // DHCP servers were introduced with settings version 1.7; if we're loading
2048 // from an older version OR this is a fresh install, then add one DHCP server
2049 // with default settings
2050 if ( (!llDhcpServers.size())
2051 && ( (!pstrFilename) // empty VirtualBox.xml file
2052 || (m->sv < SettingsVersion_v1_7) // upgrading from before 1.7
2053 )
2054 )
2055 {
2056 DHCPServer srv;
2057 srv.strNetworkName =
2058#ifdef RT_OS_WINDOWS
2059 "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
2060#else
2061 "HostInterfaceNetworking-vboxnet0";
2062#endif
2063 srv.strIPAddress = "192.168.56.100";
2064 srv.GlobalDhcpOptions[DhcpOpt_SubnetMask] = DhcpOptValue("255.255.255.0");
2065 srv.strIPLower = "192.168.56.101";
2066 srv.strIPUpper = "192.168.56.254";
2067 srv.fEnabled = true;
2068 llDhcpServers.push_back(srv);
2069 }
2070}
2071
2072void MainConfigFile::bumpSettingsVersionIfNeeded()
2073{
2074 if (m->sv < SettingsVersion_v1_16)
2075 {
2076 // VirtualBox 5.1 add support for additional USB device sources.
2077 if (!host.llUSBDeviceSources.empty())
2078 m->sv = SettingsVersion_v1_16;
2079 }
2080
2081 if (m->sv < SettingsVersion_v1_14)
2082 {
2083 // VirtualBox 4.3 adds NAT networks.
2084 if ( !llNATNetworks.empty())
2085 m->sv = SettingsVersion_v1_14;
2086 }
2087}
2088
2089
2090/**
2091 * Called from the IVirtualBox interface to write out VirtualBox.xml. This
2092 * builds an XML DOM tree and writes it out to disk.
2093 */
2094void MainConfigFile::write(const com::Utf8Str strFilename)
2095{
2096 bumpSettingsVersionIfNeeded();
2097
2098 m->strFilename = strFilename;
2099 specialBackupIfFirstBump();
2100 createStubDocument();
2101
2102 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
2103
2104 buildExtraData(*pelmGlobal, mapExtraDataItems);
2105
2106 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
2107 for (MachinesRegistry::const_iterator it = llMachines.begin();
2108 it != llMachines.end();
2109 ++it)
2110 {
2111 // <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"/>
2112 const MachineRegistryEntry &mre = *it;
2113 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
2114 pelmMachineEntry->setAttribute("uuid", mre.uuid.toStringCurly());
2115 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
2116 }
2117
2118 buildMediaRegistry(*pelmGlobal, mediaRegistry);
2119
2120 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
2121 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
2122 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
2123 it != llDhcpServers.end();
2124 ++it)
2125 {
2126 const DHCPServer &d = *it;
2127 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
2128 DhcpOptConstIterator itOpt;
2129 itOpt = d.GlobalDhcpOptions.find(DhcpOpt_SubnetMask);
2130
2131 pelmThis->setAttribute("networkName", d.strNetworkName);
2132 pelmThis->setAttribute("IPAddress", d.strIPAddress);
2133 if (itOpt != d.GlobalDhcpOptions.end())
2134 pelmThis->setAttribute("networkMask", itOpt->second.text);
2135 pelmThis->setAttribute("lowerIP", d.strIPLower);
2136 pelmThis->setAttribute("upperIP", d.strIPUpper);
2137 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
2138 /* We assume that if there're only 1 element it means that */
2139 size_t cOpt = d.GlobalDhcpOptions.size();
2140 /* We don't want duplicate validation check of networkMask here*/
2141 if ( ( itOpt == d.GlobalDhcpOptions.end()
2142 && cOpt > 0)
2143 || cOpt > 1)
2144 {
2145 xml::ElementNode *pelmOptions = pelmThis->createChild("Options");
2146 for (itOpt = d.GlobalDhcpOptions.begin();
2147 itOpt != d.GlobalDhcpOptions.end();
2148 ++itOpt)
2149 {
2150 if (itOpt->first == DhcpOpt_SubnetMask)
2151 continue;
2152
2153 xml::ElementNode *pelmOpt = pelmOptions->createChild("Option");
2154
2155 if (!pelmOpt)
2156 break;
2157
2158 pelmOpt->setAttribute("name", itOpt->first);
2159 pelmOpt->setAttribute("value", itOpt->second.text);
2160 if (itOpt->second.encoding != DhcpOptEncoding_Legacy)
2161 pelmOpt->setAttribute("encoding", (int)itOpt->second.encoding);
2162 }
2163 } /* end of if */
2164
2165 if (d.VmSlot2OptionsM.size() > 0)
2166 {
2167 VmSlot2OptionsConstIterator itVmSlot;
2168 DhcpOptConstIterator itOpt1;
2169 for(itVmSlot = d.VmSlot2OptionsM.begin();
2170 itVmSlot != d.VmSlot2OptionsM.end();
2171 ++itVmSlot)
2172 {
2173 xml::ElementNode *pelmCfg = pelmThis->createChild("Config");
2174 pelmCfg->setAttribute("vm-name", itVmSlot->first.VmName);
2175 pelmCfg->setAttribute("slot", (int32_t)itVmSlot->first.Slot);
2176
2177 for (itOpt1 = itVmSlot->second.begin();
2178 itOpt1 != itVmSlot->second.end();
2179 ++itOpt1)
2180 {
2181 xml::ElementNode *pelmOpt = pelmCfg->createChild("Option");
2182 pelmOpt->setAttribute("name", itOpt1->first);
2183 pelmOpt->setAttribute("value", itOpt1->second.text);
2184 if (itOpt1->second.encoding != DhcpOptEncoding_Legacy)
2185 pelmOpt->setAttribute("encoding", (int)itOpt1->second.encoding);
2186 }
2187 }
2188 } /* and of if */
2189
2190 }
2191
2192 xml::ElementNode *pelmNATNetworks;
2193 /* don't create entry if no NAT networks are registered. */
2194 if (!llNATNetworks.empty())
2195 {
2196 pelmNATNetworks = pelmNetserviceRegistry->createChild("NATNetworks");
2197 for (NATNetworksList::const_iterator it = llNATNetworks.begin();
2198 it != llNATNetworks.end();
2199 ++it)
2200 {
2201 const NATNetwork &n = *it;
2202 xml::ElementNode *pelmThis = pelmNATNetworks->createChild("NATNetwork");
2203 pelmThis->setAttribute("networkName", n.strNetworkName);
2204 pelmThis->setAttribute("network", n.strIPv4NetworkCidr);
2205 pelmThis->setAttribute("ipv6", n.fIPv6Enabled ? 1 : 0);
2206 pelmThis->setAttribute("ipv6prefix", n.strIPv6Prefix);
2207 pelmThis->setAttribute("advertiseDefaultIPv6Route", (n.fAdvertiseDefaultIPv6Route)? 1 : 0);
2208 pelmThis->setAttribute("needDhcp", (n.fNeedDhcpServer) ? 1 : 0);
2209 pelmThis->setAttribute("enabled", (n.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
2210 if (n.mapPortForwardRules4.size())
2211 {
2212 xml::ElementNode *pelmPf4 = pelmThis->createChild("PortForwarding4");
2213 buildNATForwardRulesMap(*pelmPf4, n.mapPortForwardRules4);
2214 }
2215 if (n.mapPortForwardRules6.size())
2216 {
2217 xml::ElementNode *pelmPf6 = pelmThis->createChild("PortForwarding6");
2218 buildNATForwardRulesMap(*pelmPf6, n.mapPortForwardRules6);
2219 }
2220
2221 if (n.llHostLoopbackOffsetList.size())
2222 {
2223 xml::ElementNode *pelmMappings = pelmThis->createChild("Mappings");
2224 buildNATLoopbacks(*pelmMappings, n.llHostLoopbackOffsetList);
2225
2226 }
2227 }
2228 }
2229
2230
2231 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
2232 if (systemProperties.strDefaultMachineFolder.length())
2233 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
2234 if (systemProperties.strLoggingLevel.length())
2235 pelmSysProps->setAttribute("LoggingLevel", systemProperties.strLoggingLevel);
2236 if (systemProperties.strDefaultHardDiskFormat.length())
2237 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
2238 if (systemProperties.strVRDEAuthLibrary.length())
2239 pelmSysProps->setAttribute("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary);
2240 if (systemProperties.strWebServiceAuthLibrary.length())
2241 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
2242 if (systemProperties.strDefaultVRDEExtPack.length())
2243 pelmSysProps->setAttribute("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
2244 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.uLogHistoryCount);
2245 if (systemProperties.strAutostartDatabasePath.length())
2246 pelmSysProps->setAttribute("autostartDatabasePath", systemProperties.strAutostartDatabasePath);
2247 if (systemProperties.strDefaultFrontend.length())
2248 pelmSysProps->setAttribute("defaultFrontend", systemProperties.strDefaultFrontend);
2249 if (systemProperties.strProxyUrl.length())
2250 pelmSysProps->setAttribute("proxyUrl", systemProperties.strProxyUrl);
2251 pelmSysProps->setAttribute("proxyMode", systemProperties.uProxyMode);
2252 pelmSysProps->setAttribute("exclusiveHwVirt", systemProperties.fExclusiveHwVirt);
2253
2254 buildUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
2255 host.llUSBDeviceFilters,
2256 true); // fHostMode
2257
2258 if (!host.llUSBDeviceSources.empty())
2259 buildUSBDeviceSources(*pelmGlobal->createChild("USBDeviceSources"),
2260 host.llUSBDeviceSources);
2261
2262 // now go write the XML
2263 xml::XmlFileWriter writer(*m->pDoc);
2264 writer.write(m->strFilename.c_str(), true /*fSafe*/);
2265
2266 m->fFileExists = true;
2267
2268 clearDocument();
2269}
2270
2271////////////////////////////////////////////////////////////////////////////////
2272//
2273// Machine XML structures
2274//
2275////////////////////////////////////////////////////////////////////////////////
2276
2277/**
2278 * Constructor. Needs to set sane defaults which stand the test of time.
2279 */
2280VRDESettings::VRDESettings() :
2281 fEnabled(true), // default for old VMs, for new ones it's false
2282 authType(AuthType_Null),
2283 ulAuthTimeout(5000),
2284 fAllowMultiConnection(false),
2285 fReuseSingleConnection(false)
2286{
2287}
2288
2289/**
2290 * Check if all settings have default values.
2291 */
2292bool VRDESettings::areDefaultSettings(SettingsVersion_T sv) const
2293{
2294 return (sv < SettingsVersion_v1_16 ? fEnabled : !fEnabled)
2295 && authType == AuthType_Null
2296 && (ulAuthTimeout == 5000 || ulAuthTimeout == 0)
2297 && strAuthLibrary.isEmpty()
2298 && !fAllowMultiConnection
2299 && !fReuseSingleConnection
2300 && strVrdeExtPack.isEmpty()
2301 && mapProperties.size() == 0;
2302}
2303
2304/**
2305 * Comparison operator. This gets called from MachineConfigFile::operator==,
2306 * which in turn gets called from Machine::saveSettings to figure out whether
2307 * machine settings have really changed and thus need to be written out to disk.
2308 */
2309bool VRDESettings::operator==(const VRDESettings& v) const
2310{
2311 return (this == &v)
2312 || ( fEnabled == v.fEnabled
2313 && authType == v.authType
2314 && ulAuthTimeout == v.ulAuthTimeout
2315 && strAuthLibrary == v.strAuthLibrary
2316 && fAllowMultiConnection == v.fAllowMultiConnection
2317 && fReuseSingleConnection == v.fReuseSingleConnection
2318 && strVrdeExtPack == v.strVrdeExtPack
2319 && mapProperties == v.mapProperties);
2320}
2321
2322/**
2323 * Constructor. Needs to set sane defaults which stand the test of time.
2324 */
2325BIOSSettings::BIOSSettings() :
2326 fACPIEnabled(true),
2327 fIOAPICEnabled(false),
2328 fLogoFadeIn(true),
2329 fLogoFadeOut(true),
2330 fPXEDebugEnabled(false),
2331 ulLogoDisplayTime(0),
2332 biosBootMenuMode(BIOSBootMenuMode_MessageAndMenu),
2333 apicMode(APICMode_APIC),
2334 llTimeOffset(0)
2335{
2336}
2337
2338/**
2339 * Check if all settings have default values.
2340 */
2341bool BIOSSettings::areDefaultSettings() const
2342{
2343 return fACPIEnabled
2344 && !fIOAPICEnabled
2345 && fLogoFadeIn
2346 && fLogoFadeOut
2347 && !fPXEDebugEnabled
2348 && ulLogoDisplayTime == 0
2349 && biosBootMenuMode == BIOSBootMenuMode_MessageAndMenu
2350 && apicMode == APICMode_APIC
2351 && llTimeOffset == 0
2352 && strLogoImagePath.isEmpty();
2353}
2354
2355/**
2356 * Comparison operator. This gets called from MachineConfigFile::operator==,
2357 * which in turn gets called from Machine::saveSettings to figure out whether
2358 * machine settings have really changed and thus need to be written out to disk.
2359 */
2360bool BIOSSettings::operator==(const BIOSSettings &d) const
2361{
2362 return (this == &d)
2363 || ( fACPIEnabled == d.fACPIEnabled
2364 && fIOAPICEnabled == d.fIOAPICEnabled
2365 && fLogoFadeIn == d.fLogoFadeIn
2366 && fLogoFadeOut == d.fLogoFadeOut
2367 && fPXEDebugEnabled == d.fPXEDebugEnabled
2368 && ulLogoDisplayTime == d.ulLogoDisplayTime
2369 && biosBootMenuMode == d.biosBootMenuMode
2370 && apicMode == d.apicMode
2371 && llTimeOffset == d.llTimeOffset
2372 && strLogoImagePath == d.strLogoImagePath);
2373}
2374
2375RecordingScreenSettings::RecordingScreenSettings(void)
2376{
2377 applyDefaults();
2378}
2379
2380RecordingScreenSettings::~RecordingScreenSettings()
2381{
2382
2383}
2384
2385/**
2386 * Applies the default settings.
2387 */
2388void RecordingScreenSettings::applyDefaults(void)
2389{
2390 /*
2391 * Set sensible defaults.
2392 */
2393
2394 fEnabled = false;
2395 enmDest = RecordingDestination_File;
2396 ulMaxTimeS = 0;
2397 strOptions = "";
2398 File.ulMaxSizeMB = 0;
2399 File.strName = "";
2400 Video.enmCodec = RecordingVideoCodec_VP8;
2401 Video.ulWidth = 1024;
2402 Video.ulHeight = 768;
2403 Video.ulRate = 512;
2404 Video.ulFPS = 25;
2405 Audio.enmAudioCodec = RecordingAudioCodec_Opus;
2406 Audio.cBits = 16;
2407 Audio.cChannels = 2;
2408 Audio.uHz = 22050;
2409
2410 featureMap[RecordingFeature_Video] = true;
2411 featureMap[RecordingFeature_Audio] = false; /** @todo Audio is not yet enabled by default. */
2412}
2413
2414/**
2415 * Check if all settings have default values.
2416 */
2417bool RecordingScreenSettings::areDefaultSettings(void) const
2418{
2419 return fEnabled == false
2420 && enmDest == RecordingDestination_File
2421 && ulMaxTimeS == 0
2422 && strOptions == ""
2423 && File.ulMaxSizeMB == 0
2424 && File.strName == ""
2425 && Video.enmCodec == RecordingVideoCodec_VP8
2426 && Video.ulWidth == 1024
2427 && Video.ulHeight == 768
2428 && Video.ulRate == 512
2429 && Video.ulFPS == 25
2430 && Audio.enmAudioCodec == RecordingAudioCodec_Opus
2431 && Audio.cBits == 16
2432 && Audio.cChannels == 2
2433 && Audio.uHz == 22050;
2434}
2435
2436/**
2437 * Returns if a certain recording feature is enabled or not.
2438 *
2439 * @returns \c true if the feature is enabled, \c false if not.
2440 * @param enmFeature Feature to check.
2441 */
2442bool RecordingScreenSettings::isFeatureEnabled(RecordingFeature_T enmFeature) const
2443{
2444 RecordingFeatureMap::const_iterator itFeature = featureMap.find(enmFeature);
2445 if (itFeature != featureMap.end())
2446 return itFeature->second;
2447
2448 return false;
2449}
2450
2451/**
2452 * Comparison operator. This gets called from MachineConfigFile::operator==,
2453 * which in turn gets called from Machine::saveSettings to figure out whether
2454 * machine settings have really changed and thus need to be written out to disk.
2455 */
2456bool RecordingScreenSettings::operator==(const RecordingScreenSettings &d) const
2457{
2458 return fEnabled == d.fEnabled
2459 && enmDest == d.enmDest
2460 && featureMap == d.featureMap
2461 && ulMaxTimeS == d.ulMaxTimeS
2462 && strOptions == d.strOptions
2463 && File.ulMaxSizeMB == d.File.ulMaxSizeMB
2464 && Video.enmCodec == d.Video.enmCodec
2465 && Video.ulWidth == d.Video.ulWidth
2466 && Video.ulHeight == d.Video.ulHeight
2467 && Video.ulRate == d.Video.ulRate
2468 && Video.ulFPS == d.Video.ulFPS
2469 && Audio.enmAudioCodec == d.Audio.enmAudioCodec
2470 && Audio.cBits == d.Audio.cBits
2471 && Audio.cChannels == d.Audio.cChannels
2472 && Audio.uHz == d.Audio.uHz;
2473}
2474
2475/**
2476 * Constructor. Needs to set sane defaults which stand the test of time.
2477 */
2478RecordingSettings::RecordingSettings()
2479{
2480 applyDefaults();
2481}
2482
2483/**
2484 * Applies the default settings.
2485 */
2486void RecordingSettings::applyDefaults(void)
2487{
2488 fEnabled = false;
2489
2490 mapScreens.clear();
2491
2492 try
2493 {
2494 /* Always add screen 0 to the default configuration. */
2495 RecordingScreenSettings screenSettings; /* Apply default settings for screen 0. */
2496 screenSettings.fEnabled = true; /* Enabled by default. */
2497 mapScreens[0] = screenSettings;
2498 }
2499 catch (std::bad_alloc &)
2500 {
2501 AssertFailed();
2502 }
2503}
2504
2505/**
2506 * Check if all settings have default values.
2507 */
2508bool RecordingSettings::areDefaultSettings() const
2509{
2510 const bool fDefault = fEnabled == false
2511 && mapScreens.size() == 1;
2512 if (!fDefault)
2513 return false;
2514
2515 RecordingScreenMap::const_iterator itScreen = mapScreens.begin();
2516 return itScreen->first == 0
2517 && itScreen->second.areDefaultSettings();
2518}
2519
2520/**
2521 * Comparison operator. This gets called from MachineConfigFile::operator==,
2522 * which in turn gets called from Machine::saveSettings to figure out whether
2523 * machine settings have really changed and thus need to be written out to disk.
2524 */
2525bool RecordingSettings::operator==(const RecordingSettings &d) const
2526{
2527 if (this == &d)
2528 return true;
2529
2530 if ( fEnabled != d.fEnabled
2531 || mapScreens.size() != d.mapScreens.size())
2532 return false;
2533
2534 RecordingScreenMap::const_iterator itScreen = mapScreens.begin();
2535 uint32_t i = 0;
2536 while (itScreen != mapScreens.end())
2537 {
2538 RecordingScreenMap::const_iterator itScreenThat = d.mapScreens.find(i);
2539 if (itScreen->second == itScreenThat->second)
2540 {
2541 /* Nothing to do in here (yet). */
2542 }
2543 else
2544 return false;
2545
2546 ++itScreen;
2547 ++i;
2548 }
2549
2550 return true;
2551}
2552
2553/**
2554 * Constructor. Needs to set sane defaults which stand the test of time.
2555 */
2556USBController::USBController() :
2557 enmType(USBControllerType_Null)
2558{
2559}
2560
2561/**
2562 * Comparison operator. This gets called from MachineConfigFile::operator==,
2563 * which in turn gets called from Machine::saveSettings to figure out whether
2564 * machine settings have really changed and thus need to be written out to disk.
2565 */
2566bool USBController::operator==(const USBController &u) const
2567{
2568 return (this == &u)
2569 || ( strName == u.strName
2570 && enmType == u.enmType);
2571}
2572
2573/**
2574 * Constructor. Needs to set sane defaults which stand the test of time.
2575 */
2576USB::USB()
2577{
2578}
2579
2580/**
2581 * Comparison operator. This gets called from MachineConfigFile::operator==,
2582 * which in turn gets called from Machine::saveSettings to figure out whether
2583 * machine settings have really changed and thus need to be written out to disk.
2584 */
2585bool USB::operator==(const USB &u) const
2586{
2587 return (this == &u)
2588 || ( llUSBControllers == u.llUSBControllers
2589 && llDeviceFilters == u.llDeviceFilters);
2590}
2591
2592/**
2593 * Constructor. Needs to set sane defaults which stand the test of time.
2594 */
2595NAT::NAT() :
2596 u32Mtu(0),
2597 u32SockRcv(0),
2598 u32SockSnd(0),
2599 u32TcpRcv(0),
2600 u32TcpSnd(0),
2601 fDNSPassDomain(true), /* historically this value is true */
2602 fDNSProxy(false),
2603 fDNSUseHostResolver(false),
2604 fAliasLog(false),
2605 fAliasProxyOnly(false),
2606 fAliasUseSamePorts(false)
2607{
2608}
2609
2610/**
2611 * Check if all DNS settings have default values.
2612 */
2613bool NAT::areDNSDefaultSettings() const
2614{
2615 return fDNSPassDomain && !fDNSProxy && !fDNSUseHostResolver;
2616}
2617
2618/**
2619 * Check if all Alias settings have default values.
2620 */
2621bool NAT::areAliasDefaultSettings() const
2622{
2623 return !fAliasLog && !fAliasProxyOnly && !fAliasUseSamePorts;
2624}
2625
2626/**
2627 * Check if all TFTP settings have default values.
2628 */
2629bool NAT::areTFTPDefaultSettings() const
2630{
2631 return strTFTPPrefix.isEmpty()
2632 && strTFTPBootFile.isEmpty()
2633 && strTFTPNextServer.isEmpty();
2634}
2635
2636/**
2637 * Check if all settings have default values.
2638 */
2639bool NAT::areDefaultSettings() const
2640{
2641 return strNetwork.isEmpty()
2642 && strBindIP.isEmpty()
2643 && u32Mtu == 0
2644 && u32SockRcv == 0
2645 && u32SockSnd == 0
2646 && u32TcpRcv == 0
2647 && u32TcpSnd == 0
2648 && areDNSDefaultSettings()
2649 && areAliasDefaultSettings()
2650 && areTFTPDefaultSettings()
2651 && mapRules.size() == 0;
2652}
2653
2654/**
2655 * Comparison operator. This gets called from MachineConfigFile::operator==,
2656 * which in turn gets called from Machine::saveSettings to figure out whether
2657 * machine settings have really changed and thus need to be written out to disk.
2658 */
2659bool NAT::operator==(const NAT &n) const
2660{
2661 return (this == &n)
2662 || ( strNetwork == n.strNetwork
2663 && strBindIP == n.strBindIP
2664 && u32Mtu == n.u32Mtu
2665 && u32SockRcv == n.u32SockRcv
2666 && u32SockSnd == n.u32SockSnd
2667 && u32TcpSnd == n.u32TcpSnd
2668 && u32TcpRcv == n.u32TcpRcv
2669 && strTFTPPrefix == n.strTFTPPrefix
2670 && strTFTPBootFile == n.strTFTPBootFile
2671 && strTFTPNextServer == n.strTFTPNextServer
2672 && fDNSPassDomain == n.fDNSPassDomain
2673 && fDNSProxy == n.fDNSProxy
2674 && fDNSUseHostResolver == n.fDNSUseHostResolver
2675 && fAliasLog == n.fAliasLog
2676 && fAliasProxyOnly == n.fAliasProxyOnly
2677 && fAliasUseSamePorts == n.fAliasUseSamePorts
2678 && mapRules == n.mapRules);
2679}
2680
2681/**
2682 * Constructor. Needs to set sane defaults which stand the test of time.
2683 */
2684NetworkAdapter::NetworkAdapter() :
2685 ulSlot(0),
2686 type(NetworkAdapterType_Am79C970A), // default for old VMs, for new ones it's Am79C973
2687 fEnabled(false),
2688 fCableConnected(false), // default for old VMs, for new ones it's true
2689 ulLineSpeed(0),
2690 enmPromiscModePolicy(NetworkAdapterPromiscModePolicy_Deny),
2691 fTraceEnabled(false),
2692 mode(NetworkAttachmentType_Null),
2693 ulBootPriority(0)
2694{
2695}
2696
2697/**
2698 * Check if all Generic Driver settings have default values.
2699 */
2700bool NetworkAdapter::areGenericDriverDefaultSettings() const
2701{
2702 return strGenericDriver.isEmpty()
2703 && genericProperties.size() == 0;
2704}
2705
2706/**
2707 * Check if all settings have default values.
2708 */
2709bool NetworkAdapter::areDefaultSettings(SettingsVersion_T sv) const
2710{
2711 // 5.0 and earlier had a default of fCableConnected=false, which doesn't
2712 // make a lot of sense (but it's a fact). Later versions don't save the
2713 // setting if it's at the default value and thus must get it right.
2714 return !fEnabled
2715 && strMACAddress.isEmpty()
2716 && ( (sv >= SettingsVersion_v1_16 && fCableConnected && type == NetworkAdapterType_Am79C973)
2717 || (sv < SettingsVersion_v1_16 && !fCableConnected && type == NetworkAdapterType_Am79C970A))
2718 && ulLineSpeed == 0
2719 && enmPromiscModePolicy == NetworkAdapterPromiscModePolicy_Deny
2720 && mode == NetworkAttachmentType_Null
2721 && nat.areDefaultSettings()
2722 && strBridgedName.isEmpty()
2723 && strInternalNetworkName.isEmpty()
2724 && strHostOnlyName.isEmpty()
2725 && areGenericDriverDefaultSettings()
2726 && strNATNetworkName.isEmpty();
2727}
2728
2729/**
2730 * Special check if settings of the non-current attachment type have default values.
2731 */
2732bool NetworkAdapter::areDisabledDefaultSettings() const
2733{
2734 return (mode != NetworkAttachmentType_NAT ? nat.areDefaultSettings() : true)
2735 && (mode != NetworkAttachmentType_Bridged ? strBridgedName.isEmpty() : true)
2736 && (mode != NetworkAttachmentType_Internal ? strInternalNetworkName.isEmpty() : true)
2737 && (mode != NetworkAttachmentType_HostOnly ? strHostOnlyName.isEmpty() : true)
2738 && (mode != NetworkAttachmentType_Generic ? areGenericDriverDefaultSettings() : true)
2739 && (mode != NetworkAttachmentType_NATNetwork ? strNATNetworkName.isEmpty() : true);
2740}
2741
2742/**
2743 * Comparison operator. This gets called from MachineConfigFile::operator==,
2744 * which in turn gets called from Machine::saveSettings to figure out whether
2745 * machine settings have really changed and thus need to be written out to disk.
2746 */
2747bool NetworkAdapter::operator==(const NetworkAdapter &n) const
2748{
2749 return (this == &n)
2750 || ( ulSlot == n.ulSlot
2751 && type == n.type
2752 && fEnabled == n.fEnabled
2753 && strMACAddress == n.strMACAddress
2754 && fCableConnected == n.fCableConnected
2755 && ulLineSpeed == n.ulLineSpeed
2756 && enmPromiscModePolicy == n.enmPromiscModePolicy
2757 && fTraceEnabled == n.fTraceEnabled
2758 && strTraceFile == n.strTraceFile
2759 && mode == n.mode
2760 && nat == n.nat
2761 && strBridgedName == n.strBridgedName
2762 && strHostOnlyName == n.strHostOnlyName
2763 && strInternalNetworkName == n.strInternalNetworkName
2764 && strGenericDriver == n.strGenericDriver
2765 && genericProperties == n.genericProperties
2766 && ulBootPriority == n.ulBootPriority
2767 && strBandwidthGroup == n.strBandwidthGroup);
2768}
2769
2770/**
2771 * Constructor. Needs to set sane defaults which stand the test of time.
2772 */
2773SerialPort::SerialPort() :
2774 ulSlot(0),
2775 fEnabled(false),
2776 ulIOBase(0x3f8),
2777 ulIRQ(4),
2778 portMode(PortMode_Disconnected),
2779 fServer(false),
2780 uartType(UartType_U16550A)
2781{
2782}
2783
2784/**
2785 * Comparison operator. This gets called from MachineConfigFile::operator==,
2786 * which in turn gets called from Machine::saveSettings to figure out whether
2787 * machine settings have really changed and thus need to be written out to disk.
2788 */
2789bool SerialPort::operator==(const SerialPort &s) const
2790{
2791 return (this == &s)
2792 || ( ulSlot == s.ulSlot
2793 && fEnabled == s.fEnabled
2794 && ulIOBase == s.ulIOBase
2795 && ulIRQ == s.ulIRQ
2796 && portMode == s.portMode
2797 && strPath == s.strPath
2798 && fServer == s.fServer
2799 && uartType == s.uartType);
2800}
2801
2802/**
2803 * Constructor. Needs to set sane defaults which stand the test of time.
2804 */
2805ParallelPort::ParallelPort() :
2806 ulSlot(0),
2807 fEnabled(false),
2808 ulIOBase(0x378),
2809 ulIRQ(7)
2810{
2811}
2812
2813/**
2814 * Comparison operator. This gets called from MachineConfigFile::operator==,
2815 * which in turn gets called from Machine::saveSettings to figure out whether
2816 * machine settings have really changed and thus need to be written out to disk.
2817 */
2818bool ParallelPort::operator==(const ParallelPort &s) const
2819{
2820 return (this == &s)
2821 || ( ulSlot == s.ulSlot
2822 && fEnabled == s.fEnabled
2823 && ulIOBase == s.ulIOBase
2824 && ulIRQ == s.ulIRQ
2825 && strPath == s.strPath);
2826}
2827
2828/**
2829 * Constructor. Needs to set sane defaults which stand the test of time.
2830 */
2831AudioAdapter::AudioAdapter() :
2832 fEnabled(true), // default for old VMs, for new ones it's false
2833 fEnabledIn(true), // default for old VMs, for new ones it's false
2834 fEnabledOut(true), // default for old VMs, for new ones it's false
2835 controllerType(AudioControllerType_AC97),
2836 codecType(AudioCodecType_STAC9700),
2837 driverType(AudioDriverType_Null)
2838{
2839}
2840
2841/**
2842 * Check if all settings have default values.
2843 */
2844bool AudioAdapter::areDefaultSettings(SettingsVersion_T sv) const
2845{
2846 return (sv < SettingsVersion_v1_16 ? false : !fEnabled)
2847 && (sv <= SettingsVersion_v1_16 ? fEnabledIn : !fEnabledIn)
2848 && (sv <= SettingsVersion_v1_16 ? fEnabledOut : !fEnabledOut)
2849 && fEnabledOut == true
2850 && controllerType == AudioControllerType_AC97
2851 && codecType == AudioCodecType_STAC9700
2852 && properties.size() == 0;
2853}
2854
2855/**
2856 * Comparison operator. This gets called from MachineConfigFile::operator==,
2857 * which in turn gets called from Machine::saveSettings to figure out whether
2858 * machine settings have really changed and thus need to be written out to disk.
2859 */
2860bool AudioAdapter::operator==(const AudioAdapter &a) const
2861{
2862 return (this == &a)
2863 || ( fEnabled == a.fEnabled
2864 && fEnabledIn == a.fEnabledIn
2865 && fEnabledOut == a.fEnabledOut
2866 && controllerType == a.controllerType
2867 && codecType == a.codecType
2868 && driverType == a.driverType
2869 && properties == a.properties);
2870}
2871
2872/**
2873 * Constructor. Needs to set sane defaults which stand the test of time.
2874 */
2875SharedFolder::SharedFolder() :
2876 fWritable(false),
2877 fAutoMount(false)
2878{
2879}
2880
2881/**
2882 * Comparison operator. This gets called from MachineConfigFile::operator==,
2883 * which in turn gets called from Machine::saveSettings to figure out whether
2884 * machine settings have really changed and thus need to be written out to disk.
2885 */
2886bool SharedFolder::operator==(const SharedFolder &g) const
2887{
2888 return (this == &g)
2889 || ( strName == g.strName
2890 && strHostPath == g.strHostPath
2891 && fWritable == g.fWritable
2892 && fAutoMount == g.fAutoMount
2893 && strAutoMountPoint == g.strAutoMountPoint);
2894}
2895
2896/**
2897 * Constructor. Needs to set sane defaults which stand the test of time.
2898 */
2899GuestProperty::GuestProperty() :
2900 timestamp(0)
2901{
2902}
2903
2904/**
2905 * Comparison operator. This gets called from MachineConfigFile::operator==,
2906 * which in turn gets called from Machine::saveSettings to figure out whether
2907 * machine settings have really changed and thus need to be written out to disk.
2908 */
2909bool GuestProperty::operator==(const GuestProperty &g) const
2910{
2911 return (this == &g)
2912 || ( strName == g.strName
2913 && strValue == g.strValue
2914 && timestamp == g.timestamp
2915 && strFlags == g.strFlags);
2916}
2917
2918/**
2919 * Constructor. Needs to set sane defaults which stand the test of time.
2920 */
2921CpuIdLeaf::CpuIdLeaf() :
2922 idx(UINT32_MAX),
2923 idxSub(0),
2924 uEax(0),
2925 uEbx(0),
2926 uEcx(0),
2927 uEdx(0)
2928{
2929}
2930
2931/**
2932 * Comparison operator. This gets called from MachineConfigFile::operator==,
2933 * which in turn gets called from Machine::saveSettings to figure out whether
2934 * machine settings have really changed and thus need to be written out to disk.
2935 */
2936bool CpuIdLeaf::operator==(const CpuIdLeaf &c) const
2937{
2938 return (this == &c)
2939 || ( idx == c.idx
2940 && idxSub == c.idxSub
2941 && uEax == c.uEax
2942 && uEbx == c.uEbx
2943 && uEcx == c.uEcx
2944 && uEdx == c.uEdx);
2945}
2946
2947/**
2948 * Constructor. Needs to set sane defaults which stand the test of time.
2949 */
2950Cpu::Cpu() :
2951 ulId(UINT32_MAX)
2952{
2953}
2954
2955/**
2956 * Comparison operator. This gets called from MachineConfigFile::operator==,
2957 * which in turn gets called from Machine::saveSettings to figure out whether
2958 * machine settings have really changed and thus need to be written out to disk.
2959 */
2960bool Cpu::operator==(const Cpu &c) const
2961{
2962 return (this == &c)
2963 || (ulId == c.ulId);
2964}
2965
2966/**
2967 * Constructor. Needs to set sane defaults which stand the test of time.
2968 */
2969BandwidthGroup::BandwidthGroup() :
2970 cMaxBytesPerSec(0),
2971 enmType(BandwidthGroupType_Null)
2972{
2973}
2974
2975/**
2976 * Comparison operator. This gets called from MachineConfigFile::operator==,
2977 * which in turn gets called from Machine::saveSettings to figure out whether
2978 * machine settings have really changed and thus need to be written out to disk.
2979 */
2980bool BandwidthGroup::operator==(const BandwidthGroup &i) const
2981{
2982 return (this == &i)
2983 || ( strName == i.strName
2984 && cMaxBytesPerSec == i.cMaxBytesPerSec
2985 && enmType == i.enmType);
2986}
2987
2988/**
2989 * IOSettings constructor.
2990 */
2991IOSettings::IOSettings() :
2992 fIOCacheEnabled(true),
2993 ulIOCacheSize(5)
2994{
2995}
2996
2997/**
2998 * Check if all IO Cache settings have default values.
2999 */
3000bool IOSettings::areIOCacheDefaultSettings() const
3001{
3002 return fIOCacheEnabled
3003 && ulIOCacheSize == 5;
3004}
3005
3006/**
3007 * Check if all settings have default values.
3008 */
3009bool IOSettings::areDefaultSettings() const
3010{
3011 return areIOCacheDefaultSettings()
3012 && llBandwidthGroups.size() == 0;
3013}
3014
3015/**
3016 * Comparison operator. This gets called from MachineConfigFile::operator==,
3017 * which in turn gets called from Machine::saveSettings to figure out whether
3018 * machine settings have really changed and thus need to be written out to disk.
3019 */
3020bool IOSettings::operator==(const IOSettings &i) const
3021{
3022 return (this == &i)
3023 || ( fIOCacheEnabled == i.fIOCacheEnabled
3024 && ulIOCacheSize == i.ulIOCacheSize
3025 && llBandwidthGroups == i.llBandwidthGroups);
3026}
3027
3028/**
3029 * Constructor. Needs to set sane defaults which stand the test of time.
3030 */
3031HostPCIDeviceAttachment::HostPCIDeviceAttachment() :
3032 uHostAddress(0),
3033 uGuestAddress(0)
3034{
3035}
3036
3037/**
3038 * Comparison operator. This gets called from MachineConfigFile::operator==,
3039 * which in turn gets called from Machine::saveSettings to figure out whether
3040 * machine settings have really changed and thus need to be written out to disk.
3041 */
3042bool HostPCIDeviceAttachment::operator==(const HostPCIDeviceAttachment &a) const
3043{
3044 return (this == &a)
3045 || ( uHostAddress == a.uHostAddress
3046 && uGuestAddress == a.uGuestAddress
3047 && strDeviceName == a.strDeviceName);
3048}
3049
3050
3051/**
3052 * Constructor. Needs to set sane defaults which stand the test of time.
3053 */
3054Hardware::Hardware() :
3055 strVersion("1"),
3056 fHardwareVirt(true),
3057 fNestedPaging(true),
3058 fVPID(true),
3059 fUnrestrictedExecution(true),
3060 fHardwareVirtForce(false),
3061 fUseNativeApi(false),
3062 fTripleFaultReset(false),
3063 fPAE(false),
3064 fAPIC(true),
3065 fX2APIC(false),
3066 fIBPBOnVMExit(false),
3067 fIBPBOnVMEntry(false),
3068 fSpecCtrl(false),
3069 fSpecCtrlByHost(false),
3070 fL1DFlushOnSched(true),
3071 fL1DFlushOnVMEntry(false),
3072 fNestedHWVirt(false),
3073 enmLongMode(HC_ARCH_BITS == 64 ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled),
3074 cCPUs(1),
3075 fCpuHotPlug(false),
3076 fHPETEnabled(false),
3077 ulCpuExecutionCap(100),
3078 uCpuIdPortabilityLevel(0),
3079 strCpuProfile("host"),
3080 ulMemorySizeMB((uint32_t)-1),
3081 graphicsControllerType(GraphicsControllerType_VBoxVGA),
3082 ulVRAMSizeMB(8),
3083 cMonitors(1),
3084 fAccelerate3D(false),
3085 fAccelerate2DVideo(false),
3086 firmwareType(FirmwareType_BIOS),
3087 pointingHIDType(PointingHIDType_PS2Mouse),
3088 keyboardHIDType(KeyboardHIDType_PS2Keyboard),
3089 chipsetType(ChipsetType_PIIX3),
3090 paravirtProvider(ParavirtProvider_Legacy), // default for old VMs, for new ones it's ParavirtProvider_Default
3091 strParavirtDebug(""),
3092 fEmulatedUSBCardReader(false),
3093 clipboardMode(ClipboardMode_Disabled),
3094 dndMode(DnDMode_Disabled),
3095 ulMemoryBalloonSize(0),
3096 fPageFusionEnabled(false)
3097{
3098 mapBootOrder[0] = DeviceType_Floppy;
3099 mapBootOrder[1] = DeviceType_DVD;
3100 mapBootOrder[2] = DeviceType_HardDisk;
3101
3102 /* The default value for PAE depends on the host:
3103 * - 64 bits host -> always true
3104 * - 32 bits host -> true for Windows & Darwin (masked off if the host cpu doesn't support it anyway)
3105 */
3106#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
3107 fPAE = true;
3108#endif
3109
3110 /* The default value of large page supports depends on the host:
3111 * - 64 bits host -> true, unless it's Linux (pending further prediction work due to excessively expensive large page allocations)
3112 * - 32 bits host -> false
3113 */
3114#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
3115 fLargePages = true;
3116#else
3117 /* Not supported on 32 bits hosts. */
3118 fLargePages = false;
3119#endif
3120}
3121
3122/**
3123 * Check if all Paravirt settings have default values.
3124 */
3125bool Hardware::areParavirtDefaultSettings(SettingsVersion_T sv) const
3126{
3127 // 5.0 didn't save the paravirt settings if it is ParavirtProvider_Legacy,
3128 // so this default must be kept. Later versions don't save the setting if
3129 // it's at the default value.
3130 return ( (sv >= SettingsVersion_v1_16 && paravirtProvider == ParavirtProvider_Default)
3131 || (sv < SettingsVersion_v1_16 && paravirtProvider == ParavirtProvider_Legacy))
3132 && strParavirtDebug.isEmpty();
3133}
3134
3135/**
3136 * Check if all Boot Order settings have default values.
3137 */
3138bool Hardware::areBootOrderDefaultSettings() const
3139{
3140 BootOrderMap::const_iterator it0 = mapBootOrder.find(0);
3141 BootOrderMap::const_iterator it1 = mapBootOrder.find(1);
3142 BootOrderMap::const_iterator it2 = mapBootOrder.find(2);
3143 BootOrderMap::const_iterator it3 = mapBootOrder.find(3);
3144 return ( mapBootOrder.size() == 3
3145 || ( mapBootOrder.size() == 4
3146 && (it3 != mapBootOrder.end() && it3->second == DeviceType_Null)))
3147 && (it0 != mapBootOrder.end() && it0->second == DeviceType_Floppy)
3148 && (it1 != mapBootOrder.end() && it1->second == DeviceType_DVD)
3149 && (it2 != mapBootOrder.end() && it2->second == DeviceType_HardDisk);
3150}
3151
3152/**
3153 * Check if all Display settings have default values.
3154 */
3155bool Hardware::areDisplayDefaultSettings() const
3156{
3157 return graphicsControllerType == GraphicsControllerType_VBoxVGA
3158 && ulVRAMSizeMB == 8
3159 && cMonitors <= 1
3160 && !fAccelerate3D
3161 && !fAccelerate2DVideo;
3162}
3163
3164/**
3165 * Check if all Network Adapter settings have default values.
3166 */
3167bool Hardware::areAllNetworkAdaptersDefaultSettings(SettingsVersion_T sv) const
3168{
3169 for (NetworkAdaptersList::const_iterator it = llNetworkAdapters.begin();
3170 it != llNetworkAdapters.end();
3171 ++it)
3172 {
3173 if (!it->areDefaultSettings(sv))
3174 return false;
3175 }
3176 return true;
3177}
3178
3179/**
3180 * Comparison operator. This gets called from MachineConfigFile::operator==,
3181 * which in turn gets called from Machine::saveSettings to figure out whether
3182 * machine settings have really changed and thus need to be written out to disk.
3183 */
3184bool Hardware::operator==(const Hardware& h) const
3185{
3186 return (this == &h)
3187 || ( strVersion == h.strVersion
3188 && uuid == h.uuid
3189 && fHardwareVirt == h.fHardwareVirt
3190 && fNestedPaging == h.fNestedPaging
3191 && fLargePages == h.fLargePages
3192 && fVPID == h.fVPID
3193 && fUnrestrictedExecution == h.fUnrestrictedExecution
3194 && fHardwareVirtForce == h.fHardwareVirtForce
3195 && fUseNativeApi == h.fUseNativeApi
3196 && fPAE == h.fPAE
3197 && enmLongMode == h.enmLongMode
3198 && fTripleFaultReset == h.fTripleFaultReset
3199 && fAPIC == h.fAPIC
3200 && fX2APIC == h.fX2APIC
3201 && fIBPBOnVMExit == h.fIBPBOnVMExit
3202 && fIBPBOnVMEntry == h.fIBPBOnVMEntry
3203 && fSpecCtrl == h.fSpecCtrl
3204 && fSpecCtrlByHost == h.fSpecCtrlByHost
3205 && fL1DFlushOnSched == h.fL1DFlushOnSched
3206 && fL1DFlushOnVMEntry == h.fL1DFlushOnVMEntry
3207 && fNestedHWVirt == h.fNestedHWVirt
3208 && cCPUs == h.cCPUs
3209 && fCpuHotPlug == h.fCpuHotPlug
3210 && ulCpuExecutionCap == h.ulCpuExecutionCap
3211 && uCpuIdPortabilityLevel == h.uCpuIdPortabilityLevel
3212 && strCpuProfile == h.strCpuProfile
3213 && fHPETEnabled == h.fHPETEnabled
3214 && llCpus == h.llCpus
3215 && llCpuIdLeafs == h.llCpuIdLeafs
3216 && ulMemorySizeMB == h.ulMemorySizeMB
3217 && mapBootOrder == h.mapBootOrder
3218 && graphicsControllerType == h.graphicsControllerType
3219 && ulVRAMSizeMB == h.ulVRAMSizeMB
3220 && cMonitors == h.cMonitors
3221 && fAccelerate3D == h.fAccelerate3D
3222 && fAccelerate2DVideo == h.fAccelerate2DVideo
3223 && firmwareType == h.firmwareType
3224 && pointingHIDType == h.pointingHIDType
3225 && keyboardHIDType == h.keyboardHIDType
3226 && chipsetType == h.chipsetType
3227 && paravirtProvider == h.paravirtProvider
3228 && strParavirtDebug == h.strParavirtDebug
3229 && fEmulatedUSBCardReader == h.fEmulatedUSBCardReader
3230 && vrdeSettings == h.vrdeSettings
3231 && biosSettings == h.biosSettings
3232 && usbSettings == h.usbSettings
3233 && llNetworkAdapters == h.llNetworkAdapters
3234 && llSerialPorts == h.llSerialPorts
3235 && llParallelPorts == h.llParallelPorts
3236 && audioAdapter == h.audioAdapter
3237 && storage == h.storage
3238 && llSharedFolders == h.llSharedFolders
3239 && clipboardMode == h.clipboardMode
3240 && dndMode == h.dndMode
3241 && ulMemoryBalloonSize == h.ulMemoryBalloonSize
3242 && fPageFusionEnabled == h.fPageFusionEnabled
3243 && llGuestProperties == h.llGuestProperties
3244 && ioSettings == h.ioSettings
3245 && pciAttachments == h.pciAttachments
3246 && strDefaultFrontend == h.strDefaultFrontend);
3247}
3248
3249/**
3250 * Constructor. Needs to set sane defaults which stand the test of time.
3251 */
3252AttachedDevice::AttachedDevice() :
3253 deviceType(DeviceType_Null),
3254 fPassThrough(false),
3255 fTempEject(false),
3256 fNonRotational(false),
3257 fDiscard(false),
3258 fHotPluggable(false),
3259 lPort(0),
3260 lDevice(0)
3261{
3262}
3263
3264/**
3265 * Comparison operator. This gets called from MachineConfigFile::operator==,
3266 * which in turn gets called from Machine::saveSettings to figure out whether
3267 * machine settings have really changed and thus need to be written out to disk.
3268 */
3269bool AttachedDevice::operator==(const AttachedDevice &a) const
3270{
3271 return (this == &a)
3272 || ( deviceType == a.deviceType
3273 && fPassThrough == a.fPassThrough
3274 && fTempEject == a.fTempEject
3275 && fNonRotational == a.fNonRotational
3276 && fDiscard == a.fDiscard
3277 && fHotPluggable == a.fHotPluggable
3278 && lPort == a.lPort
3279 && lDevice == a.lDevice
3280 && uuid == a.uuid
3281 && strHostDriveSrc == a.strHostDriveSrc
3282 && strBwGroup == a.strBwGroup);
3283}
3284
3285/**
3286 * Constructor. Needs to set sane defaults which stand the test of time.
3287 */
3288StorageController::StorageController() :
3289 storageBus(StorageBus_IDE),
3290 controllerType(StorageControllerType_PIIX3),
3291 ulPortCount(2),
3292 ulInstance(0),
3293 fUseHostIOCache(true),
3294 fBootable(true)
3295{
3296}
3297
3298/**
3299 * Comparison operator. This gets called from MachineConfigFile::operator==,
3300 * which in turn gets called from Machine::saveSettings to figure out whether
3301 * machine settings have really changed and thus need to be written out to disk.
3302 */
3303bool StorageController::operator==(const StorageController &s) const
3304{
3305 return (this == &s)
3306 || ( strName == s.strName
3307 && storageBus == s.storageBus
3308 && controllerType == s.controllerType
3309 && ulPortCount == s.ulPortCount
3310 && ulInstance == s.ulInstance
3311 && fUseHostIOCache == s.fUseHostIOCache
3312 && llAttachedDevices == s.llAttachedDevices);
3313}
3314
3315/**
3316 * Comparison operator. This gets called from MachineConfigFile::operator==,
3317 * which in turn gets called from Machine::saveSettings to figure out whether
3318 * machine settings have really changed and thus need to be written out to disk.
3319 */
3320bool Storage::operator==(const Storage &s) const
3321{
3322 return (this == &s)
3323 || (llStorageControllers == s.llStorageControllers); // deep compare
3324}
3325
3326/**
3327 * Constructor. Needs to set sane defaults which stand the test of time.
3328 */
3329Debugging::Debugging() :
3330 fTracingEnabled(false),
3331 fAllowTracingToAccessVM(false),
3332 strTracingConfig()
3333{
3334}
3335
3336/**
3337 * Check if all settings have default values.
3338 */
3339bool Debugging::areDefaultSettings() const
3340{
3341 return !fTracingEnabled
3342 && !fAllowTracingToAccessVM
3343 && strTracingConfig.isEmpty();
3344}
3345
3346/**
3347 * Comparison operator. This gets called from MachineConfigFile::operator==,
3348 * which in turn gets called from Machine::saveSettings to figure out whether
3349 * machine settings have really changed and thus need to be written out to disk.
3350 */
3351bool Debugging::operator==(const Debugging &d) const
3352{
3353 return (this == &d)
3354 || ( fTracingEnabled == d.fTracingEnabled
3355 && fAllowTracingToAccessVM == d.fAllowTracingToAccessVM
3356 && strTracingConfig == d.strTracingConfig);
3357}
3358
3359/**
3360 * Constructor. Needs to set sane defaults which stand the test of time.
3361 */
3362Autostart::Autostart() :
3363 fAutostartEnabled(false),
3364 uAutostartDelay(0),
3365 enmAutostopType(AutostopType_Disabled)
3366{
3367}
3368
3369/**
3370 * Check if all settings have default values.
3371 */
3372bool Autostart::areDefaultSettings() const
3373{
3374 return !fAutostartEnabled
3375 && !uAutostartDelay
3376 && enmAutostopType == AutostopType_Disabled;
3377}
3378
3379/**
3380 * Comparison operator. This gets called from MachineConfigFile::operator==,
3381 * which in turn gets called from Machine::saveSettings to figure out whether
3382 * machine settings have really changed and thus need to be written out to disk.
3383 */
3384bool Autostart::operator==(const Autostart &a) const
3385{
3386 return (this == &a)
3387 || ( fAutostartEnabled == a.fAutostartEnabled
3388 && uAutostartDelay == a.uAutostartDelay
3389 && enmAutostopType == a.enmAutostopType);
3390}
3391
3392/**
3393 * Constructor. Needs to set sane defaults which stand the test of time.
3394 */
3395Snapshot::Snapshot()
3396{
3397 RTTimeSpecSetNano(&timestamp, 0);
3398}
3399
3400/**
3401 * Comparison operator. This gets called from MachineConfigFile::operator==,
3402 * which in turn gets called from Machine::saveSettings to figure out whether
3403 * machine settings have really changed and thus need to be written out to disk.
3404 */
3405bool Snapshot::operator==(const Snapshot &s) const
3406{
3407 return (this == &s)
3408 || ( uuid == s.uuid
3409 && strName == s.strName
3410 && strDescription == s.strDescription
3411 && RTTimeSpecIsEqual(&timestamp, &s.timestamp)
3412 && strStateFile == s.strStateFile
3413 && hardware == s.hardware // deep compare
3414 && llChildSnapshots == s.llChildSnapshots // deep compare
3415 && debugging == s.debugging
3416 && autostart == s.autostart);
3417}
3418
3419const struct Snapshot settings::Snapshot::Empty; /* default ctor is OK */
3420
3421/**
3422 * Constructor. Needs to set sane defaults which stand the test of time.
3423 */
3424MachineUserData::MachineUserData() :
3425 fDirectoryIncludesUUID(false),
3426 fNameSync(true),
3427 fTeleporterEnabled(false),
3428 uTeleporterPort(0),
3429 enmFaultToleranceState(FaultToleranceState_Inactive),
3430 uFaultTolerancePort(0),
3431 uFaultToleranceInterval(0),
3432 fRTCUseUTC(false),
3433 strVMPriority()
3434{
3435 llGroups.push_back("/");
3436}
3437
3438/**
3439 * Comparison operator. This gets called from MachineConfigFile::operator==,
3440 * which in turn gets called from Machine::saveSettings to figure out whether
3441 * machine settings have really changed and thus need to be written out to disk.
3442 */
3443bool MachineUserData::operator==(const MachineUserData &c) const
3444{
3445 return (this == &c)
3446 || ( strName == c.strName
3447 && fDirectoryIncludesUUID == c.fDirectoryIncludesUUID
3448 && fNameSync == c.fNameSync
3449 && strDescription == c.strDescription
3450 && llGroups == c.llGroups
3451 && strOsType == c.strOsType
3452 && strSnapshotFolder == c.strSnapshotFolder
3453 && fTeleporterEnabled == c.fTeleporterEnabled
3454 && uTeleporterPort == c.uTeleporterPort
3455 && strTeleporterAddress == c.strTeleporterAddress
3456 && strTeleporterPassword == c.strTeleporterPassword
3457 && enmFaultToleranceState == c.enmFaultToleranceState
3458 && uFaultTolerancePort == c.uFaultTolerancePort
3459 && uFaultToleranceInterval == c.uFaultToleranceInterval
3460 && strFaultToleranceAddress == c.strFaultToleranceAddress
3461 && strFaultTolerancePassword == c.strFaultTolerancePassword
3462 && fRTCUseUTC == c.fRTCUseUTC
3463 && ovIcon == c.ovIcon
3464 && strVMPriority == c.strVMPriority);
3465}
3466
3467
3468////////////////////////////////////////////////////////////////////////////////
3469//
3470// MachineConfigFile
3471//
3472////////////////////////////////////////////////////////////////////////////////
3473
3474/**
3475 * Constructor.
3476 *
3477 * If pstrFilename is != NULL, this reads the given settings file into the member
3478 * variables and various substructures and lists. Otherwise, the member variables
3479 * are initialized with default values.
3480 *
3481 * Throws variants of xml::Error for I/O, XML and logical content errors, which
3482 * the caller should catch; if this constructor does not throw, then the member
3483 * variables contain meaningful values (either from the file or defaults).
3484 *
3485 * @param pstrFilename
3486 */
3487MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
3488 : ConfigFileBase(pstrFilename),
3489 fCurrentStateModified(true),
3490 fAborted(false)
3491{
3492 RTTimeNow(&timeLastStateChange);
3493
3494 if (pstrFilename)
3495 {
3496 // the ConfigFileBase constructor has loaded the XML file, so now
3497 // we need only analyze what is in there
3498
3499 xml::NodesLoop nlRootChildren(*m->pelmRoot);
3500 const xml::ElementNode *pelmRootChild;
3501 while ((pelmRootChild = nlRootChildren.forAllNodes()))
3502 {
3503 if (pelmRootChild->nameEquals("Machine"))
3504 readMachine(*pelmRootChild);
3505 }
3506
3507 // clean up memory allocated by XML engine
3508 clearDocument();
3509 }
3510}
3511
3512/**
3513 * Public routine which returns true if this machine config file can have its
3514 * own media registry (which is true for settings version v1.11 and higher,
3515 * i.e. files created by VirtualBox 4.0 and higher).
3516 * @return
3517 */
3518bool MachineConfigFile::canHaveOwnMediaRegistry() const
3519{
3520 return (m->sv >= SettingsVersion_v1_11);
3521}
3522
3523/**
3524 * Public routine which allows for importing machine XML from an external DOM tree.
3525 * Use this after having called the constructor with a NULL argument.
3526 *
3527 * This is used by the OVF code if a <vbox:Machine> element has been encountered
3528 * in an OVF VirtualSystem element.
3529 *
3530 * @param elmMachine
3531 */
3532void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine)
3533{
3534 // Ideally the version should be mandatory, but since VirtualBox didn't
3535 // care about it until 5.1 came with different defaults, there are OVF
3536 // files created by magicians (not using VirtualBox, which always wrote it)
3537 // which lack this information. Let's hope that they learn to add the
3538 // version when they switch to the newer settings style/defaults of 5.1.
3539 if (!(elmMachine.getAttributeValue("version", m->strSettingsVersionFull)))
3540 m->strSettingsVersionFull = VBOX_XML_IMPORT_VERSION_FULL;
3541
3542 LogRel(("Import settings with version \"%s\"\n", m->strSettingsVersionFull.c_str()));
3543
3544 m->sv = parseVersion(m->strSettingsVersionFull, &elmMachine);
3545
3546 // remember the settings version we read in case it gets upgraded later,
3547 // so we know when to make backups
3548 m->svRead = m->sv;
3549
3550 readMachine(elmMachine);
3551}
3552
3553/**
3554 * Comparison operator. This gets called from Machine::saveSettings to figure out
3555 * whether machine settings have really changed and thus need to be written out to disk.
3556 *
3557 * Even though this is called operator==, this does NOT compare all fields; the "equals"
3558 * should be understood as "has the same machine config as". The following fields are
3559 * NOT compared:
3560 * -- settings versions and file names inherited from ConfigFileBase;
3561 * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
3562 *
3563 * The "deep" comparisons marked below will invoke the operator== functions of the
3564 * structs defined in this file, which may in turn go into comparing lists of
3565 * other structures. As a result, invoking this can be expensive, but it's
3566 * less expensive than writing out XML to disk.
3567 */
3568bool MachineConfigFile::operator==(const MachineConfigFile &c) const
3569{
3570 return (this == &c)
3571 || ( uuid == c.uuid
3572 && machineUserData == c.machineUserData
3573 && strStateFile == c.strStateFile
3574 && uuidCurrentSnapshot == c.uuidCurrentSnapshot
3575 // skip fCurrentStateModified!
3576 && RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange)
3577 && fAborted == c.fAborted
3578 && hardwareMachine == c.hardwareMachine // this one's deep
3579 && mediaRegistry == c.mediaRegistry // this one's deep
3580 // skip mapExtraDataItems! there is no old state available as it's always forced
3581 && llFirstSnapshot == c.llFirstSnapshot); // this one's deep
3582}
3583
3584/**
3585 * Called from MachineConfigFile::readHardware() to read cpu information.
3586 * @param elmCpu
3587 * @param ll
3588 */
3589void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
3590 CpuList &ll)
3591{
3592 xml::NodesLoop nl1(elmCpu, "Cpu");
3593 const xml::ElementNode *pelmCpu;
3594 while ((pelmCpu = nl1.forAllNodes()))
3595 {
3596 Cpu cpu;
3597
3598 if (!pelmCpu->getAttributeValue("id", cpu.ulId))
3599 throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
3600
3601 ll.push_back(cpu);
3602 }
3603}
3604
3605/**
3606 * Called from MachineConfigFile::readHardware() to cpuid information.
3607 * @param elmCpuid
3608 * @param ll
3609 */
3610void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid,
3611 CpuIdLeafsList &ll)
3612{
3613 xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf");
3614 const xml::ElementNode *pelmCpuIdLeaf;
3615 while ((pelmCpuIdLeaf = nl1.forAllNodes()))
3616 {
3617 CpuIdLeaf leaf;
3618
3619 if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.idx))
3620 throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing"));
3621
3622 if (!pelmCpuIdLeaf->getAttributeValue("subleaf", leaf.idxSub))
3623 leaf.idxSub = 0;
3624 pelmCpuIdLeaf->getAttributeValue("eax", leaf.uEax);
3625 pelmCpuIdLeaf->getAttributeValue("ebx", leaf.uEbx);
3626 pelmCpuIdLeaf->getAttributeValue("ecx", leaf.uEcx);
3627 pelmCpuIdLeaf->getAttributeValue("edx", leaf.uEdx);
3628
3629 ll.push_back(leaf);
3630 }
3631}
3632
3633/**
3634 * Called from MachineConfigFile::readHardware() to network information.
3635 * @param elmNetwork
3636 * @param ll
3637 */
3638void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
3639 NetworkAdaptersList &ll)
3640{
3641 xml::NodesLoop nl1(elmNetwork, "Adapter");
3642 const xml::ElementNode *pelmAdapter;
3643 while ((pelmAdapter = nl1.forAllNodes()))
3644 {
3645 NetworkAdapter nic;
3646
3647 if (m->sv >= SettingsVersion_v1_16)
3648 {
3649 /* Starting with VirtualBox 5.1 the default is cable connected and
3650 * PCnet-FAST III. Needs to match NetworkAdapter.areDefaultSettings(). */
3651 nic.fCableConnected = true;
3652 nic.type = NetworkAdapterType_Am79C973;
3653 }
3654
3655 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
3656 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
3657
3658 Utf8Str strTemp;
3659 if (pelmAdapter->getAttributeValue("type", strTemp))
3660 {
3661 if (strTemp == "Am79C970A")
3662 nic.type = NetworkAdapterType_Am79C970A;
3663 else if (strTemp == "Am79C973")
3664 nic.type = NetworkAdapterType_Am79C973;
3665 else if (strTemp == "82540EM")
3666 nic.type = NetworkAdapterType_I82540EM;
3667 else if (strTemp == "82543GC")
3668 nic.type = NetworkAdapterType_I82543GC;
3669 else if (strTemp == "82545EM")
3670 nic.type = NetworkAdapterType_I82545EM;
3671 else if (strTemp == "virtio")
3672 nic.type = NetworkAdapterType_Virtio;
3673 else
3674 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
3675 }
3676
3677 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
3678 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
3679 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
3680 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
3681
3682 if (pelmAdapter->getAttributeValue("promiscuousModePolicy", strTemp))
3683 {
3684 if (strTemp == "Deny")
3685 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
3686 else if (strTemp == "AllowNetwork")
3687 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
3688 else if (strTemp == "AllowAll")
3689 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
3690 else
3691 throw ConfigFileError(this, pelmAdapter,
3692 N_("Invalid value '%s' in Adapter/@promiscuousModePolicy attribute"), strTemp.c_str());
3693 }
3694
3695 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
3696 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
3697 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
3698 pelmAdapter->getAttributeValue("bandwidthGroup", nic.strBandwidthGroup);
3699
3700 xml::ElementNodesList llNetworkModes;
3701 pelmAdapter->getChildElements(llNetworkModes);
3702 xml::ElementNodesList::iterator it;
3703 /* We should have only active mode descriptor and disabled modes set */
3704 if (llNetworkModes.size() > 2)
3705 {
3706 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
3707 }
3708 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
3709 {
3710 const xml::ElementNode *pelmNode = *it;
3711 if (pelmNode->nameEquals("DisabledModes"))
3712 {
3713 xml::ElementNodesList llDisabledNetworkModes;
3714 xml::ElementNodesList::iterator itDisabled;
3715 pelmNode->getChildElements(llDisabledNetworkModes);
3716 /* run over disabled list and load settings */
3717 for (itDisabled = llDisabledNetworkModes.begin();
3718 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
3719 {
3720 const xml::ElementNode *pelmDisabledNode = *itDisabled;
3721 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
3722 }
3723 }
3724 else
3725 readAttachedNetworkMode(*pelmNode, true, nic);
3726 }
3727 // else: default is NetworkAttachmentType_Null
3728
3729 ll.push_back(nic);
3730 }
3731}
3732
3733void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
3734{
3735 NetworkAttachmentType_T enmAttachmentType = NetworkAttachmentType_Null;
3736
3737 if (elmMode.nameEquals("NAT"))
3738 {
3739 enmAttachmentType = NetworkAttachmentType_NAT;
3740
3741 elmMode.getAttributeValue("network", nic.nat.strNetwork);
3742 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
3743 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
3744 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
3745 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
3746 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
3747 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
3748 const xml::ElementNode *pelmDNS;
3749 if ((pelmDNS = elmMode.findChildElement("DNS")))
3750 {
3751 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDNSPassDomain);
3752 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDNSProxy);
3753 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDNSUseHostResolver);
3754 }
3755 const xml::ElementNode *pelmAlias;
3756 if ((pelmAlias = elmMode.findChildElement("Alias")))
3757 {
3758 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
3759 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
3760 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
3761 }
3762 const xml::ElementNode *pelmTFTP;
3763 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
3764 {
3765 pelmTFTP->getAttributeValue("prefix", nic.nat.strTFTPPrefix);
3766 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTFTPBootFile);
3767 pelmTFTP->getAttributeValue("next-server", nic.nat.strTFTPNextServer);
3768 }
3769
3770 readNATForwardRulesMap(elmMode, nic.nat.mapRules);
3771 }
3772 else if ( elmMode.nameEquals("HostInterface")
3773 || elmMode.nameEquals("BridgedInterface"))
3774 {
3775 enmAttachmentType = NetworkAttachmentType_Bridged;
3776
3777 // optional network name, cannot be required or we have trouble with
3778 // settings which are saved before configuring the network name
3779 elmMode.getAttributeValue("name", nic.strBridgedName);
3780 }
3781 else if (elmMode.nameEquals("InternalNetwork"))
3782 {
3783 enmAttachmentType = NetworkAttachmentType_Internal;
3784
3785 // optional network name, cannot be required or we have trouble with
3786 // settings which are saved before configuring the network name
3787 elmMode.getAttributeValue("name", nic.strInternalNetworkName);
3788 }
3789 else if (elmMode.nameEquals("HostOnlyInterface"))
3790 {
3791 enmAttachmentType = NetworkAttachmentType_HostOnly;
3792
3793 // optional network name, cannot be required or we have trouble with
3794 // settings which are saved before configuring the network name
3795 elmMode.getAttributeValue("name", nic.strHostOnlyName);
3796 }
3797 else if (elmMode.nameEquals("GenericInterface"))
3798 {
3799 enmAttachmentType = NetworkAttachmentType_Generic;
3800
3801 elmMode.getAttributeValue("driver", nic.strGenericDriver); // optional network attachment driver
3802
3803 // get all properties
3804 xml::NodesLoop nl(elmMode);
3805 const xml::ElementNode *pelmModeChild;
3806 while ((pelmModeChild = nl.forAllNodes()))
3807 {
3808 if (pelmModeChild->nameEquals("Property"))
3809 {
3810 Utf8Str strPropName, strPropValue;
3811 if ( pelmModeChild->getAttributeValue("name", strPropName)
3812 && pelmModeChild->getAttributeValue("value", strPropValue) )
3813 nic.genericProperties[strPropName] = strPropValue;
3814 else
3815 throw ConfigFileError(this, pelmModeChild, N_("Required GenericInterface/Property/@name or @value attribute is missing"));
3816 }
3817 }
3818 }
3819 else if (elmMode.nameEquals("NATNetwork"))
3820 {
3821 enmAttachmentType = NetworkAttachmentType_NATNetwork;
3822
3823 // optional network name, cannot be required or we have trouble with
3824 // settings which are saved before configuring the network name
3825 elmMode.getAttributeValue("name", nic.strNATNetworkName);
3826 }
3827 else if (elmMode.nameEquals("VDE"))
3828 {
3829 // inofficial hack (VDE networking was never part of the official
3830 // settings, so it's not mentioned in VirtualBox-settings.xsd)
3831 enmAttachmentType = NetworkAttachmentType_Generic;
3832
3833 com::Utf8Str strVDEName;
3834 elmMode.getAttributeValue("network", strVDEName); // optional network name
3835 nic.strGenericDriver = "VDE";
3836 nic.genericProperties["network"] = strVDEName;
3837 }
3838
3839 if (fEnabled && enmAttachmentType != NetworkAttachmentType_Null)
3840 nic.mode = enmAttachmentType;
3841}
3842
3843/**
3844 * Called from MachineConfigFile::readHardware() to read serial port information.
3845 * @param elmUART
3846 * @param ll
3847 */
3848void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
3849 SerialPortsList &ll)
3850{
3851 xml::NodesLoop nl1(elmUART, "Port");
3852 const xml::ElementNode *pelmPort;
3853 while ((pelmPort = nl1.forAllNodes()))
3854 {
3855 SerialPort port;
3856 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
3857 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
3858
3859 // slot must be unique
3860 for (SerialPortsList::const_iterator it = ll.begin();
3861 it != ll.end();
3862 ++it)
3863 if ((*it).ulSlot == port.ulSlot)
3864 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
3865
3866 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
3867 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
3868 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
3869 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
3870 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
3871 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
3872
3873 Utf8Str strPortMode;
3874 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
3875 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
3876 if (strPortMode == "RawFile")
3877 port.portMode = PortMode_RawFile;
3878 else if (strPortMode == "HostPipe")
3879 port.portMode = PortMode_HostPipe;
3880 else if (strPortMode == "HostDevice")
3881 port.portMode = PortMode_HostDevice;
3882 else if (strPortMode == "Disconnected")
3883 port.portMode = PortMode_Disconnected;
3884 else if (strPortMode == "TCP")
3885 port.portMode = PortMode_TCP;
3886 else
3887 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
3888
3889 pelmPort->getAttributeValue("path", port.strPath);
3890 pelmPort->getAttributeValue("server", port.fServer);
3891
3892 Utf8Str strUartType;
3893 if (pelmPort->getAttributeValue("uartType", strUartType))
3894 {
3895 if (strUartType == "16450")
3896 port.uartType = UartType_U16450;
3897 else if (strUartType == "16550A")
3898 port.uartType = UartType_U16550A;
3899 else if (strUartType == "16750")
3900 port.uartType = UartType_U16750;
3901 else
3902 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@uartType attribute"), strUartType.c_str());
3903 }
3904
3905 ll.push_back(port);
3906 }
3907}
3908
3909/**
3910 * Called from MachineConfigFile::readHardware() to read parallel port information.
3911 * @param elmLPT
3912 * @param ll
3913 */
3914void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
3915 ParallelPortsList &ll)
3916{
3917 xml::NodesLoop nl1(elmLPT, "Port");
3918 const xml::ElementNode *pelmPort;
3919 while ((pelmPort = nl1.forAllNodes()))
3920 {
3921 ParallelPort port;
3922 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
3923 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
3924
3925 // slot must be unique
3926 for (ParallelPortsList::const_iterator it = ll.begin();
3927 it != ll.end();
3928 ++it)
3929 if ((*it).ulSlot == port.ulSlot)
3930 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
3931
3932 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
3933 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
3934 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
3935 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
3936 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
3937 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
3938
3939 pelmPort->getAttributeValue("path", port.strPath);
3940
3941 ll.push_back(port);
3942 }
3943}
3944
3945/**
3946 * Called from MachineConfigFile::readHardware() to read audio adapter information
3947 * and maybe fix driver information depending on the current host hardware.
3948 *
3949 * @param elmAudioAdapter "AudioAdapter" XML element.
3950 * @param aa
3951 */
3952void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
3953 AudioAdapter &aa)
3954{
3955 if (m->sv >= SettingsVersion_v1_15)
3956 {
3957 // get all properties
3958 xml::NodesLoop nl1(elmAudioAdapter, "Property");
3959 const xml::ElementNode *pelmModeChild;
3960 while ((pelmModeChild = nl1.forAllNodes()))
3961 {
3962 Utf8Str strPropName, strPropValue;
3963 if ( pelmModeChild->getAttributeValue("name", strPropName)
3964 && pelmModeChild->getAttributeValue("value", strPropValue) )
3965 aa.properties[strPropName] = strPropValue;
3966 else
3967 throw ConfigFileError(this, pelmModeChild, N_("Required AudioAdapter/Property/@name or @value attribute "
3968 "is missing"));
3969 }
3970 }
3971
3972 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
3973 elmAudioAdapter.getAttributeValue("enabledIn", aa.fEnabledIn);
3974 elmAudioAdapter.getAttributeValue("enabledOut", aa.fEnabledOut);
3975
3976 Utf8Str strTemp;
3977 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
3978 {
3979 if (strTemp == "SB16")
3980 aa.controllerType = AudioControllerType_SB16;
3981 else if (strTemp == "AC97")
3982 aa.controllerType = AudioControllerType_AC97;
3983 else if (strTemp == "HDA")
3984 aa.controllerType = AudioControllerType_HDA;
3985 else
3986 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
3987 }
3988
3989 if (elmAudioAdapter.getAttributeValue("codec", strTemp))
3990 {
3991 if (strTemp == "SB16")
3992 aa.codecType = AudioCodecType_SB16;
3993 else if (strTemp == "STAC9700")
3994 aa.codecType = AudioCodecType_STAC9700;
3995 else if (strTemp == "AD1980")
3996 aa.codecType = AudioCodecType_AD1980;
3997 else if (strTemp == "STAC9221")
3998 aa.codecType = AudioCodecType_STAC9221;
3999 else
4000 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@codec attribute"), strTemp.c_str());
4001 }
4002 else
4003 {
4004 /* No codec attribute provided; use defaults. */
4005 switch (aa.controllerType)
4006 {
4007 case AudioControllerType_AC97:
4008 aa.codecType = AudioCodecType_STAC9700;
4009 break;
4010 case AudioControllerType_SB16:
4011 aa.codecType = AudioCodecType_SB16;
4012 break;
4013 case AudioControllerType_HDA:
4014 aa.codecType = AudioCodecType_STAC9221;
4015 break;
4016 default:
4017 Assert(false); /* We just checked the controller type above. */
4018 }
4019 }
4020
4021 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
4022 {
4023 // settings before 1.3 used lower case so make sure this is case-insensitive
4024 strTemp.toUpper();
4025 if (strTemp == "NULL")
4026 aa.driverType = AudioDriverType_Null;
4027 else if (strTemp == "WINMM")
4028 aa.driverType = AudioDriverType_WinMM;
4029 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
4030 aa.driverType = AudioDriverType_DirectSound;
4031 else if (strTemp == "SOLAUDIO") /* Deprecated -- Solaris will use OSS by default now. */
4032 aa.driverType = AudioDriverType_SolAudio;
4033 else if (strTemp == "ALSA")
4034 aa.driverType = AudioDriverType_ALSA;
4035 else if (strTemp == "PULSE")
4036 aa.driverType = AudioDriverType_Pulse;
4037 else if (strTemp == "OSS")
4038 aa.driverType = AudioDriverType_OSS;
4039 else if (strTemp == "COREAUDIO")
4040 aa.driverType = AudioDriverType_CoreAudio;
4041 else if (strTemp == "MMPM")
4042 aa.driverType = AudioDriverType_MMPM;
4043 else
4044 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
4045
4046 // now check if this is actually supported on the current host platform;
4047 // people might be opening a file created on a Windows host, and that
4048 // VM should still start on a Linux host
4049 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
4050 aa.driverType = getHostDefaultAudioDriver();
4051 }
4052}
4053
4054/**
4055 * Called from MachineConfigFile::readHardware() to read guest property information.
4056 * @param elmGuestProperties
4057 * @param hw
4058 */
4059void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
4060 Hardware &hw)
4061{
4062 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
4063 const xml::ElementNode *pelmProp;
4064 while ((pelmProp = nl1.forAllNodes()))
4065 {
4066 GuestProperty prop;
4067 pelmProp->getAttributeValue("name", prop.strName);
4068 pelmProp->getAttributeValue("value", prop.strValue);
4069
4070 pelmProp->getAttributeValue("timestamp", prop.timestamp);
4071 pelmProp->getAttributeValue("flags", prop.strFlags);
4072 hw.llGuestProperties.push_back(prop);
4073 }
4074}
4075
4076/**
4077 * Helper function to read attributes that are common to \<SATAController\> (pre-1.7)
4078 * and \<StorageController\>.
4079 * @param elmStorageController
4080 * @param sctl
4081 */
4082void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
4083 StorageController &sctl)
4084{
4085 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
4086 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
4087}
4088
4089/**
4090 * Reads in a \<Hardware\> block and stores it in the given structure. Used
4091 * both directly from readMachine and from readSnapshot, since snapshots
4092 * have their own hardware sections.
4093 *
4094 * For legacy pre-1.7 settings we also need a storage structure because
4095 * the IDE and SATA controllers used to be defined under \<Hardware\>.
4096 *
4097 * @param elmHardware
4098 * @param hw
4099 */
4100void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
4101 Hardware &hw)
4102{
4103 if (m->sv >= SettingsVersion_v1_16)
4104 {
4105 /* Starting with VirtualBox 5.1 the default is Default, before it was
4106 * Legacy. This needs to matched by areParavirtDefaultSettings(). */
4107 hw.paravirtProvider = ParavirtProvider_Default;
4108 /* The new default is disabled, before it was enabled by default. */
4109 hw.vrdeSettings.fEnabled = false;
4110 /* The new default is disabled, before it was enabled by default. */
4111 hw.audioAdapter.fEnabled = false;
4112 }
4113 else if (m->sv >= SettingsVersion_v1_17)
4114 {
4115 /* Starting with VirtualBox 5.2 the default is disabled, before it was
4116 * enabled. This needs to matched by AudioAdapter::areDefaultSettings(). */
4117 hw.audioAdapter.fEnabledIn = false;
4118 /* The new default is disabled, before it was enabled by default. */
4119 hw.audioAdapter.fEnabledOut = false;
4120 }
4121
4122 if (!elmHardware.getAttributeValue("version", hw.strVersion))
4123 {
4124 /* KLUDGE ALERT! For a while during the 3.1 development this was not
4125 written because it was thought to have a default value of "2". For
4126 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
4127 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
4128 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
4129 missing the hardware version, then it probably should be "2" instead
4130 of "1". */
4131 if (m->sv < SettingsVersion_v1_7)
4132 hw.strVersion = "1";
4133 else
4134 hw.strVersion = "2";
4135 }
4136 Utf8Str strUUID;
4137 if (elmHardware.getAttributeValue("uuid", strUUID))
4138 parseUUID(hw.uuid, strUUID, &elmHardware);
4139
4140 xml::NodesLoop nl1(elmHardware);
4141 const xml::ElementNode *pelmHwChild;
4142 while ((pelmHwChild = nl1.forAllNodes()))
4143 {
4144 if (pelmHwChild->nameEquals("CPU"))
4145 {
4146 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
4147 {
4148 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
4149 const xml::ElementNode *pelmCPUChild;
4150 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
4151 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
4152 }
4153
4154 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
4155 pelmHwChild->getAttributeValue("executionCap", hw.ulCpuExecutionCap);
4156
4157 const xml::ElementNode *pelmCPUChild;
4158 if (hw.fCpuHotPlug)
4159 {
4160 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
4161 readCpuTree(*pelmCPUChild, hw.llCpus);
4162 }
4163
4164 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
4165 {
4166 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
4167 }
4168 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
4169 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
4170 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
4171 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
4172 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
4173 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
4174 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUX")))
4175 pelmCPUChild->getAttributeValue("enabled", hw.fUnrestrictedExecution);
4176 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtForce")))
4177 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirtForce);
4178 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUseNativeApi")))
4179 pelmCPUChild->getAttributeValue("enabled", hw.fUseNativeApi);
4180
4181 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
4182 {
4183 /* The default for pre 3.1 was false, so we must respect that. */
4184 if (m->sv < SettingsVersion_v1_9)
4185 hw.fPAE = false;
4186 }
4187 else
4188 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
4189
4190 bool fLongMode;
4191 if ( (pelmCPUChild = pelmHwChild->findChildElement("LongMode"))
4192 && pelmCPUChild->getAttributeValue("enabled", fLongMode) )
4193 hw.enmLongMode = fLongMode ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled;
4194 else
4195 hw.enmLongMode = Hardware::LongMode_Legacy;
4196
4197 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
4198 {
4199 bool fSyntheticCpu = false;
4200 pelmCPUChild->getAttributeValue("enabled", fSyntheticCpu);
4201 hw.uCpuIdPortabilityLevel = fSyntheticCpu ? 1 : 0;
4202 }
4203 pelmHwChild->getAttributeValue("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
4204 pelmHwChild->getAttributeValue("CpuProfile", hw.strCpuProfile);
4205
4206 if ((pelmCPUChild = pelmHwChild->findChildElement("TripleFaultReset")))
4207 pelmCPUChild->getAttributeValue("enabled", hw.fTripleFaultReset);
4208
4209 if ((pelmCPUChild = pelmHwChild->findChildElement("APIC")))
4210 pelmCPUChild->getAttributeValue("enabled", hw.fAPIC);
4211 if ((pelmCPUChild = pelmHwChild->findChildElement("X2APIC")))
4212 pelmCPUChild->getAttributeValue("enabled", hw.fX2APIC);
4213 if (hw.fX2APIC)
4214 hw.fAPIC = true;
4215 pelmCPUChild = pelmHwChild->findChildElement("IBPBOn");
4216 if (pelmCPUChild)
4217 {
4218 pelmCPUChild->getAttributeValue("vmexit", hw.fIBPBOnVMExit);
4219 pelmCPUChild->getAttributeValue("vmentry", hw.fIBPBOnVMEntry);
4220 }
4221 pelmCPUChild = pelmHwChild->findChildElement("SpecCtrl");
4222 if (pelmCPUChild)
4223 pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrl);
4224 pelmCPUChild = pelmHwChild->findChildElement("SpecCtrlByHost");
4225 if (pelmCPUChild)
4226 pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrlByHost);
4227 pelmCPUChild = pelmHwChild->findChildElement("L1DFlushOn");
4228 if (pelmCPUChild)
4229 {
4230 pelmCPUChild->getAttributeValue("scheduling", hw.fL1DFlushOnSched);
4231 pelmCPUChild->getAttributeValue("vmentry", hw.fL1DFlushOnVMEntry);
4232 }
4233 pelmCPUChild = pelmHwChild->findChildElement("NestedHWVirt");
4234 if (pelmCPUChild)
4235 pelmCPUChild->getAttributeValue("enabled", hw.fNestedHWVirt);
4236
4237 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
4238 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
4239 }
4240 else if (pelmHwChild->nameEquals("Memory"))
4241 {
4242 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
4243 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
4244 }
4245 else if (pelmHwChild->nameEquals("Firmware"))
4246 {
4247 Utf8Str strFirmwareType;
4248 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
4249 {
4250 if ( (strFirmwareType == "BIOS")
4251 || (strFirmwareType == "1") // some trunk builds used the number here
4252 )
4253 hw.firmwareType = FirmwareType_BIOS;
4254 else if ( (strFirmwareType == "EFI")
4255 || (strFirmwareType == "2") // some trunk builds used the number here
4256 )
4257 hw.firmwareType = FirmwareType_EFI;
4258 else if ( strFirmwareType == "EFI32")
4259 hw.firmwareType = FirmwareType_EFI32;
4260 else if ( strFirmwareType == "EFI64")
4261 hw.firmwareType = FirmwareType_EFI64;
4262 else if ( strFirmwareType == "EFIDUAL")
4263 hw.firmwareType = FirmwareType_EFIDUAL;
4264 else
4265 throw ConfigFileError(this,
4266 pelmHwChild,
4267 N_("Invalid value '%s' in Firmware/@type"),
4268 strFirmwareType.c_str());
4269 }
4270 }
4271 else if (pelmHwChild->nameEquals("HID"))
4272 {
4273 Utf8Str strHIDType;
4274 if (pelmHwChild->getAttributeValue("Keyboard", strHIDType))
4275 {
4276 if (strHIDType == "None")
4277 hw.keyboardHIDType = KeyboardHIDType_None;
4278 else if (strHIDType == "USBKeyboard")
4279 hw.keyboardHIDType = KeyboardHIDType_USBKeyboard;
4280 else if (strHIDType == "PS2Keyboard")
4281 hw.keyboardHIDType = KeyboardHIDType_PS2Keyboard;
4282 else if (strHIDType == "ComboKeyboard")
4283 hw.keyboardHIDType = KeyboardHIDType_ComboKeyboard;
4284 else
4285 throw ConfigFileError(this,
4286 pelmHwChild,
4287 N_("Invalid value '%s' in HID/Keyboard/@type"),
4288 strHIDType.c_str());
4289 }
4290 if (pelmHwChild->getAttributeValue("Pointing", strHIDType))
4291 {
4292 if (strHIDType == "None")
4293 hw.pointingHIDType = PointingHIDType_None;
4294 else if (strHIDType == "USBMouse")
4295 hw.pointingHIDType = PointingHIDType_USBMouse;
4296 else if (strHIDType == "USBTablet")
4297 hw.pointingHIDType = PointingHIDType_USBTablet;
4298 else if (strHIDType == "PS2Mouse")
4299 hw.pointingHIDType = PointingHIDType_PS2Mouse;
4300 else if (strHIDType == "ComboMouse")
4301 hw.pointingHIDType = PointingHIDType_ComboMouse;
4302 else if (strHIDType == "USBMultiTouch")
4303 hw.pointingHIDType = PointingHIDType_USBMultiTouch;
4304 else
4305 throw ConfigFileError(this,
4306 pelmHwChild,
4307 N_("Invalid value '%s' in HID/Pointing/@type"),
4308 strHIDType.c_str());
4309 }
4310 }
4311 else if (pelmHwChild->nameEquals("Chipset"))
4312 {
4313 Utf8Str strChipsetType;
4314 if (pelmHwChild->getAttributeValue("type", strChipsetType))
4315 {
4316 if (strChipsetType == "PIIX3")
4317 hw.chipsetType = ChipsetType_PIIX3;
4318 else if (strChipsetType == "ICH9")
4319 hw.chipsetType = ChipsetType_ICH9;
4320 else
4321 throw ConfigFileError(this,
4322 pelmHwChild,
4323 N_("Invalid value '%s' in Chipset/@type"),
4324 strChipsetType.c_str());
4325 }
4326 }
4327 else if (pelmHwChild->nameEquals("Paravirt"))
4328 {
4329 Utf8Str strProvider;
4330 if (pelmHwChild->getAttributeValue("provider", strProvider))
4331 {
4332 if (strProvider == "None")
4333 hw.paravirtProvider = ParavirtProvider_None;
4334 else if (strProvider == "Default")
4335 hw.paravirtProvider = ParavirtProvider_Default;
4336 else if (strProvider == "Legacy")
4337 hw.paravirtProvider = ParavirtProvider_Legacy;
4338 else if (strProvider == "Minimal")
4339 hw.paravirtProvider = ParavirtProvider_Minimal;
4340 else if (strProvider == "HyperV")
4341 hw.paravirtProvider = ParavirtProvider_HyperV;
4342 else if (strProvider == "KVM")
4343 hw.paravirtProvider = ParavirtProvider_KVM;
4344 else
4345 throw ConfigFileError(this,
4346 pelmHwChild,
4347 N_("Invalid value '%s' in Paravirt/@provider attribute"),
4348 strProvider.c_str());
4349 }
4350
4351 pelmHwChild->getAttributeValue("debug", hw.strParavirtDebug);
4352 }
4353 else if (pelmHwChild->nameEquals("HPET"))
4354 {
4355 pelmHwChild->getAttributeValue("enabled", hw.fHPETEnabled);
4356 }
4357 else if (pelmHwChild->nameEquals("Boot"))
4358 {
4359 hw.mapBootOrder.clear();
4360
4361 xml::NodesLoop nl2(*pelmHwChild, "Order");
4362 const xml::ElementNode *pelmOrder;
4363 while ((pelmOrder = nl2.forAllNodes()))
4364 {
4365 uint32_t ulPos;
4366 Utf8Str strDevice;
4367 if (!pelmOrder->getAttributeValue("position", ulPos))
4368 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
4369
4370 if ( ulPos < 1
4371 || ulPos > SchemaDefs::MaxBootPosition
4372 )
4373 throw ConfigFileError(this,
4374 pelmOrder,
4375 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
4376 ulPos,
4377 SchemaDefs::MaxBootPosition + 1);
4378 // XML is 1-based but internal data is 0-based
4379 --ulPos;
4380
4381 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
4382 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
4383
4384 if (!pelmOrder->getAttributeValue("device", strDevice))
4385 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
4386
4387 DeviceType_T type;
4388 if (strDevice == "None")
4389 type = DeviceType_Null;
4390 else if (strDevice == "Floppy")
4391 type = DeviceType_Floppy;
4392 else if (strDevice == "DVD")
4393 type = DeviceType_DVD;
4394 else if (strDevice == "HardDisk")
4395 type = DeviceType_HardDisk;
4396 else if (strDevice == "Network")
4397 type = DeviceType_Network;
4398 else
4399 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
4400 hw.mapBootOrder[ulPos] = type;
4401 }
4402 }
4403 else if (pelmHwChild->nameEquals("Display"))
4404 {
4405 Utf8Str strGraphicsControllerType;
4406 if (!pelmHwChild->getAttributeValue("controller", strGraphicsControllerType))
4407 hw.graphicsControllerType = GraphicsControllerType_VBoxVGA;
4408 else
4409 {
4410 strGraphicsControllerType.toUpper();
4411 GraphicsControllerType_T type;
4412 if (strGraphicsControllerType == "VBOXVGA")
4413 type = GraphicsControllerType_VBoxVGA;
4414 else if (strGraphicsControllerType == "VMSVGA")
4415 type = GraphicsControllerType_VMSVGA;
4416 else if (strGraphicsControllerType == "VBOXSVGA")
4417 type = GraphicsControllerType_VBoxSVGA;
4418 else if (strGraphicsControllerType == "NONE")
4419 type = GraphicsControllerType_Null;
4420 else
4421 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Display/@controller attribute"), strGraphicsControllerType.c_str());
4422 hw.graphicsControllerType = type;
4423 }
4424 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
4425 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
4426 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
4427 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
4428 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
4429 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
4430 }
4431 else if (pelmHwChild->nameEquals("VideoCapture"))
4432 {
4433 pelmHwChild->getAttributeValue("enabled", hw.recordingSettings.fEnabled);
4434
4435 /* Right now I don't want to bump the settings version, so just convert the enabled
4436 * screens to the former uint64t_t bit array and vice versa. */
4437 uint64_t u64VideoCaptureScreens;
4438 pelmHwChild->getAttributeValue("screens", u64VideoCaptureScreens);
4439
4440 /* At the moment we only support one capturing configuration, that is, all screens
4441 * have the same configuration. So load/save to/from screen 0. */
4442 Assert(hw.recordingSettings.mapScreens.size()); /* At least screen must be present. */
4443 RecordingScreenSettings &screen0Settings = hw.recordingSettings.mapScreens[0];
4444
4445 pelmHwChild->getAttributeValue("maxTime", screen0Settings.ulMaxTimeS);
4446 pelmHwChild->getAttributeValue("options", screen0Settings.strOptions);
4447 pelmHwChild->getAttributeValuePath("file", screen0Settings.File.strName);
4448 pelmHwChild->getAttributeValue("maxSize", screen0Settings.File.ulMaxSizeMB);
4449 pelmHwChild->getAttributeValue("horzRes", screen0Settings.Video.ulWidth);
4450 pelmHwChild->getAttributeValue("vertRes", screen0Settings.Video.ulHeight);
4451 pelmHwChild->getAttributeValue("rate", screen0Settings.Video.ulRate);
4452 pelmHwChild->getAttributeValue("fps", screen0Settings.Video.ulFPS);
4453
4454 for (unsigned i = 0; i < hw.cMonitors; i++) /* Don't add more settings than we have monitors configured. */
4455 {
4456 /* Add screen i to config in any case. */
4457 hw.recordingSettings.mapScreens[i] = screen0Settings;
4458
4459 if (u64VideoCaptureScreens & RT_BIT_64(i)) /* Screen i enabled? */
4460 hw.recordingSettings.mapScreens[i].fEnabled = true;
4461 }
4462 }
4463 else if (pelmHwChild->nameEquals("RemoteDisplay"))
4464 {
4465 pelmHwChild->getAttributeValue("enabled", hw.vrdeSettings.fEnabled);
4466
4467 Utf8Str str;
4468 if (pelmHwChild->getAttributeValue("port", str))
4469 hw.vrdeSettings.mapProperties["TCP/Ports"] = str;
4470 if (pelmHwChild->getAttributeValue("netAddress", str))
4471 hw.vrdeSettings.mapProperties["TCP/Address"] = str;
4472
4473 Utf8Str strAuthType;
4474 if (pelmHwChild->getAttributeValue("authType", strAuthType))
4475 {
4476 // settings before 1.3 used lower case so make sure this is case-insensitive
4477 strAuthType.toUpper();
4478 if (strAuthType == "NULL")
4479 hw.vrdeSettings.authType = AuthType_Null;
4480 else if (strAuthType == "GUEST")
4481 hw.vrdeSettings.authType = AuthType_Guest;
4482 else if (strAuthType == "EXTERNAL")
4483 hw.vrdeSettings.authType = AuthType_External;
4484 else
4485 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
4486 }
4487
4488 pelmHwChild->getAttributeValue("authLibrary", hw.vrdeSettings.strAuthLibrary);
4489 pelmHwChild->getAttributeValue("authTimeout", hw.vrdeSettings.ulAuthTimeout);
4490 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
4491 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
4492
4493 /* 3.2 and 4.0 betas, 4.0 has this information in VRDEProperties. */
4494 const xml::ElementNode *pelmVideoChannel;
4495 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
4496 {
4497 bool fVideoChannel = false;
4498 pelmVideoChannel->getAttributeValue("enabled", fVideoChannel);
4499 hw.vrdeSettings.mapProperties["VideoChannel/Enabled"] = fVideoChannel? "true": "false";
4500
4501 uint32_t ulVideoChannelQuality = 75;
4502 pelmVideoChannel->getAttributeValue("quality", ulVideoChannelQuality);
4503 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
4504 char *pszBuffer = NULL;
4505 if (RTStrAPrintf(&pszBuffer, "%d", ulVideoChannelQuality) >= 0)
4506 {
4507 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = pszBuffer;
4508 RTStrFree(pszBuffer);
4509 }
4510 else
4511 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = "75";
4512 }
4513 pelmHwChild->getAttributeValue("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
4514
4515 const xml::ElementNode *pelmProperties = pelmHwChild->findChildElement("VRDEProperties");
4516 if (pelmProperties != NULL)
4517 {
4518 xml::NodesLoop nl(*pelmProperties);
4519 const xml::ElementNode *pelmProperty;
4520 while ((pelmProperty = nl.forAllNodes()))
4521 {
4522 if (pelmProperty->nameEquals("Property"))
4523 {
4524 /* <Property name="TCP/Ports" value="3000-3002"/> */
4525 Utf8Str strName, strValue;
4526 if ( pelmProperty->getAttributeValue("name", strName)
4527 && pelmProperty->getAttributeValue("value", strValue))
4528 hw.vrdeSettings.mapProperties[strName] = strValue;
4529 else
4530 throw ConfigFileError(this, pelmProperty, N_("Required VRDE Property/@name or @value attribute is missing"));
4531 }
4532 }
4533 }
4534 }
4535 else if (pelmHwChild->nameEquals("BIOS"))
4536 {
4537 const xml::ElementNode *pelmBIOSChild;
4538 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
4539 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
4540 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
4541 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
4542 if ((pelmBIOSChild = pelmHwChild->findChildElement("APIC")))
4543 {
4544 Utf8Str strAPIC;
4545 if (pelmBIOSChild->getAttributeValue("mode", strAPIC))
4546 {
4547 strAPIC.toUpper();
4548 if (strAPIC == "DISABLED")
4549 hw.biosSettings.apicMode = APICMode_Disabled;
4550 else if (strAPIC == "APIC")
4551 hw.biosSettings.apicMode = APICMode_APIC;
4552 else if (strAPIC == "X2APIC")
4553 hw.biosSettings.apicMode = APICMode_X2APIC;
4554 else
4555 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in APIC/@mode attribute"), strAPIC.c_str());
4556 }
4557 }
4558 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
4559 {
4560 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
4561 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
4562 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
4563 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
4564 }
4565 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
4566 {
4567 Utf8Str strBootMenuMode;
4568 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
4569 {
4570 // settings before 1.3 used lower case so make sure this is case-insensitive
4571 strBootMenuMode.toUpper();
4572 if (strBootMenuMode == "DISABLED")
4573 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
4574 else if (strBootMenuMode == "MENUONLY")
4575 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
4576 else if (strBootMenuMode == "MESSAGEANDMENU")
4577 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
4578 else
4579 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
4580 }
4581 }
4582 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
4583 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
4584 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
4585 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
4586
4587 // legacy BIOS/IDEController (pre 1.7)
4588 if ( (m->sv < SettingsVersion_v1_7)
4589 && (pelmBIOSChild = pelmHwChild->findChildElement("IDEController"))
4590 )
4591 {
4592 StorageController sctl;
4593 sctl.strName = "IDE Controller";
4594 sctl.storageBus = StorageBus_IDE;
4595
4596 Utf8Str strType;
4597 if (pelmBIOSChild->getAttributeValue("type", strType))
4598 {
4599 if (strType == "PIIX3")
4600 sctl.controllerType = StorageControllerType_PIIX3;
4601 else if (strType == "PIIX4")
4602 sctl.controllerType = StorageControllerType_PIIX4;
4603 else if (strType == "ICH6")
4604 sctl.controllerType = StorageControllerType_ICH6;
4605 else
4606 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
4607 }
4608 sctl.ulPortCount = 2;
4609 hw.storage.llStorageControllers.push_back(sctl);
4610 }
4611 }
4612 else if ( (m->sv <= SettingsVersion_v1_14)
4613 && pelmHwChild->nameEquals("USBController"))
4614 {
4615 bool fEnabled = false;
4616
4617 pelmHwChild->getAttributeValue("enabled", fEnabled);
4618 if (fEnabled)
4619 {
4620 /* Create OHCI controller with default name. */
4621 USBController ctrl;
4622
4623 ctrl.strName = "OHCI";
4624 ctrl.enmType = USBControllerType_OHCI;
4625 hw.usbSettings.llUSBControllers.push_back(ctrl);
4626 }
4627
4628 pelmHwChild->getAttributeValue("enabledEhci", fEnabled);
4629 if (fEnabled)
4630 {
4631 /* Create OHCI controller with default name. */
4632 USBController ctrl;
4633
4634 ctrl.strName = "EHCI";
4635 ctrl.enmType = USBControllerType_EHCI;
4636 hw.usbSettings.llUSBControllers.push_back(ctrl);
4637 }
4638
4639 readUSBDeviceFilters(*pelmHwChild,
4640 hw.usbSettings.llDeviceFilters);
4641 }
4642 else if (pelmHwChild->nameEquals("USB"))
4643 {
4644 const xml::ElementNode *pelmUSBChild;
4645
4646 if ((pelmUSBChild = pelmHwChild->findChildElement("Controllers")))
4647 {
4648 xml::NodesLoop nl2(*pelmUSBChild, "Controller");
4649 const xml::ElementNode *pelmCtrl;
4650
4651 while ((pelmCtrl = nl2.forAllNodes()))
4652 {
4653 USBController ctrl;
4654 com::Utf8Str strCtrlType;
4655
4656 pelmCtrl->getAttributeValue("name", ctrl.strName);
4657
4658 if (pelmCtrl->getAttributeValue("type", strCtrlType))
4659 {
4660 if (strCtrlType == "OHCI")
4661 ctrl.enmType = USBControllerType_OHCI;
4662 else if (strCtrlType == "EHCI")
4663 ctrl.enmType = USBControllerType_EHCI;
4664 else if (strCtrlType == "XHCI")
4665 ctrl.enmType = USBControllerType_XHCI;
4666 else
4667 throw ConfigFileError(this, pelmCtrl, N_("Invalid value '%s' for Controller/@type attribute"), strCtrlType.c_str());
4668 }
4669
4670 hw.usbSettings.llUSBControllers.push_back(ctrl);
4671 }
4672 }
4673
4674 if ((pelmUSBChild = pelmHwChild->findChildElement("DeviceFilters")))
4675 readUSBDeviceFilters(*pelmUSBChild, hw.usbSettings.llDeviceFilters);
4676 }
4677 else if ( m->sv < SettingsVersion_v1_7
4678 && pelmHwChild->nameEquals("SATAController"))
4679 {
4680 bool f;
4681 if ( pelmHwChild->getAttributeValue("enabled", f)
4682 && f)
4683 {
4684 StorageController sctl;
4685 sctl.strName = "SATA Controller";
4686 sctl.storageBus = StorageBus_SATA;
4687 sctl.controllerType = StorageControllerType_IntelAhci;
4688
4689 readStorageControllerAttributes(*pelmHwChild, sctl);
4690
4691 hw.storage.llStorageControllers.push_back(sctl);
4692 }
4693 }
4694 else if (pelmHwChild->nameEquals("Network"))
4695 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
4696 else if (pelmHwChild->nameEquals("RTC"))
4697 {
4698 Utf8Str strLocalOrUTC;
4699 machineUserData.fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
4700 && strLocalOrUTC == "UTC";
4701 }
4702 else if ( pelmHwChild->nameEquals("UART")
4703 || pelmHwChild->nameEquals("Uart") // used before 1.3
4704 )
4705 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
4706 else if ( pelmHwChild->nameEquals("LPT")
4707 || pelmHwChild->nameEquals("Lpt") // used before 1.3
4708 )
4709 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
4710 else if (pelmHwChild->nameEquals("AudioAdapter"))
4711 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
4712 else if (pelmHwChild->nameEquals("SharedFolders"))
4713 {
4714 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
4715 const xml::ElementNode *pelmFolder;
4716 while ((pelmFolder = nl2.forAllNodes()))
4717 {
4718 SharedFolder sf;
4719 pelmFolder->getAttributeValue("name", sf.strName);
4720 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
4721 pelmFolder->getAttributeValue("writable", sf.fWritable);
4722 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
4723 pelmFolder->getAttributeValue("autoMountPoint", sf.strAutoMountPoint);
4724 hw.llSharedFolders.push_back(sf);
4725 }
4726 }
4727 else if (pelmHwChild->nameEquals("Clipboard"))
4728 {
4729 Utf8Str strTemp;
4730 if (pelmHwChild->getAttributeValue("mode", strTemp))
4731 {
4732 if (strTemp == "Disabled")
4733 hw.clipboardMode = ClipboardMode_Disabled;
4734 else if (strTemp == "HostToGuest")
4735 hw.clipboardMode = ClipboardMode_HostToGuest;
4736 else if (strTemp == "GuestToHost")
4737 hw.clipboardMode = ClipboardMode_GuestToHost;
4738 else if (strTemp == "Bidirectional")
4739 hw.clipboardMode = ClipboardMode_Bidirectional;
4740 else
4741 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
4742 }
4743 }
4744 else if (pelmHwChild->nameEquals("DragAndDrop"))
4745 {
4746 Utf8Str strTemp;
4747 if (pelmHwChild->getAttributeValue("mode", strTemp))
4748 {
4749 if (strTemp == "Disabled")
4750 hw.dndMode = DnDMode_Disabled;
4751 else if (strTemp == "HostToGuest")
4752 hw.dndMode = DnDMode_HostToGuest;
4753 else if (strTemp == "GuestToHost")
4754 hw.dndMode = DnDMode_GuestToHost;
4755 else if (strTemp == "Bidirectional")
4756 hw.dndMode = DnDMode_Bidirectional;
4757 else
4758 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in DragAndDrop/@mode attribute"), strTemp.c_str());
4759 }
4760 }
4761 else if (pelmHwChild->nameEquals("Guest"))
4762 {
4763 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
4764 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
4765 }
4766 else if (pelmHwChild->nameEquals("GuestProperties"))
4767 readGuestProperties(*pelmHwChild, hw);
4768 else if (pelmHwChild->nameEquals("IO"))
4769 {
4770 const xml::ElementNode *pelmBwGroups;
4771 const xml::ElementNode *pelmIOChild;
4772
4773 if ((pelmIOChild = pelmHwChild->findChildElement("IoCache")))
4774 {
4775 pelmIOChild->getAttributeValue("enabled", hw.ioSettings.fIOCacheEnabled);
4776 pelmIOChild->getAttributeValue("size", hw.ioSettings.ulIOCacheSize);
4777 }
4778
4779 if ((pelmBwGroups = pelmHwChild->findChildElement("BandwidthGroups")))
4780 {
4781 xml::NodesLoop nl2(*pelmBwGroups, "BandwidthGroup");
4782 const xml::ElementNode *pelmBandwidthGroup;
4783 while ((pelmBandwidthGroup = nl2.forAllNodes()))
4784 {
4785 BandwidthGroup gr;
4786 Utf8Str strTemp;
4787
4788 pelmBandwidthGroup->getAttributeValue("name", gr.strName);
4789
4790 if (pelmBandwidthGroup->getAttributeValue("type", strTemp))
4791 {
4792 if (strTemp == "Disk")
4793 gr.enmType = BandwidthGroupType_Disk;
4794 else if (strTemp == "Network")
4795 gr.enmType = BandwidthGroupType_Network;
4796 else
4797 throw ConfigFileError(this, pelmBandwidthGroup, N_("Invalid value '%s' in BandwidthGroup/@type attribute"), strTemp.c_str());
4798 }
4799 else
4800 throw ConfigFileError(this, pelmBandwidthGroup, N_("Missing BandwidthGroup/@type attribute"));
4801
4802 if (!pelmBandwidthGroup->getAttributeValue("maxBytesPerSec", gr.cMaxBytesPerSec))
4803 {
4804 pelmBandwidthGroup->getAttributeValue("maxMbPerSec", gr.cMaxBytesPerSec);
4805 gr.cMaxBytesPerSec *= _1M;
4806 }
4807 hw.ioSettings.llBandwidthGroups.push_back(gr);
4808 }
4809 }
4810 }
4811 else if (pelmHwChild->nameEquals("HostPci"))
4812 {
4813 const xml::ElementNode *pelmDevices;
4814
4815 if ((pelmDevices = pelmHwChild->findChildElement("Devices")))
4816 {
4817 xml::NodesLoop nl2(*pelmDevices, "Device");
4818 const xml::ElementNode *pelmDevice;
4819 while ((pelmDevice = nl2.forAllNodes()))
4820 {
4821 HostPCIDeviceAttachment hpda;
4822
4823 if (!pelmDevice->getAttributeValue("host", hpda.uHostAddress))
4824 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@host attribute"));
4825
4826 if (!pelmDevice->getAttributeValue("guest", hpda.uGuestAddress))
4827 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@guest attribute"));
4828
4829 /* name is optional */
4830 pelmDevice->getAttributeValue("name", hpda.strDeviceName);
4831
4832 hw.pciAttachments.push_back(hpda);
4833 }
4834 }
4835 }
4836 else if (pelmHwChild->nameEquals("EmulatedUSB"))
4837 {
4838 const xml::ElementNode *pelmCardReader;
4839
4840 if ((pelmCardReader = pelmHwChild->findChildElement("CardReader")))
4841 {
4842 pelmCardReader->getAttributeValue("enabled", hw.fEmulatedUSBCardReader);
4843 }
4844 }
4845 else if (pelmHwChild->nameEquals("Frontend"))
4846 {
4847 const xml::ElementNode *pelmDefault;
4848
4849 if ((pelmDefault = pelmHwChild->findChildElement("Default")))
4850 {
4851 pelmDefault->getAttributeValue("type", hw.strDefaultFrontend);
4852 }
4853 }
4854 else if (pelmHwChild->nameEquals("StorageControllers"))
4855 readStorageControllers(*pelmHwChild, hw.storage);
4856 }
4857
4858 if (hw.ulMemorySizeMB == (uint32_t)-1)
4859 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
4860}
4861
4862/**
4863 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
4864 * files which have a \<HardDiskAttachments\> node and storage controller settings
4865 * hidden in the \<Hardware\> settings. We set the StorageControllers fields just the
4866 * same, just from different sources.
4867 * @param elmHardDiskAttachments \<HardDiskAttachments\> XML node.
4868 * @param strg
4869 */
4870void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
4871 Storage &strg)
4872{
4873 StorageController *pIDEController = NULL;
4874 StorageController *pSATAController = NULL;
4875
4876 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
4877 it != strg.llStorageControllers.end();
4878 ++it)
4879 {
4880 StorageController &s = *it;
4881 if (s.storageBus == StorageBus_IDE)
4882 pIDEController = &s;
4883 else if (s.storageBus == StorageBus_SATA)
4884 pSATAController = &s;
4885 }
4886
4887 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
4888 const xml::ElementNode *pelmAttachment;
4889 while ((pelmAttachment = nl1.forAllNodes()))
4890 {
4891 AttachedDevice att;
4892 Utf8Str strUUID, strBus;
4893
4894 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
4895 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
4896 parseUUID(att.uuid, strUUID, pelmAttachment);
4897
4898 if (!pelmAttachment->getAttributeValue("bus", strBus))
4899 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
4900 // pre-1.7 'channel' is now port
4901 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
4902 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
4903 // pre-1.7 'device' is still device
4904 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
4905 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
4906
4907 att.deviceType = DeviceType_HardDisk;
4908
4909 if (strBus == "IDE")
4910 {
4911 if (!pIDEController)
4912 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
4913 pIDEController->llAttachedDevices.push_back(att);
4914 }
4915 else if (strBus == "SATA")
4916 {
4917 if (!pSATAController)
4918 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
4919 pSATAController->llAttachedDevices.push_back(att);
4920 }
4921 else
4922 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
4923 }
4924}
4925
4926/**
4927 * Reads in a \<StorageControllers\> block and stores it in the given Storage structure.
4928 * Used both directly from readMachine and from readSnapshot, since snapshots
4929 * have their own storage controllers sections.
4930 *
4931 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
4932 * for earlier versions.
4933 *
4934 * @param elmStorageControllers
4935 * @param strg
4936 */
4937void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
4938 Storage &strg)
4939{
4940 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
4941 const xml::ElementNode *pelmController;
4942 while ((pelmController = nlStorageControllers.forAllNodes()))
4943 {
4944 StorageController sctl;
4945
4946 if (!pelmController->getAttributeValue("name", sctl.strName))
4947 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
4948 // canonicalize storage controller names for configs in the switchover
4949 // period.
4950 if (m->sv < SettingsVersion_v1_9)
4951 {
4952 if (sctl.strName == "IDE")
4953 sctl.strName = "IDE Controller";
4954 else if (sctl.strName == "SATA")
4955 sctl.strName = "SATA Controller";
4956 else if (sctl.strName == "SCSI")
4957 sctl.strName = "SCSI Controller";
4958 }
4959
4960 pelmController->getAttributeValue("Instance", sctl.ulInstance);
4961 // default from constructor is 0
4962
4963 pelmController->getAttributeValue("Bootable", sctl.fBootable);
4964 // default from constructor is true which is true
4965 // for settings below version 1.11 because they allowed only
4966 // one controller per type.
4967
4968 Utf8Str strType;
4969 if (!pelmController->getAttributeValue("type", strType))
4970 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
4971
4972 if (strType == "AHCI")
4973 {
4974 sctl.storageBus = StorageBus_SATA;
4975 sctl.controllerType = StorageControllerType_IntelAhci;
4976 }
4977 else if (strType == "LsiLogic")
4978 {
4979 sctl.storageBus = StorageBus_SCSI;
4980 sctl.controllerType = StorageControllerType_LsiLogic;
4981 }
4982 else if (strType == "BusLogic")
4983 {
4984 sctl.storageBus = StorageBus_SCSI;
4985 sctl.controllerType = StorageControllerType_BusLogic;
4986 }
4987 else if (strType == "PIIX3")
4988 {
4989 sctl.storageBus = StorageBus_IDE;
4990 sctl.controllerType = StorageControllerType_PIIX3;
4991 }
4992 else if (strType == "PIIX4")
4993 {
4994 sctl.storageBus = StorageBus_IDE;
4995 sctl.controllerType = StorageControllerType_PIIX4;
4996 }
4997 else if (strType == "ICH6")
4998 {
4999 sctl.storageBus = StorageBus_IDE;
5000 sctl.controllerType = StorageControllerType_ICH6;
5001 }
5002 else if ( (m->sv >= SettingsVersion_v1_9)
5003 && (strType == "I82078")
5004 )
5005 {
5006 sctl.storageBus = StorageBus_Floppy;
5007 sctl.controllerType = StorageControllerType_I82078;
5008 }
5009 else if (strType == "LsiLogicSas")
5010 {
5011 sctl.storageBus = StorageBus_SAS;
5012 sctl.controllerType = StorageControllerType_LsiLogicSas;
5013 }
5014 else if (strType == "USB")
5015 {
5016 sctl.storageBus = StorageBus_USB;
5017 sctl.controllerType = StorageControllerType_USB;
5018 }
5019 else if (strType == "NVMe")
5020 {
5021 sctl.storageBus = StorageBus_PCIe;
5022 sctl.controllerType = StorageControllerType_NVMe;
5023 }
5024 else
5025 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
5026
5027 readStorageControllerAttributes(*pelmController, sctl);
5028
5029 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
5030 const xml::ElementNode *pelmAttached;
5031 while ((pelmAttached = nlAttached.forAllNodes()))
5032 {
5033 AttachedDevice att;
5034 Utf8Str strTemp;
5035 pelmAttached->getAttributeValue("type", strTemp);
5036
5037 att.fDiscard = false;
5038 att.fNonRotational = false;
5039 att.fHotPluggable = false;
5040 att.fPassThrough = false;
5041
5042 if (strTemp == "HardDisk")
5043 {
5044 att.deviceType = DeviceType_HardDisk;
5045 pelmAttached->getAttributeValue("nonrotational", att.fNonRotational);
5046 pelmAttached->getAttributeValue("discard", att.fDiscard);
5047 }
5048 else if (m->sv >= SettingsVersion_v1_9)
5049 {
5050 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
5051 if (strTemp == "DVD")
5052 {
5053 att.deviceType = DeviceType_DVD;
5054 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
5055 pelmAttached->getAttributeValue("tempeject", att.fTempEject);
5056 }
5057 else if (strTemp == "Floppy")
5058 att.deviceType = DeviceType_Floppy;
5059 }
5060
5061 if (att.deviceType != DeviceType_Null)
5062 {
5063 const xml::ElementNode *pelmImage;
5064 // all types can have images attached, but for HardDisk it's required
5065 if (!(pelmImage = pelmAttached->findChildElement("Image")))
5066 {
5067 if (att.deviceType == DeviceType_HardDisk)
5068 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
5069 else
5070 {
5071 // DVDs and floppies can also have <HostDrive> instead of <Image>
5072 const xml::ElementNode *pelmHostDrive;
5073 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
5074 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
5075 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
5076 }
5077 }
5078 else
5079 {
5080 if (!pelmImage->getAttributeValue("uuid", strTemp))
5081 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
5082 parseUUID(att.uuid, strTemp, pelmImage);
5083 }
5084
5085 if (!pelmAttached->getAttributeValue("port", att.lPort))
5086 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
5087 if (!pelmAttached->getAttributeValue("device", att.lDevice))
5088 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
5089
5090 /* AHCI controller ports are hotpluggable by default, keep compatibility with existing settings. */
5091 if (m->sv >= SettingsVersion_v1_15)
5092 pelmAttached->getAttributeValue("hotpluggable", att.fHotPluggable);
5093 else if (sctl.controllerType == StorageControllerType_IntelAhci)
5094 att.fHotPluggable = true;
5095
5096 pelmAttached->getAttributeValue("bandwidthGroup", att.strBwGroup);
5097 sctl.llAttachedDevices.push_back(att);
5098 }
5099 }
5100
5101 strg.llStorageControllers.push_back(sctl);
5102 }
5103}
5104
5105/**
5106 * This gets called for legacy pre-1.9 settings files after having parsed the
5107 * \<Hardware\> and \<StorageControllers\> sections to parse \<Hardware\> once more
5108 * for the \<DVDDrive\> and \<FloppyDrive\> sections.
5109 *
5110 * Before settings version 1.9, DVD and floppy drives were specified separately
5111 * under \<Hardware\>; we then need this extra loop to make sure the storage
5112 * controller structs are already set up so we can add stuff to them.
5113 *
5114 * @param elmHardware
5115 * @param strg
5116 */
5117void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
5118 Storage &strg)
5119{
5120 xml::NodesLoop nl1(elmHardware);
5121 const xml::ElementNode *pelmHwChild;
5122 while ((pelmHwChild = nl1.forAllNodes()))
5123 {
5124 if (pelmHwChild->nameEquals("DVDDrive"))
5125 {
5126 // create a DVD "attached device" and attach it to the existing IDE controller
5127 AttachedDevice att;
5128 att.deviceType = DeviceType_DVD;
5129 // legacy DVD drive is always secondary master (port 1, device 0)
5130 att.lPort = 1;
5131 att.lDevice = 0;
5132 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
5133 pelmHwChild->getAttributeValue("tempeject", att.fTempEject);
5134
5135 const xml::ElementNode *pDriveChild;
5136 Utf8Str strTmp;
5137 if ( (pDriveChild = pelmHwChild->findChildElement("Image")) != NULL
5138 && pDriveChild->getAttributeValue("uuid", strTmp))
5139 parseUUID(att.uuid, strTmp, pDriveChild);
5140 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
5141 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
5142
5143 // find the IDE controller and attach the DVD drive
5144 bool fFound = false;
5145 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
5146 it != strg.llStorageControllers.end();
5147 ++it)
5148 {
5149 StorageController &sctl = *it;
5150 if (sctl.storageBus == StorageBus_IDE)
5151 {
5152 sctl.llAttachedDevices.push_back(att);
5153 fFound = true;
5154 break;
5155 }
5156 }
5157
5158 if (!fFound)
5159 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
5160 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
5161 // which should have gotten parsed in <StorageControllers> before this got called
5162 }
5163 else if (pelmHwChild->nameEquals("FloppyDrive"))
5164 {
5165 bool fEnabled;
5166 if ( pelmHwChild->getAttributeValue("enabled", fEnabled)
5167 && fEnabled)
5168 {
5169 // create a new floppy controller and attach a floppy "attached device"
5170 StorageController sctl;
5171 sctl.strName = "Floppy Controller";
5172 sctl.storageBus = StorageBus_Floppy;
5173 sctl.controllerType = StorageControllerType_I82078;
5174 sctl.ulPortCount = 1;
5175
5176 AttachedDevice att;
5177 att.deviceType = DeviceType_Floppy;
5178 att.lPort = 0;
5179 att.lDevice = 0;
5180
5181 const xml::ElementNode *pDriveChild;
5182 Utf8Str strTmp;
5183 if ( (pDriveChild = pelmHwChild->findChildElement("Image"))
5184 && pDriveChild->getAttributeValue("uuid", strTmp) )
5185 parseUUID(att.uuid, strTmp, pDriveChild);
5186 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
5187 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
5188
5189 // store attachment with controller
5190 sctl.llAttachedDevices.push_back(att);
5191 // store controller with storage
5192 strg.llStorageControllers.push_back(sctl);
5193 }
5194 }
5195 }
5196}
5197
5198/**
5199 * Called for reading the \<Teleporter\> element under \<Machine\>.
5200 */
5201void MachineConfigFile::readTeleporter(const xml::ElementNode *pElmTeleporter,
5202 MachineUserData *pUserData)
5203{
5204 pElmTeleporter->getAttributeValue("enabled", pUserData->fTeleporterEnabled);
5205 pElmTeleporter->getAttributeValue("port", pUserData->uTeleporterPort);
5206 pElmTeleporter->getAttributeValue("address", pUserData->strTeleporterAddress);
5207 pElmTeleporter->getAttributeValue("password", pUserData->strTeleporterPassword);
5208
5209 if ( pUserData->strTeleporterPassword.isNotEmpty()
5210 && !VBoxIsPasswordHashed(&pUserData->strTeleporterPassword))
5211 VBoxHashPassword(&pUserData->strTeleporterPassword);
5212}
5213
5214/**
5215 * Called for reading the \<Debugging\> element under \<Machine\> or \<Snapshot\>.
5216 */
5217void MachineConfigFile::readDebugging(const xml::ElementNode *pElmDebugging, Debugging *pDbg)
5218{
5219 if (!pElmDebugging || m->sv < SettingsVersion_v1_13)
5220 return;
5221
5222 const xml::ElementNode * const pelmTracing = pElmDebugging->findChildElement("Tracing");
5223 if (pelmTracing)
5224 {
5225 pelmTracing->getAttributeValue("enabled", pDbg->fTracingEnabled);
5226 pelmTracing->getAttributeValue("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
5227 pelmTracing->getAttributeValue("config", pDbg->strTracingConfig);
5228 }
5229}
5230
5231/**
5232 * Called for reading the \<Autostart\> element under \<Machine\> or \<Snapshot\>.
5233 */
5234void MachineConfigFile::readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart)
5235{
5236 Utf8Str strAutostop;
5237
5238 if (!pElmAutostart || m->sv < SettingsVersion_v1_13)
5239 return;
5240
5241 pElmAutostart->getAttributeValue("enabled", pAutostart->fAutostartEnabled);
5242 pElmAutostart->getAttributeValue("delay", pAutostart->uAutostartDelay);
5243 pElmAutostart->getAttributeValue("autostop", strAutostop);
5244 if (strAutostop == "Disabled")
5245 pAutostart->enmAutostopType = AutostopType_Disabled;
5246 else if (strAutostop == "SaveState")
5247 pAutostart->enmAutostopType = AutostopType_SaveState;
5248 else if (strAutostop == "PowerOff")
5249 pAutostart->enmAutostopType = AutostopType_PowerOff;
5250 else if (strAutostop == "AcpiShutdown")
5251 pAutostart->enmAutostopType = AutostopType_AcpiShutdown;
5252 else
5253 throw ConfigFileError(this, pElmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str());
5254}
5255
5256/**
5257 * Called for reading the \<Groups\> element under \<Machine\>.
5258 */
5259void MachineConfigFile::readGroups(const xml::ElementNode *pElmGroups, StringsList *pllGroups)
5260{
5261 pllGroups->clear();
5262 if (!pElmGroups || m->sv < SettingsVersion_v1_13)
5263 {
5264 pllGroups->push_back("/");
5265 return;
5266 }
5267
5268 xml::NodesLoop nlGroups(*pElmGroups);
5269 const xml::ElementNode *pelmGroup;
5270 while ((pelmGroup = nlGroups.forAllNodes()))
5271 {
5272 if (pelmGroup->nameEquals("Group"))
5273 {
5274 Utf8Str strGroup;
5275 if (!pelmGroup->getAttributeValue("name", strGroup))
5276 throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing"));
5277 pllGroups->push_back(strGroup);
5278 }
5279 }
5280}
5281
5282/**
5283 * Called initially for the \<Snapshot\> element under \<Machine\>, if present,
5284 * to store the snapshot's data into the given Snapshot structure (which is
5285 * then the one in the Machine struct). This might then recurse if
5286 * a \<Snapshots\> (plural) element is found in the snapshot, which should
5287 * contain a list of child snapshots; such lists are maintained in the
5288 * Snapshot structure.
5289 *
5290 * @param curSnapshotUuid
5291 * @param depth
5292 * @param elmSnapshot
5293 * @param snap
5294 * @returns true if curSnapshotUuid is in this snapshot subtree, otherwise false
5295 */
5296bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid,
5297 uint32_t depth,
5298 const xml::ElementNode &elmSnapshot,
5299 Snapshot &snap)
5300{
5301 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
5302 throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
5303
5304 Utf8Str strTemp;
5305
5306 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
5307 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
5308 parseUUID(snap.uuid, strTemp, &elmSnapshot);
5309 bool foundCurrentSnapshot = (snap.uuid == curSnapshotUuid);
5310
5311 if (!elmSnapshot.getAttributeValue("name", snap.strName))
5312 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
5313
5314 // 3.1 dev builds added Description as an attribute, read it silently
5315 // and write it back as an element
5316 elmSnapshot.getAttributeValue("Description", snap.strDescription);
5317
5318 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
5319 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
5320 parseTimestamp(snap.timestamp, strTemp, &elmSnapshot);
5321
5322 elmSnapshot.getAttributeValuePath("stateFile", snap.strStateFile); // online snapshots only
5323
5324 // parse Hardware before the other elements because other things depend on it
5325 const xml::ElementNode *pelmHardware;
5326 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
5327 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
5328 readHardware(*pelmHardware, snap.hardware);
5329
5330 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
5331 const xml::ElementNode *pelmSnapshotChild;
5332 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
5333 {
5334 if (pelmSnapshotChild->nameEquals("Description"))
5335 snap.strDescription = pelmSnapshotChild->getValue();
5336 else if ( m->sv < SettingsVersion_v1_7
5337 && pelmSnapshotChild->nameEquals("HardDiskAttachments"))
5338 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.hardware.storage);
5339 else if ( m->sv >= SettingsVersion_v1_7
5340 && pelmSnapshotChild->nameEquals("StorageControllers"))
5341 readStorageControllers(*pelmSnapshotChild, snap.hardware.storage);
5342 else if (pelmSnapshotChild->nameEquals("Snapshots"))
5343 {
5344 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
5345 const xml::ElementNode *pelmChildSnapshot;
5346 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
5347 {
5348 if (pelmChildSnapshot->nameEquals("Snapshot"))
5349 {
5350 // recurse with this element and put the child at the
5351 // end of the list. XPCOM has very small stack, avoid
5352 // big local variables and use the list element.
5353 snap.llChildSnapshots.push_back(Snapshot::Empty);
5354 bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, snap.llChildSnapshots.back());
5355 foundCurrentSnapshot = foundCurrentSnapshot || found;
5356 }
5357 }
5358 }
5359 }
5360
5361 if (m->sv < SettingsVersion_v1_9)
5362 // go through Hardware once more to repair the settings controller structures
5363 // with data from old DVDDrive and FloppyDrive elements
5364 readDVDAndFloppies_pre1_9(*pelmHardware, snap.hardware.storage);
5365
5366 readDebugging(elmSnapshot.findChildElement("Debugging"), &snap.debugging);
5367 readAutostart(elmSnapshot.findChildElement("Autostart"), &snap.autostart);
5368 // note: Groups exist only for Machine, not for Snapshot
5369
5370 return foundCurrentSnapshot;
5371}
5372
5373const struct {
5374 const char *pcszOld;
5375 const char *pcszNew;
5376} aConvertOSTypes[] =
5377{
5378 { "unknown", "Other" },
5379 { "dos", "DOS" },
5380 { "win31", "Windows31" },
5381 { "win95", "Windows95" },
5382 { "win98", "Windows98" },
5383 { "winme", "WindowsMe" },
5384 { "winnt4", "WindowsNT4" },
5385 { "win2k", "Windows2000" },
5386 { "winxp", "WindowsXP" },
5387 { "win2k3", "Windows2003" },
5388 { "winvista", "WindowsVista" },
5389 { "win2k8", "Windows2008" },
5390 { "os2warp3", "OS2Warp3" },
5391 { "os2warp4", "OS2Warp4" },
5392 { "os2warp45", "OS2Warp45" },
5393 { "ecs", "OS2eCS" },
5394 { "linux22", "Linux22" },
5395 { "linux24", "Linux24" },
5396 { "linux26", "Linux26" },
5397 { "archlinux", "ArchLinux" },
5398 { "debian", "Debian" },
5399 { "opensuse", "OpenSUSE" },
5400 { "fedoracore", "Fedora" },
5401 { "gentoo", "Gentoo" },
5402 { "mandriva", "Mandriva" },
5403 { "redhat", "RedHat" },
5404 { "ubuntu", "Ubuntu" },
5405 { "xandros", "Xandros" },
5406 { "freebsd", "FreeBSD" },
5407 { "openbsd", "OpenBSD" },
5408 { "netbsd", "NetBSD" },
5409 { "netware", "Netware" },
5410 { "solaris", "Solaris" },
5411 { "opensolaris", "OpenSolaris" },
5412 { "l4", "L4" }
5413};
5414
5415void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
5416{
5417 for (unsigned u = 0;
5418 u < RT_ELEMENTS(aConvertOSTypes);
5419 ++u)
5420 {
5421 if (str == aConvertOSTypes[u].pcszOld)
5422 {
5423 str = aConvertOSTypes[u].pcszNew;
5424 break;
5425 }
5426 }
5427}
5428
5429/**
5430 * Called from the constructor to actually read in the \<Machine\> element
5431 * of a machine config file.
5432 * @param elmMachine
5433 */
5434void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
5435{
5436 Utf8Str strUUID;
5437 if ( elmMachine.getAttributeValue("uuid", strUUID)
5438 && elmMachine.getAttributeValue("name", machineUserData.strName))
5439 {
5440 parseUUID(uuid, strUUID, &elmMachine);
5441
5442 elmMachine.getAttributeValue("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
5443 elmMachine.getAttributeValue("nameSync", machineUserData.fNameSync);
5444
5445 Utf8Str str;
5446 elmMachine.getAttributeValue("Description", machineUserData.strDescription);
5447 elmMachine.getAttributeValue("OSType", machineUserData.strOsType);
5448 if (m->sv < SettingsVersion_v1_5)
5449 convertOldOSType_pre1_5(machineUserData.strOsType);
5450
5451 elmMachine.getAttributeValuePath("stateFile", strStateFile);
5452
5453 if (elmMachine.getAttributeValue("currentSnapshot", str))
5454 parseUUID(uuidCurrentSnapshot, str, &elmMachine);
5455
5456 elmMachine.getAttributeValuePath("snapshotFolder", machineUserData.strSnapshotFolder);
5457
5458 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
5459 fCurrentStateModified = true;
5460 if (elmMachine.getAttributeValue("lastStateChange", str))
5461 parseTimestamp(timeLastStateChange, str, &elmMachine);
5462 // constructor has called RTTimeNow(&timeLastStateChange) before
5463 if (elmMachine.getAttributeValue("aborted", fAborted))
5464 fAborted = true;
5465
5466 elmMachine.getAttributeValue("processPriority", machineUserData.strVMPriority);
5467
5468 str.setNull();
5469 elmMachine.getAttributeValue("icon", str);
5470 parseBase64(machineUserData.ovIcon, str, &elmMachine);
5471
5472 // parse Hardware before the other elements because other things depend on it
5473 const xml::ElementNode *pelmHardware;
5474 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
5475 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
5476 readHardware(*pelmHardware, hardwareMachine);
5477
5478 xml::NodesLoop nlRootChildren(elmMachine);
5479 const xml::ElementNode *pelmMachineChild;
5480 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
5481 {
5482 if (pelmMachineChild->nameEquals("ExtraData"))
5483 readExtraData(*pelmMachineChild,
5484 mapExtraDataItems);
5485 else if ( (m->sv < SettingsVersion_v1_7)
5486 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
5487 )
5488 readHardDiskAttachments_pre1_7(*pelmMachineChild, hardwareMachine.storage);
5489 else if ( (m->sv >= SettingsVersion_v1_7)
5490 && (pelmMachineChild->nameEquals("StorageControllers"))
5491 )
5492 readStorageControllers(*pelmMachineChild, hardwareMachine.storage);
5493 else if (pelmMachineChild->nameEquals("Snapshot"))
5494 {
5495 if (uuidCurrentSnapshot.isZero())
5496 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing"));
5497 bool foundCurrentSnapshot = false;
5498 Snapshot snap;
5499 // this will recurse into child snapshots, if necessary
5500 foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, 1, *pelmMachineChild, snap);
5501 if (!foundCurrentSnapshot)
5502 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute"));
5503 llFirstSnapshot.push_back(snap);
5504 }
5505 else if (pelmMachineChild->nameEquals("Description"))
5506 machineUserData.strDescription = pelmMachineChild->getValue();
5507 else if (pelmMachineChild->nameEquals("Teleporter"))
5508 readTeleporter(pelmMachineChild, &machineUserData);
5509 else if (pelmMachineChild->nameEquals("FaultTolerance"))
5510 {
5511 Utf8Str strFaultToleranceSate;
5512 if (pelmMachineChild->getAttributeValue("state", strFaultToleranceSate))
5513 {
5514 if (strFaultToleranceSate == "master")
5515 machineUserData.enmFaultToleranceState = FaultToleranceState_Master;
5516 else
5517 if (strFaultToleranceSate == "standby")
5518 machineUserData.enmFaultToleranceState = FaultToleranceState_Standby;
5519 else
5520 machineUserData.enmFaultToleranceState = FaultToleranceState_Inactive;
5521 }
5522 pelmMachineChild->getAttributeValue("port", machineUserData.uFaultTolerancePort);
5523 pelmMachineChild->getAttributeValue("address", machineUserData.strFaultToleranceAddress);
5524 pelmMachineChild->getAttributeValue("interval", machineUserData.uFaultToleranceInterval);
5525 pelmMachineChild->getAttributeValue("password", machineUserData.strFaultTolerancePassword);
5526 }
5527 else if (pelmMachineChild->nameEquals("MediaRegistry"))
5528 readMediaRegistry(*pelmMachineChild, mediaRegistry);
5529 else if (pelmMachineChild->nameEquals("Debugging"))
5530 readDebugging(pelmMachineChild, &debugging);
5531 else if (pelmMachineChild->nameEquals("Autostart"))
5532 readAutostart(pelmMachineChild, &autostart);
5533 else if (pelmMachineChild->nameEquals("Groups"))
5534 readGroups(pelmMachineChild, &machineUserData.llGroups);
5535 }
5536
5537 if (m->sv < SettingsVersion_v1_9)
5538 // go through Hardware once more to repair the settings controller structures
5539 // with data from old DVDDrive and FloppyDrive elements
5540 readDVDAndFloppies_pre1_9(*pelmHardware, hardwareMachine.storage);
5541 }
5542 else
5543 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
5544}
5545
5546/**
5547 * Creates a \<Hardware\> node under elmParent and then writes out the XML
5548 * keys under that. Called for both the \<Machine\> node and for snapshots.
5549 * @param elmParent
5550 * @param hw
5551 * @param fl
5552 * @param pllElementsWithUuidAttributes
5553 */
5554void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
5555 const Hardware &hw,
5556 uint32_t fl,
5557 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
5558{
5559 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
5560
5561 if ( m->sv >= SettingsVersion_v1_4
5562 && (m->sv < SettingsVersion_v1_7 ? hw.strVersion != "1" : hw.strVersion != "2"))
5563 pelmHardware->setAttribute("version", hw.strVersion);
5564
5565 if ((m->sv >= SettingsVersion_v1_9)
5566 && !hw.uuid.isZero()
5567 && hw.uuid.isValid()
5568 )
5569 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
5570
5571 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
5572
5573 if (!hw.fHardwareVirt)
5574 pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
5575 if (!hw.fNestedPaging)
5576 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
5577 if (!hw.fVPID)
5578 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
5579 if (!hw.fUnrestrictedExecution)
5580 pelmCPU->createChild("HardwareVirtExUX")->setAttribute("enabled", hw.fUnrestrictedExecution);
5581 // PAE has too crazy default handling, must always save this setting.
5582 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
5583 if (m->sv >= SettingsVersion_v1_16)
5584 {
5585 if (hw.fIBPBOnVMEntry || hw.fIBPBOnVMExit)
5586 {
5587 xml::ElementNode *pelmChild = pelmCPU->createChild("IBPBOn");
5588 if (hw.fIBPBOnVMExit)
5589 pelmChild->setAttribute("vmexit", hw.fIBPBOnVMExit);
5590 if (hw.fIBPBOnVMEntry)
5591 pelmChild->setAttribute("vmentry", hw.fIBPBOnVMEntry);
5592 }
5593 if (hw.fSpecCtrl)
5594 pelmCPU->createChild("SpecCtrl")->setAttribute("enabled", hw.fSpecCtrl);
5595 if (hw.fSpecCtrlByHost)
5596 pelmCPU->createChild("SpecCtrlByHost")->setAttribute("enabled", hw.fSpecCtrlByHost);
5597 if (!hw.fL1DFlushOnSched || hw.fL1DFlushOnVMEntry)
5598 {
5599 xml::ElementNode *pelmChild = pelmCPU->createChild("L1DFlushOn");
5600 if (!hw.fL1DFlushOnSched)
5601 pelmChild->setAttribute("scheduling", hw.fL1DFlushOnSched);
5602 if (hw.fL1DFlushOnVMEntry)
5603 pelmChild->setAttribute("vmentry", hw.fL1DFlushOnVMEntry);
5604 }
5605 }
5606 if (m->sv >= SettingsVersion_v1_17 && hw.fNestedHWVirt)
5607 pelmCPU->createChild("NestedHWVirt")->setAttribute("enabled", hw.fNestedHWVirt);
5608
5609 if (m->sv >= SettingsVersion_v1_14 && hw.enmLongMode != Hardware::LongMode_Legacy)
5610 {
5611 // LongMode has too crazy default handling, must always save this setting.
5612 pelmCPU->createChild("LongMode")->setAttribute("enabled", hw.enmLongMode == Hardware::LongMode_Enabled);
5613 }
5614
5615 if (hw.fTripleFaultReset)
5616 pelmCPU->createChild("TripleFaultReset")->setAttribute("enabled", hw.fTripleFaultReset);
5617 if (m->sv >= SettingsVersion_v1_14)
5618 {
5619 if (hw.fX2APIC)
5620 pelmCPU->createChild("X2APIC")->setAttribute("enabled", hw.fX2APIC);
5621 else if (!hw.fAPIC)
5622 pelmCPU->createChild("APIC")->setAttribute("enabled", hw.fAPIC);
5623 }
5624 if (hw.cCPUs > 1)
5625 pelmCPU->setAttribute("count", hw.cCPUs);
5626 if (hw.ulCpuExecutionCap != 100)
5627 pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap);
5628 if (hw.uCpuIdPortabilityLevel != 0)
5629 pelmCPU->setAttribute("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
5630 if (!hw.strCpuProfile.equals("host") && hw.strCpuProfile.isNotEmpty())
5631 pelmCPU->setAttribute("CpuProfile", hw.strCpuProfile);
5632
5633 // HardwareVirtExLargePages has too crazy default handling, must always save this setting.
5634 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
5635
5636 if (m->sv >= SettingsVersion_v1_9)
5637 {
5638 if (hw.fHardwareVirtForce)
5639 pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce);
5640 }
5641
5642 if (m->sv >= SettingsVersion_v1_9 && hw.fUseNativeApi)
5643 pelmCPU->createChild("HardwareVirtExUseNativeApi")->setAttribute("enabled", hw.fUseNativeApi);
5644
5645 if (m->sv >= SettingsVersion_v1_10)
5646 {
5647 if (hw.fCpuHotPlug)
5648 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
5649
5650 xml::ElementNode *pelmCpuTree = NULL;
5651 for (CpuList::const_iterator it = hw.llCpus.begin();
5652 it != hw.llCpus.end();
5653 ++it)
5654 {
5655 const Cpu &cpu = *it;
5656
5657 if (pelmCpuTree == NULL)
5658 pelmCpuTree = pelmCPU->createChild("CpuTree");
5659
5660 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
5661 pelmCpu->setAttribute("id", cpu.ulId);
5662 }
5663 }
5664
5665 xml::ElementNode *pelmCpuIdTree = NULL;
5666 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
5667 it != hw.llCpuIdLeafs.end();
5668 ++it)
5669 {
5670 const CpuIdLeaf &leaf = *it;
5671
5672 if (pelmCpuIdTree == NULL)
5673 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
5674
5675 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
5676 pelmCpuIdLeaf->setAttribute("id", leaf.idx);
5677 if (leaf.idxSub != 0)
5678 pelmCpuIdLeaf->setAttribute("subleaf", leaf.idxSub);
5679 pelmCpuIdLeaf->setAttribute("eax", leaf.uEax);
5680 pelmCpuIdLeaf->setAttribute("ebx", leaf.uEbx);
5681 pelmCpuIdLeaf->setAttribute("ecx", leaf.uEcx);
5682 pelmCpuIdLeaf->setAttribute("edx", leaf.uEdx);
5683 }
5684
5685 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
5686 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
5687 if (m->sv >= SettingsVersion_v1_10)
5688 {
5689 if (hw.fPageFusionEnabled)
5690 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
5691 }
5692
5693 if ( (m->sv >= SettingsVersion_v1_9)
5694 && (hw.firmwareType >= FirmwareType_EFI)
5695 )
5696 {
5697 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
5698 const char *pcszFirmware;
5699
5700 switch (hw.firmwareType)
5701 {
5702 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
5703 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
5704 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
5705 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
5706 default: pcszFirmware = "None"; break;
5707 }
5708 pelmFirmware->setAttribute("type", pcszFirmware);
5709 }
5710
5711 if ( m->sv >= SettingsVersion_v1_10
5712 && ( hw.pointingHIDType != PointingHIDType_PS2Mouse
5713 || hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard))
5714 {
5715 xml::ElementNode *pelmHID = pelmHardware->createChild("HID");
5716 const char *pcszHID;
5717
5718 if (hw.pointingHIDType != PointingHIDType_PS2Mouse)
5719 {
5720 switch (hw.pointingHIDType)
5721 {
5722 case PointingHIDType_USBMouse: pcszHID = "USBMouse"; break;
5723 case PointingHIDType_USBTablet: pcszHID = "USBTablet"; break;
5724 case PointingHIDType_PS2Mouse: pcszHID = "PS2Mouse"; break;
5725 case PointingHIDType_ComboMouse: pcszHID = "ComboMouse"; break;
5726 case PointingHIDType_USBMultiTouch: pcszHID = "USBMultiTouch";break;
5727 case PointingHIDType_None: pcszHID = "None"; break;
5728 default: Assert(false); pcszHID = "PS2Mouse"; break;
5729 }
5730 pelmHID->setAttribute("Pointing", pcszHID);
5731 }
5732
5733 if (hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard)
5734 {
5735 switch (hw.keyboardHIDType)
5736 {
5737 case KeyboardHIDType_USBKeyboard: pcszHID = "USBKeyboard"; break;
5738 case KeyboardHIDType_PS2Keyboard: pcszHID = "PS2Keyboard"; break;
5739 case KeyboardHIDType_ComboKeyboard: pcszHID = "ComboKeyboard"; break;
5740 case KeyboardHIDType_None: pcszHID = "None"; break;
5741 default: Assert(false); pcszHID = "PS2Keyboard"; break;
5742 }
5743 pelmHID->setAttribute("Keyboard", pcszHID);
5744 }
5745 }
5746
5747 if ( (m->sv >= SettingsVersion_v1_10)
5748 && hw.fHPETEnabled
5749 )
5750 {
5751 xml::ElementNode *pelmHPET = pelmHardware->createChild("HPET");
5752 pelmHPET->setAttribute("enabled", hw.fHPETEnabled);
5753 }
5754
5755 if ( (m->sv >= SettingsVersion_v1_11)
5756 )
5757 {
5758 if (hw.chipsetType != ChipsetType_PIIX3)
5759 {
5760 xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset");
5761 const char *pcszChipset;
5762
5763 switch (hw.chipsetType)
5764 {
5765 case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break;
5766 case ChipsetType_ICH9: pcszChipset = "ICH9"; break;
5767 default: Assert(false); pcszChipset = "PIIX3"; break;
5768 }
5769 pelmChipset->setAttribute("type", pcszChipset);
5770 }
5771 }
5772
5773 if ( (m->sv >= SettingsVersion_v1_15)
5774 && !hw.areParavirtDefaultSettings(m->sv)
5775 )
5776 {
5777 const char *pcszParavirtProvider;
5778 switch (hw.paravirtProvider)
5779 {
5780 case ParavirtProvider_None: pcszParavirtProvider = "None"; break;
5781 case ParavirtProvider_Default: pcszParavirtProvider = "Default"; break;
5782 case ParavirtProvider_Legacy: pcszParavirtProvider = "Legacy"; break;
5783 case ParavirtProvider_Minimal: pcszParavirtProvider = "Minimal"; break;
5784 case ParavirtProvider_HyperV: pcszParavirtProvider = "HyperV"; break;
5785 case ParavirtProvider_KVM: pcszParavirtProvider = "KVM"; break;
5786 default: Assert(false); pcszParavirtProvider = "None"; break;
5787 }
5788
5789 xml::ElementNode *pelmParavirt = pelmHardware->createChild("Paravirt");
5790 pelmParavirt->setAttribute("provider", pcszParavirtProvider);
5791
5792 if ( m->sv >= SettingsVersion_v1_16
5793 && hw.strParavirtDebug.isNotEmpty())
5794 pelmParavirt->setAttribute("debug", hw.strParavirtDebug);
5795 }
5796
5797 if (!hw.areBootOrderDefaultSettings())
5798 {
5799 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
5800 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
5801 it != hw.mapBootOrder.end();
5802 ++it)
5803 {
5804 uint32_t i = it->first;
5805 DeviceType_T type = it->second;
5806 const char *pcszDevice;
5807
5808 switch (type)
5809 {
5810 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
5811 case DeviceType_DVD: pcszDevice = "DVD"; break;
5812 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
5813 case DeviceType_Network: pcszDevice = "Network"; break;
5814 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
5815 }
5816
5817 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
5818 pelmOrder->setAttribute("position",
5819 i + 1); // XML is 1-based but internal data is 0-based
5820 pelmOrder->setAttribute("device", pcszDevice);
5821 }
5822 }
5823
5824 if (!hw.areDisplayDefaultSettings())
5825 {
5826 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
5827 if (hw.graphicsControllerType != GraphicsControllerType_VBoxVGA)
5828 {
5829 const char *pcszGraphics;
5830 switch (hw.graphicsControllerType)
5831 {
5832 case GraphicsControllerType_VBoxVGA: pcszGraphics = "VBoxVGA"; break;
5833 case GraphicsControllerType_VMSVGA: pcszGraphics = "VMSVGA"; break;
5834 case GraphicsControllerType_VBoxSVGA: pcszGraphics = "VBoxSVGA"; break;
5835 default: /*case GraphicsControllerType_Null:*/ pcszGraphics = "None"; break;
5836 }
5837 pelmDisplay->setAttribute("controller", pcszGraphics);
5838 }
5839 if (hw.ulVRAMSizeMB != 8)
5840 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
5841 if (hw.cMonitors > 1)
5842 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
5843 if (hw.fAccelerate3D)
5844 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
5845
5846 if (m->sv >= SettingsVersion_v1_8)
5847 {
5848 if (hw.fAccelerate2DVideo)
5849 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
5850 }
5851 }
5852
5853 if (m->sv >= SettingsVersion_v1_14 && !hw.recordingSettings.areDefaultSettings())
5854 {
5855 xml::ElementNode *pelmVideoCapture = pelmHardware->createChild("VideoCapture");
5856
5857 if (hw.recordingSettings.fEnabled)
5858 pelmVideoCapture->setAttribute("enabled", hw.recordingSettings.fEnabled);
5859
5860 /* Right now I don't want to bump the settings version, so just convert the enabled
5861 * screens to the former uint64t_t bit array and vice versa. */
5862 uint64_t u64VideoCaptureScreens = 0;
5863 RecordingScreenMap::const_iterator itScreen = hw.recordingSettings.mapScreens.begin();
5864 while (itScreen != hw.recordingSettings.mapScreens.end())
5865 {
5866 if (itScreen->second.fEnabled)
5867 u64VideoCaptureScreens |= RT_BIT_64(itScreen->first);
5868 ++itScreen;
5869 }
5870
5871 if (u64VideoCaptureScreens)
5872 pelmVideoCapture->setAttribute("screens", u64VideoCaptureScreens);
5873
5874 /* At the moment we only support one capturing configuration, that is, all screens
5875 * have the same configuration. So load/save to/from screen 0. */
5876 Assert(hw.recordingSettings.mapScreens.size());
5877 const RecordingScreenMap::const_iterator itScreen0Settings = hw.recordingSettings.mapScreens.find(0);
5878 Assert(itScreen0Settings != hw.recordingSettings.mapScreens.end());
5879
5880 if (itScreen0Settings->second.ulMaxTimeS)
5881 pelmVideoCapture->setAttribute("maxTime", itScreen0Settings->second.ulMaxTimeS);
5882 if (itScreen0Settings->second.strOptions.isNotEmpty())
5883 pelmVideoCapture->setAttributePath("options", itScreen0Settings->second.strOptions);
5884
5885 if (!itScreen0Settings->second.File.strName.isEmpty())
5886 pelmVideoCapture->setAttributePath("file", itScreen0Settings->second.File.strName);
5887 if (itScreen0Settings->second.File.ulMaxSizeMB)
5888 pelmVideoCapture->setAttribute("maxSize", itScreen0Settings->second.File.ulMaxSizeMB);
5889
5890 if ( itScreen0Settings->second.Video.ulWidth != 1024
5891 || itScreen0Settings->second.Video.ulHeight != 768)
5892 {
5893 pelmVideoCapture->setAttribute("horzRes", itScreen0Settings->second.Video.ulWidth);
5894 pelmVideoCapture->setAttribute("vertRes", itScreen0Settings->second.Video.ulHeight);
5895 }
5896 if (itScreen0Settings->second.Video.ulRate != 512)
5897 pelmVideoCapture->setAttribute("rate", itScreen0Settings->second.Video.ulRate);
5898 if (itScreen0Settings->second.Video.ulFPS)
5899 pelmVideoCapture->setAttribute("fps", itScreen0Settings->second.Video.ulFPS);
5900 }
5901
5902 if (!hw.vrdeSettings.areDefaultSettings(m->sv))
5903 {
5904 xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay");
5905 if (m->sv < SettingsVersion_v1_16 ? !hw.vrdeSettings.fEnabled : hw.vrdeSettings.fEnabled)
5906 pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled);
5907 if (m->sv < SettingsVersion_v1_11)
5908 {
5909 /* In VBox 4.0 these attributes are replaced with "Properties". */
5910 Utf8Str strPort;
5911 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports");
5912 if (it != hw.vrdeSettings.mapProperties.end())
5913 strPort = it->second;
5914 if (!strPort.length())
5915 strPort = "3389";
5916 pelmVRDE->setAttribute("port", strPort);
5917
5918 Utf8Str strAddress;
5919 it = hw.vrdeSettings.mapProperties.find("TCP/Address");
5920 if (it != hw.vrdeSettings.mapProperties.end())
5921 strAddress = it->second;
5922 if (strAddress.length())
5923 pelmVRDE->setAttribute("netAddress", strAddress);
5924 }
5925 if (hw.vrdeSettings.authType != AuthType_Null)
5926 {
5927 const char *pcszAuthType;
5928 switch (hw.vrdeSettings.authType)
5929 {
5930 case AuthType_Guest: pcszAuthType = "Guest"; break;
5931 case AuthType_External: pcszAuthType = "External"; break;
5932 default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break;
5933 }
5934 pelmVRDE->setAttribute("authType", pcszAuthType);
5935 }
5936
5937 if (hw.vrdeSettings.ulAuthTimeout != 0 && hw.vrdeSettings.ulAuthTimeout != 5000)
5938 pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout);
5939 if (hw.vrdeSettings.fAllowMultiConnection)
5940 pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
5941 if (hw.vrdeSettings.fReuseSingleConnection)
5942 pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
5943
5944 if (m->sv == SettingsVersion_v1_10)
5945 {
5946 xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel");
5947
5948 /* In 4.0 videochannel settings were replaced with properties, so look at properties. */
5949 Utf8Str str;
5950 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
5951 if (it != hw.vrdeSettings.mapProperties.end())
5952 str = it->second;
5953 bool fVideoChannel = RTStrICmp(str.c_str(), "true") == 0
5954 || RTStrCmp(str.c_str(), "1") == 0;
5955 pelmVideoChannel->setAttribute("enabled", fVideoChannel);
5956
5957 it = hw.vrdeSettings.mapProperties.find("VideoChannel/Quality");
5958 if (it != hw.vrdeSettings.mapProperties.end())
5959 str = it->second;
5960 uint32_t ulVideoChannelQuality = RTStrToUInt32(str.c_str()); /* This returns 0 on invalid string which is ok. */
5961 if (ulVideoChannelQuality == 0)
5962 ulVideoChannelQuality = 75;
5963 else
5964 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
5965 pelmVideoChannel->setAttribute("quality", ulVideoChannelQuality);
5966 }
5967 if (m->sv >= SettingsVersion_v1_11)
5968 {
5969 if (hw.vrdeSettings.strAuthLibrary.length())
5970 pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary);
5971 if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty())
5972 pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
5973 if (hw.vrdeSettings.mapProperties.size() > 0)
5974 {
5975 xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties");
5976 for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin();
5977 it != hw.vrdeSettings.mapProperties.end();
5978 ++it)
5979 {
5980 const Utf8Str &strName = it->first;
5981 const Utf8Str &strValue = it->second;
5982 xml::ElementNode *pelm = pelmProperties->createChild("Property");
5983 pelm->setAttribute("name", strName);
5984 pelm->setAttribute("value", strValue);
5985 }
5986 }
5987 }
5988 }
5989
5990 if (!hw.biosSettings.areDefaultSettings())
5991 {
5992 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
5993 if (!hw.biosSettings.fACPIEnabled)
5994 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
5995 if (hw.biosSettings.fIOAPICEnabled)
5996 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
5997 if (hw.biosSettings.apicMode != APICMode_APIC)
5998 {
5999 const char *pcszAPIC;
6000 switch (hw.biosSettings.apicMode)
6001 {
6002 case APICMode_Disabled:
6003 pcszAPIC = "Disabled";
6004 break;
6005 case APICMode_APIC:
6006 default:
6007 pcszAPIC = "APIC";
6008 break;
6009 case APICMode_X2APIC:
6010 pcszAPIC = "X2APIC";
6011 break;
6012 }
6013 pelmBIOS->createChild("APIC")->setAttribute("mode", pcszAPIC);
6014 }
6015
6016 if ( !hw.biosSettings.fLogoFadeIn
6017 || !hw.biosSettings.fLogoFadeOut
6018 || hw.biosSettings.ulLogoDisplayTime
6019 || !hw.biosSettings.strLogoImagePath.isEmpty())
6020 {
6021 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
6022 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
6023 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
6024 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
6025 if (!hw.biosSettings.strLogoImagePath.isEmpty())
6026 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
6027 }
6028
6029 if (hw.biosSettings.biosBootMenuMode != BIOSBootMenuMode_MessageAndMenu)
6030 {
6031 const char *pcszBootMenu;
6032 switch (hw.biosSettings.biosBootMenuMode)
6033 {
6034 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
6035 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
6036 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
6037 }
6038 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
6039 }
6040 if (hw.biosSettings.llTimeOffset)
6041 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
6042 if (hw.biosSettings.fPXEDebugEnabled)
6043 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
6044 }
6045
6046 if (m->sv < SettingsVersion_v1_9)
6047 {
6048 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
6049 // run thru the storage controllers to see if we have a DVD or floppy drives
6050 size_t cDVDs = 0;
6051 size_t cFloppies = 0;
6052
6053 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
6054 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
6055
6056 for (StorageControllersList::const_iterator it = hw.storage.llStorageControllers.begin();
6057 it != hw.storage.llStorageControllers.end();
6058 ++it)
6059 {
6060 const StorageController &sctl = *it;
6061 // in old settings format, the DVD drive could only have been under the IDE controller
6062 if (sctl.storageBus == StorageBus_IDE)
6063 {
6064 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
6065 it2 != sctl.llAttachedDevices.end();
6066 ++it2)
6067 {
6068 const AttachedDevice &att = *it2;
6069 if (att.deviceType == DeviceType_DVD)
6070 {
6071 if (cDVDs > 0)
6072 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
6073
6074 ++cDVDs;
6075
6076 pelmDVD->setAttribute("passthrough", att.fPassThrough);
6077 if (att.fTempEject)
6078 pelmDVD->setAttribute("tempeject", att.fTempEject);
6079
6080 if (!att.uuid.isZero() && att.uuid.isValid())
6081 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
6082 else if (att.strHostDriveSrc.length())
6083 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6084 }
6085 }
6086 }
6087 else if (sctl.storageBus == StorageBus_Floppy)
6088 {
6089 size_t cFloppiesHere = sctl.llAttachedDevices.size();
6090 if (cFloppiesHere > 1)
6091 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
6092 if (cFloppiesHere)
6093 {
6094 const AttachedDevice &att = sctl.llAttachedDevices.front();
6095 pelmFloppy->setAttribute("enabled", true);
6096
6097 if (!att.uuid.isZero() && att.uuid.isValid())
6098 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
6099 else if (att.strHostDriveSrc.length())
6100 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6101 }
6102
6103 cFloppies += cFloppiesHere;
6104 }
6105 }
6106
6107 if (cFloppies == 0)
6108 pelmFloppy->setAttribute("enabled", false);
6109 else if (cFloppies > 1)
6110 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
6111 }
6112
6113 if (m->sv < SettingsVersion_v1_14)
6114 {
6115 bool fOhciEnabled = false;
6116 bool fEhciEnabled = false;
6117 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
6118
6119 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
6120 it != hw.usbSettings.llUSBControllers.end();
6121 ++it)
6122 {
6123 const USBController &ctrl = *it;
6124
6125 switch (ctrl.enmType)
6126 {
6127 case USBControllerType_OHCI:
6128 fOhciEnabled = true;
6129 break;
6130 case USBControllerType_EHCI:
6131 fEhciEnabled = true;
6132 break;
6133 default:
6134 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
6135 }
6136 }
6137
6138 pelmUSB->setAttribute("enabled", fOhciEnabled);
6139 pelmUSB->setAttribute("enabledEhci", fEhciEnabled);
6140
6141 buildUSBDeviceFilters(*pelmUSB, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
6142 }
6143 else
6144 {
6145 if ( hw.usbSettings.llUSBControllers.size()
6146 || hw.usbSettings.llDeviceFilters.size())
6147 {
6148 xml::ElementNode *pelmUSB = pelmHardware->createChild("USB");
6149 if (hw.usbSettings.llUSBControllers.size())
6150 {
6151 xml::ElementNode *pelmCtrls = pelmUSB->createChild("Controllers");
6152
6153 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
6154 it != hw.usbSettings.llUSBControllers.end();
6155 ++it)
6156 {
6157 const USBController &ctrl = *it;
6158 com::Utf8Str strType;
6159 xml::ElementNode *pelmCtrl = pelmCtrls->createChild("Controller");
6160
6161 switch (ctrl.enmType)
6162 {
6163 case USBControllerType_OHCI:
6164 strType = "OHCI";
6165 break;
6166 case USBControllerType_EHCI:
6167 strType = "EHCI";
6168 break;
6169 case USBControllerType_XHCI:
6170 strType = "XHCI";
6171 break;
6172 default:
6173 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
6174 }
6175
6176 pelmCtrl->setAttribute("name", ctrl.strName);
6177 pelmCtrl->setAttribute("type", strType);
6178 }
6179 }
6180
6181 if (hw.usbSettings.llDeviceFilters.size())
6182 {
6183 xml::ElementNode *pelmFilters = pelmUSB->createChild("DeviceFilters");
6184 buildUSBDeviceFilters(*pelmFilters, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
6185 }
6186 }
6187 }
6188
6189 if ( hw.llNetworkAdapters.size()
6190 && !hw.areAllNetworkAdaptersDefaultSettings(m->sv))
6191 {
6192 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
6193 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
6194 it != hw.llNetworkAdapters.end();
6195 ++it)
6196 {
6197 const NetworkAdapter &nic = *it;
6198
6199 if (!nic.areDefaultSettings(m->sv))
6200 {
6201 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
6202 pelmAdapter->setAttribute("slot", nic.ulSlot);
6203 if (nic.fEnabled)
6204 pelmAdapter->setAttribute("enabled", nic.fEnabled);
6205 if (!nic.strMACAddress.isEmpty())
6206 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
6207 if ( (m->sv >= SettingsVersion_v1_16 && !nic.fCableConnected)
6208 || (m->sv < SettingsVersion_v1_16 && nic.fCableConnected))
6209 pelmAdapter->setAttribute("cable", nic.fCableConnected);
6210 if (nic.ulLineSpeed)
6211 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
6212 if (nic.ulBootPriority != 0)
6213 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
6214 if (nic.fTraceEnabled)
6215 {
6216 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
6217 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
6218 }
6219 if (nic.strBandwidthGroup.isNotEmpty())
6220 pelmAdapter->setAttribute("bandwidthGroup", nic.strBandwidthGroup);
6221
6222 const char *pszPolicy;
6223 switch (nic.enmPromiscModePolicy)
6224 {
6225 case NetworkAdapterPromiscModePolicy_Deny: pszPolicy = NULL; break;
6226 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPolicy = "AllowNetwork"; break;
6227 case NetworkAdapterPromiscModePolicy_AllowAll: pszPolicy = "AllowAll"; break;
6228 default: pszPolicy = NULL; AssertFailed(); break;
6229 }
6230 if (pszPolicy)
6231 pelmAdapter->setAttribute("promiscuousModePolicy", pszPolicy);
6232
6233 if ( (m->sv >= SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C973)
6234 || (m->sv < SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C970A))
6235 {
6236 const char *pcszType;
6237 switch (nic.type)
6238 {
6239 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
6240 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
6241 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
6242 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
6243 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
6244 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
6245 }
6246 pelmAdapter->setAttribute("type", pcszType);
6247 }
6248
6249 xml::ElementNode *pelmNAT;
6250 if (m->sv < SettingsVersion_v1_10)
6251 {
6252 switch (nic.mode)
6253 {
6254 case NetworkAttachmentType_NAT:
6255 pelmNAT = pelmAdapter->createChild("NAT");
6256 if (nic.nat.strNetwork.length())
6257 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6258 break;
6259
6260 case NetworkAttachmentType_Bridged:
6261 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
6262 break;
6263
6264 case NetworkAttachmentType_Internal:
6265 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
6266 break;
6267
6268 case NetworkAttachmentType_HostOnly:
6269 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
6270 break;
6271
6272 default: /*case NetworkAttachmentType_Null:*/
6273 break;
6274 }
6275 }
6276 else
6277 {
6278 /* m->sv >= SettingsVersion_v1_10 */
6279 if (!nic.areDisabledDefaultSettings())
6280 {
6281 xml::ElementNode *pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
6282 if (nic.mode != NetworkAttachmentType_NAT)
6283 buildNetworkXML(NetworkAttachmentType_NAT, false, *pelmDisabledNode, nic);
6284 if (nic.mode != NetworkAttachmentType_Bridged)
6285 buildNetworkXML(NetworkAttachmentType_Bridged, false, *pelmDisabledNode, nic);
6286 if (nic.mode != NetworkAttachmentType_Internal)
6287 buildNetworkXML(NetworkAttachmentType_Internal, false, *pelmDisabledNode, nic);
6288 if (nic.mode != NetworkAttachmentType_HostOnly)
6289 buildNetworkXML(NetworkAttachmentType_HostOnly, false, *pelmDisabledNode, nic);
6290 if (nic.mode != NetworkAttachmentType_Generic)
6291 buildNetworkXML(NetworkAttachmentType_Generic, false, *pelmDisabledNode, nic);
6292 if (nic.mode != NetworkAttachmentType_NATNetwork)
6293 buildNetworkXML(NetworkAttachmentType_NATNetwork, false, *pelmDisabledNode, nic);
6294 }
6295 buildNetworkXML(nic.mode, true, *pelmAdapter, nic);
6296 }
6297 }
6298 }
6299 }
6300
6301 if (hw.llSerialPorts.size())
6302 {
6303 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
6304 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
6305 it != hw.llSerialPorts.end();
6306 ++it)
6307 {
6308 const SerialPort &port = *it;
6309 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6310 pelmPort->setAttribute("slot", port.ulSlot);
6311 pelmPort->setAttribute("enabled", port.fEnabled);
6312 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6313 pelmPort->setAttribute("IRQ", port.ulIRQ);
6314
6315 const char *pcszHostMode;
6316 switch (port.portMode)
6317 {
6318 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
6319 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
6320 case PortMode_TCP: pcszHostMode = "TCP"; break;
6321 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
6322 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
6323 }
6324 switch (port.portMode)
6325 {
6326 case PortMode_TCP:
6327 case PortMode_HostPipe:
6328 pelmPort->setAttribute("server", port.fServer);
6329 RT_FALL_THRU();
6330 case PortMode_HostDevice:
6331 case PortMode_RawFile:
6332 pelmPort->setAttribute("path", port.strPath);
6333 break;
6334
6335 default:
6336 break;
6337 }
6338 pelmPort->setAttribute("hostMode", pcszHostMode);
6339
6340 if ( m->sv >= SettingsVersion_v1_17
6341 && port.uartType != UartType_U16550A)
6342 {
6343 const char *pcszUartType;
6344
6345 switch (port.uartType)
6346 {
6347 case UartType_U16450: pcszUartType = "16450"; break;
6348 case UartType_U16550A: pcszUartType = "16550A"; break;
6349 case UartType_U16750: pcszUartType = "16750"; break;
6350 default: pcszUartType = "16550A"; break;
6351 }
6352 pelmPort->setAttribute("uartType", pcszUartType);
6353 }
6354 }
6355 }
6356
6357 if (hw.llParallelPorts.size())
6358 {
6359 xml::ElementNode *pelmPorts = pelmHardware->createChild("LPT");
6360 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
6361 it != hw.llParallelPorts.end();
6362 ++it)
6363 {
6364 const ParallelPort &port = *it;
6365 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6366 pelmPort->setAttribute("slot", port.ulSlot);
6367 pelmPort->setAttribute("enabled", port.fEnabled);
6368 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6369 pelmPort->setAttribute("IRQ", port.ulIRQ);
6370 if (port.strPath.length())
6371 pelmPort->setAttribute("path", port.strPath);
6372 }
6373 }
6374
6375 /* Always write the AudioAdapter config, intentionally not checking if
6376 * the settings are at the default, because that would be problematic
6377 * for the configured host driver type, which would automatically change
6378 * if the default host driver is detected differently. */
6379 {
6380 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
6381
6382 const char *pcszController;
6383 switch (hw.audioAdapter.controllerType)
6384 {
6385 case AudioControllerType_SB16:
6386 pcszController = "SB16";
6387 break;
6388 case AudioControllerType_HDA:
6389 if (m->sv >= SettingsVersion_v1_11)
6390 {
6391 pcszController = "HDA";
6392 break;
6393 }
6394 RT_FALL_THRU();
6395 case AudioControllerType_AC97:
6396 default:
6397 pcszController = NULL;
6398 break;
6399 }
6400 if (pcszController)
6401 pelmAudio->setAttribute("controller", pcszController);
6402
6403 const char *pcszCodec;
6404 switch (hw.audioAdapter.codecType)
6405 {
6406 /* Only write out the setting for non-default AC'97 codec
6407 * and leave the rest alone.
6408 */
6409#if 0
6410 case AudioCodecType_SB16:
6411 pcszCodec = "SB16";
6412 break;
6413 case AudioCodecType_STAC9221:
6414 pcszCodec = "STAC9221";
6415 break;
6416 case AudioCodecType_STAC9700:
6417 pcszCodec = "STAC9700";
6418 break;
6419#endif
6420 case AudioCodecType_AD1980:
6421 pcszCodec = "AD1980";
6422 break;
6423 default:
6424 /* Don't write out anything if unknown. */
6425 pcszCodec = NULL;
6426 }
6427 if (pcszCodec)
6428 pelmAudio->setAttribute("codec", pcszCodec);
6429
6430 const char *pcszDriver;
6431 switch (hw.audioAdapter.driverType)
6432 {
6433 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
6434 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
6435 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
6436 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
6437 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
6438 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
6439 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
6440 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
6441 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
6442 }
6443 /* Deliberately have the audio driver explicitly in the config file,
6444 * otherwise an unwritten default driver triggers auto-detection. */
6445 pelmAudio->setAttribute("driver", pcszDriver);
6446
6447 if (hw.audioAdapter.fEnabled || m->sv < SettingsVersion_v1_16)
6448 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
6449
6450 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledIn)
6451 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledIn))
6452 pelmAudio->setAttribute("enabledIn", hw.audioAdapter.fEnabledIn);
6453
6454 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledOut)
6455 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledOut))
6456 pelmAudio->setAttribute("enabledOut", hw.audioAdapter.fEnabledOut);
6457
6458 if (m->sv >= SettingsVersion_v1_15 && hw.audioAdapter.properties.size() > 0)
6459 {
6460 for (StringsMap::const_iterator it = hw.audioAdapter.properties.begin();
6461 it != hw.audioAdapter.properties.end();
6462 ++it)
6463 {
6464 const Utf8Str &strName = it->first;
6465 const Utf8Str &strValue = it->second;
6466 xml::ElementNode *pelm = pelmAudio->createChild("Property");
6467 pelm->setAttribute("name", strName);
6468 pelm->setAttribute("value", strValue);
6469 }
6470 }
6471 }
6472
6473 if (m->sv >= SettingsVersion_v1_10 && machineUserData.fRTCUseUTC)
6474 {
6475 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
6476 pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local");
6477 }
6478
6479 if (hw.llSharedFolders.size())
6480 {
6481 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
6482 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
6483 it != hw.llSharedFolders.end();
6484 ++it)
6485 {
6486 const SharedFolder &sf = *it;
6487 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
6488 pelmThis->setAttribute("name", sf.strName);
6489 pelmThis->setAttribute("hostPath", sf.strHostPath);
6490 pelmThis->setAttribute("writable", sf.fWritable);
6491 pelmThis->setAttribute("autoMount", sf.fAutoMount);
6492 if (sf.strAutoMountPoint.isNotEmpty())
6493 pelmThis->setAttribute("autoMountPoint", sf.strAutoMountPoint);
6494 }
6495 }
6496
6497 if (hw.clipboardMode != ClipboardMode_Disabled)
6498 {
6499 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
6500 const char *pcszClip;
6501 switch (hw.clipboardMode)
6502 {
6503 default: /*case ClipboardMode_Disabled:*/ pcszClip = "Disabled"; break;
6504 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
6505 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
6506 case ClipboardMode_Bidirectional: pcszClip = "Bidirectional"; break;
6507 }
6508 pelmClip->setAttribute("mode", pcszClip);
6509 }
6510
6511 if (hw.dndMode != DnDMode_Disabled)
6512 {
6513 xml::ElementNode *pelmDragAndDrop = pelmHardware->createChild("DragAndDrop");
6514 const char *pcszDragAndDrop;
6515 switch (hw.dndMode)
6516 {
6517 default: /*case DnDMode_Disabled:*/ pcszDragAndDrop = "Disabled"; break;
6518 case DnDMode_HostToGuest: pcszDragAndDrop = "HostToGuest"; break;
6519 case DnDMode_GuestToHost: pcszDragAndDrop = "GuestToHost"; break;
6520 case DnDMode_Bidirectional: pcszDragAndDrop = "Bidirectional"; break;
6521 }
6522 pelmDragAndDrop->setAttribute("mode", pcszDragAndDrop);
6523 }
6524
6525 if ( m->sv >= SettingsVersion_v1_10
6526 && !hw.ioSettings.areDefaultSettings())
6527 {
6528 xml::ElementNode *pelmIO = pelmHardware->createChild("IO");
6529 xml::ElementNode *pelmIOCache;
6530
6531 if (!hw.ioSettings.areDefaultSettings())
6532 {
6533 pelmIOCache = pelmIO->createChild("IoCache");
6534 if (!hw.ioSettings.fIOCacheEnabled)
6535 pelmIOCache->setAttribute("enabled", hw.ioSettings.fIOCacheEnabled);
6536 if (hw.ioSettings.ulIOCacheSize != 5)
6537 pelmIOCache->setAttribute("size", hw.ioSettings.ulIOCacheSize);
6538 }
6539
6540 if ( m->sv >= SettingsVersion_v1_11
6541 && hw.ioSettings.llBandwidthGroups.size())
6542 {
6543 xml::ElementNode *pelmBandwidthGroups = pelmIO->createChild("BandwidthGroups");
6544 for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin();
6545 it != hw.ioSettings.llBandwidthGroups.end();
6546 ++it)
6547 {
6548 const BandwidthGroup &gr = *it;
6549 const char *pcszType;
6550 xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup");
6551 pelmThis->setAttribute("name", gr.strName);
6552 switch (gr.enmType)
6553 {
6554 case BandwidthGroupType_Network: pcszType = "Network"; break;
6555 default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break;
6556 }
6557 pelmThis->setAttribute("type", pcszType);
6558 if (m->sv >= SettingsVersion_v1_13)
6559 pelmThis->setAttribute("maxBytesPerSec", gr.cMaxBytesPerSec);
6560 else
6561 pelmThis->setAttribute("maxMbPerSec", gr.cMaxBytesPerSec / _1M);
6562 }
6563 }
6564 }
6565
6566 if ( m->sv >= SettingsVersion_v1_12
6567 && hw.pciAttachments.size())
6568 {
6569 xml::ElementNode *pelmPCI = pelmHardware->createChild("HostPci");
6570 xml::ElementNode *pelmPCIDevices = pelmPCI->createChild("Devices");
6571
6572 for (HostPCIDeviceAttachmentList::const_iterator it = hw.pciAttachments.begin();
6573 it != hw.pciAttachments.end();
6574 ++it)
6575 {
6576 const HostPCIDeviceAttachment &hpda = *it;
6577
6578 xml::ElementNode *pelmThis = pelmPCIDevices->createChild("Device");
6579
6580 pelmThis->setAttribute("host", hpda.uHostAddress);
6581 pelmThis->setAttribute("guest", hpda.uGuestAddress);
6582 pelmThis->setAttribute("name", hpda.strDeviceName);
6583 }
6584 }
6585
6586 if ( m->sv >= SettingsVersion_v1_12
6587 && hw.fEmulatedUSBCardReader)
6588 {
6589 xml::ElementNode *pelmEmulatedUSB = pelmHardware->createChild("EmulatedUSB");
6590
6591 xml::ElementNode *pelmCardReader = pelmEmulatedUSB->createChild("CardReader");
6592 pelmCardReader->setAttribute("enabled", hw.fEmulatedUSBCardReader);
6593 }
6594
6595 if ( m->sv >= SettingsVersion_v1_14
6596 && !hw.strDefaultFrontend.isEmpty())
6597 {
6598 xml::ElementNode *pelmFrontend = pelmHardware->createChild("Frontend");
6599 xml::ElementNode *pelmDefault = pelmFrontend->createChild("Default");
6600 pelmDefault->setAttribute("type", hw.strDefaultFrontend);
6601 }
6602
6603 if (hw.ulMemoryBalloonSize)
6604 {
6605 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
6606 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
6607 }
6608
6609 if (hw.llGuestProperties.size())
6610 {
6611 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
6612 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
6613 it != hw.llGuestProperties.end();
6614 ++it)
6615 {
6616 const GuestProperty &prop = *it;
6617 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
6618 pelmProp->setAttribute("name", prop.strName);
6619 pelmProp->setAttribute("value", prop.strValue);
6620 pelmProp->setAttribute("timestamp", prop.timestamp);
6621 pelmProp->setAttribute("flags", prop.strFlags);
6622 }
6623 }
6624
6625 /** @todo In the future (6.0?) place the storage controllers under \<Hardware\>, because
6626 * this is where it always should've been. What else than hardware are they? */
6627 xml::ElementNode &elmStorageParent = (m->sv > SettingsVersion_Future) ? *pelmHardware : elmParent;
6628 buildStorageControllersXML(elmStorageParent,
6629 hw.storage,
6630 !!(fl & BuildMachineXML_SkipRemovableMedia),
6631 pllElementsWithUuidAttributes);
6632}
6633
6634/**
6635 * Fill a \<Network\> node. Only relevant for XML version >= v1_10.
6636 * @param mode
6637 * @param fEnabled
6638 * @param elmParent
6639 * @param nic
6640 */
6641void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
6642 bool fEnabled,
6643 xml::ElementNode &elmParent,
6644 const NetworkAdapter &nic)
6645{
6646 switch (mode)
6647 {
6648 case NetworkAttachmentType_NAT:
6649 // For the currently active network attachment type we have to
6650 // generate the tag, otherwise the attachment type is lost.
6651 if (fEnabled || !nic.nat.areDefaultSettings())
6652 {
6653 xml::ElementNode *pelmNAT = elmParent.createChild("NAT");
6654
6655 if (!nic.nat.areDefaultSettings())
6656 {
6657 if (nic.nat.strNetwork.length())
6658 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6659 if (nic.nat.strBindIP.length())
6660 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
6661 if (nic.nat.u32Mtu)
6662 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
6663 if (nic.nat.u32SockRcv)
6664 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
6665 if (nic.nat.u32SockSnd)
6666 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
6667 if (nic.nat.u32TcpRcv)
6668 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
6669 if (nic.nat.u32TcpSnd)
6670 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
6671 if (!nic.nat.areDNSDefaultSettings())
6672 {
6673 xml::ElementNode *pelmDNS = pelmNAT->createChild("DNS");
6674 if (!nic.nat.fDNSPassDomain)
6675 pelmDNS->setAttribute("pass-domain", nic.nat.fDNSPassDomain);
6676 if (nic.nat.fDNSProxy)
6677 pelmDNS->setAttribute("use-proxy", nic.nat.fDNSProxy);
6678 if (nic.nat.fDNSUseHostResolver)
6679 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDNSUseHostResolver);
6680 }
6681
6682 if (!nic.nat.areAliasDefaultSettings())
6683 {
6684 xml::ElementNode *pelmAlias = pelmNAT->createChild("Alias");
6685 if (nic.nat.fAliasLog)
6686 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
6687 if (nic.nat.fAliasProxyOnly)
6688 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
6689 if (nic.nat.fAliasUseSamePorts)
6690 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
6691 }
6692
6693 if (!nic.nat.areTFTPDefaultSettings())
6694 {
6695 xml::ElementNode *pelmTFTP;
6696 pelmTFTP = pelmNAT->createChild("TFTP");
6697 if (nic.nat.strTFTPPrefix.length())
6698 pelmTFTP->setAttribute("prefix", nic.nat.strTFTPPrefix);
6699 if (nic.nat.strTFTPBootFile.length())
6700 pelmTFTP->setAttribute("boot-file", nic.nat.strTFTPBootFile);
6701 if (nic.nat.strTFTPNextServer.length())
6702 pelmTFTP->setAttribute("next-server", nic.nat.strTFTPNextServer);
6703 }
6704 buildNATForwardRulesMap(*pelmNAT, nic.nat.mapRules);
6705 }
6706 }
6707 break;
6708
6709 case NetworkAttachmentType_Bridged:
6710 // For the currently active network attachment type we have to
6711 // generate the tag, otherwise the attachment type is lost.
6712 if (fEnabled || !nic.strBridgedName.isEmpty())
6713 {
6714 xml::ElementNode *pelmMode = elmParent.createChild("BridgedInterface");
6715 if (!nic.strBridgedName.isEmpty())
6716 pelmMode->setAttribute("name", nic.strBridgedName);
6717 }
6718 break;
6719
6720 case NetworkAttachmentType_Internal:
6721 // For the currently active network attachment type we have to
6722 // generate the tag, otherwise the attachment type is lost.
6723 if (fEnabled || !nic.strInternalNetworkName.isEmpty())
6724 {
6725 xml::ElementNode *pelmMode = elmParent.createChild("InternalNetwork");
6726 if (!nic.strInternalNetworkName.isEmpty())
6727 pelmMode->setAttribute("name", nic.strInternalNetworkName);
6728 }
6729 break;
6730
6731 case NetworkAttachmentType_HostOnly:
6732 // For the currently active network attachment type we have to
6733 // generate the tag, otherwise the attachment type is lost.
6734 if (fEnabled || !nic.strHostOnlyName.isEmpty())
6735 {
6736 xml::ElementNode *pelmMode = elmParent.createChild("HostOnlyInterface");
6737 if (!nic.strHostOnlyName.isEmpty())
6738 pelmMode->setAttribute("name", nic.strHostOnlyName);
6739 }
6740 break;
6741
6742 case NetworkAttachmentType_Generic:
6743 // For the currently active network attachment type we have to
6744 // generate the tag, otherwise the attachment type is lost.
6745 if (fEnabled || !nic.areGenericDriverDefaultSettings())
6746 {
6747 xml::ElementNode *pelmMode = elmParent.createChild("GenericInterface");
6748 if (!nic.areGenericDriverDefaultSettings())
6749 {
6750 pelmMode->setAttribute("driver", nic.strGenericDriver);
6751 for (StringsMap::const_iterator it = nic.genericProperties.begin();
6752 it != nic.genericProperties.end();
6753 ++it)
6754 {
6755 xml::ElementNode *pelmProp = pelmMode->createChild("Property");
6756 pelmProp->setAttribute("name", it->first);
6757 pelmProp->setAttribute("value", it->second);
6758 }
6759 }
6760 }
6761 break;
6762
6763 case NetworkAttachmentType_NATNetwork:
6764 // For the currently active network attachment type we have to
6765 // generate the tag, otherwise the attachment type is lost.
6766 if (fEnabled || !nic.strNATNetworkName.isEmpty())
6767 {
6768 xml::ElementNode *pelmMode = elmParent.createChild("NATNetwork");
6769 if (!nic.strNATNetworkName.isEmpty())
6770 pelmMode->setAttribute("name", nic.strNATNetworkName);
6771 }
6772 break;
6773
6774 default: /*case NetworkAttachmentType_Null:*/
6775 break;
6776 }
6777}
6778
6779/**
6780 * Creates a \<StorageControllers\> node under elmParent and then writes out the XML
6781 * keys under that. Called for both the \<Machine\> node and for snapshots.
6782 * @param elmParent
6783 * @param st
6784 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
6785 * an empty drive is always written instead. This is for the OVF export case.
6786 * This parameter is ignored unless the settings version is at least v1.9, which
6787 * is always the case when this gets called for OVF export.
6788 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
6789 * pointers to which we will append all elements that we created here that contain
6790 * UUID attributes. This allows the OVF export code to quickly replace the internal
6791 * media UUIDs with the UUIDs of the media that were exported.
6792 */
6793void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
6794 const Storage &st,
6795 bool fSkipRemovableMedia,
6796 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
6797{
6798 if (!st.llStorageControllers.size())
6799 return;
6800 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
6801
6802 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
6803 it != st.llStorageControllers.end();
6804 ++it)
6805 {
6806 const StorageController &sc = *it;
6807
6808 if ( (m->sv < SettingsVersion_v1_9)
6809 && (sc.controllerType == StorageControllerType_I82078)
6810 )
6811 // floppy controller already got written into <Hardware>/<FloppyController> in buildHardwareXML()
6812 // for pre-1.9 settings
6813 continue;
6814
6815 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
6816 com::Utf8Str name = sc.strName;
6817 if (m->sv < SettingsVersion_v1_8)
6818 {
6819 // pre-1.8 settings use shorter controller names, they are
6820 // expanded when reading the settings
6821 if (name == "IDE Controller")
6822 name = "IDE";
6823 else if (name == "SATA Controller")
6824 name = "SATA";
6825 else if (name == "SCSI Controller")
6826 name = "SCSI";
6827 }
6828 pelmController->setAttribute("name", sc.strName);
6829
6830 const char *pcszType;
6831 switch (sc.controllerType)
6832 {
6833 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
6834 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
6835 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
6836 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
6837 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
6838 case StorageControllerType_I82078: pcszType = "I82078"; break;
6839 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
6840 case StorageControllerType_USB: pcszType = "USB"; break;
6841 case StorageControllerType_NVMe: pcszType = "NVMe"; break;
6842 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
6843 }
6844 pelmController->setAttribute("type", pcszType);
6845
6846 pelmController->setAttribute("PortCount", sc.ulPortCount);
6847
6848 if (m->sv >= SettingsVersion_v1_9)
6849 if (sc.ulInstance)
6850 pelmController->setAttribute("Instance", sc.ulInstance);
6851
6852 if (m->sv >= SettingsVersion_v1_10)
6853 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
6854
6855 if (m->sv >= SettingsVersion_v1_11)
6856 pelmController->setAttribute("Bootable", sc.fBootable);
6857
6858 if (sc.controllerType == StorageControllerType_IntelAhci)
6859 {
6860 pelmController->setAttribute("IDE0MasterEmulationPort", 0);
6861 pelmController->setAttribute("IDE0SlaveEmulationPort", 1);
6862 pelmController->setAttribute("IDE1MasterEmulationPort", 2);
6863 pelmController->setAttribute("IDE1SlaveEmulationPort", 3);
6864 }
6865
6866 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
6867 it2 != sc.llAttachedDevices.end();
6868 ++it2)
6869 {
6870 const AttachedDevice &att = *it2;
6871
6872 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
6873 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
6874 // the floppy controller at the top of the loop
6875 if ( att.deviceType == DeviceType_DVD
6876 && m->sv < SettingsVersion_v1_9
6877 )
6878 continue;
6879
6880 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
6881
6882 pcszType = NULL;
6883
6884 switch (att.deviceType)
6885 {
6886 case DeviceType_HardDisk:
6887 pcszType = "HardDisk";
6888 if (att.fNonRotational)
6889 pelmDevice->setAttribute("nonrotational", att.fNonRotational);
6890 if (att.fDiscard)
6891 pelmDevice->setAttribute("discard", att.fDiscard);
6892 break;
6893
6894 case DeviceType_DVD:
6895 pcszType = "DVD";
6896 pelmDevice->setAttribute("passthrough", att.fPassThrough);
6897 if (att.fTempEject)
6898 pelmDevice->setAttribute("tempeject", att.fTempEject);
6899 break;
6900
6901 case DeviceType_Floppy:
6902 pcszType = "Floppy";
6903 break;
6904
6905 default: break; /* Shut up MSC. */
6906 }
6907
6908 pelmDevice->setAttribute("type", pcszType);
6909
6910 if (m->sv >= SettingsVersion_v1_15)
6911 pelmDevice->setAttribute("hotpluggable", att.fHotPluggable);
6912
6913 pelmDevice->setAttribute("port", att.lPort);
6914 pelmDevice->setAttribute("device", att.lDevice);
6915
6916 if (att.strBwGroup.length())
6917 pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup);
6918
6919 // attached image, if any
6920 if (!att.uuid.isZero()
6921 && att.uuid.isValid()
6922 && (att.deviceType == DeviceType_HardDisk
6923 || !fSkipRemovableMedia
6924 )
6925 )
6926 {
6927 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
6928 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
6929
6930 // if caller wants a list of UUID elements, give it to them
6931 if (pllElementsWithUuidAttributes)
6932 pllElementsWithUuidAttributes->push_back(pelmImage);
6933 }
6934 else if ( (m->sv >= SettingsVersion_v1_9)
6935 && (att.strHostDriveSrc.length())
6936 )
6937 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6938 }
6939 }
6940}
6941
6942/**
6943 * Creates a \<Debugging\> node under elmParent and then writes out the XML
6944 * keys under that. Called for both the \<Machine\> node and for snapshots.
6945 *
6946 * @param pElmParent Pointer to the parent element.
6947 * @param pDbg Pointer to the debugging settings.
6948 */
6949void MachineConfigFile::buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg)
6950{
6951 if (m->sv < SettingsVersion_v1_13 || pDbg->areDefaultSettings())
6952 return;
6953
6954 xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");
6955 xml::ElementNode *pElmTracing = pElmDebugging->createChild("Tracing");
6956 pElmTracing->setAttribute("enabled", pDbg->fTracingEnabled);
6957 pElmTracing->setAttribute("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
6958 pElmTracing->setAttribute("config", pDbg->strTracingConfig);
6959}
6960
6961/**
6962 * Creates a \<Autostart\> node under elmParent and then writes out the XML
6963 * keys under that. Called for both the \<Machine\> node and for snapshots.
6964 *
6965 * @param pElmParent Pointer to the parent element.
6966 * @param pAutostart Pointer to the autostart settings.
6967 */
6968void MachineConfigFile::buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart)
6969{
6970 const char *pcszAutostop = NULL;
6971
6972 if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())
6973 return;
6974
6975 xml::ElementNode *pElmAutostart = pElmParent->createChild("Autostart");
6976 pElmAutostart->setAttribute("enabled", pAutostart->fAutostartEnabled);
6977 pElmAutostart->setAttribute("delay", pAutostart->uAutostartDelay);
6978
6979 switch (pAutostart->enmAutostopType)
6980 {
6981 case AutostopType_Disabled: pcszAutostop = "Disabled"; break;
6982 case AutostopType_SaveState: pcszAutostop = "SaveState"; break;
6983 case AutostopType_PowerOff: pcszAutostop = "PowerOff"; break;
6984 case AutostopType_AcpiShutdown: pcszAutostop = "AcpiShutdown"; break;
6985 default: Assert(false); pcszAutostop = "Disabled"; break;
6986 }
6987 pElmAutostart->setAttribute("autostop", pcszAutostop);
6988}
6989
6990/**
6991 * Creates a \<Groups\> node under elmParent and then writes out the XML
6992 * keys under that. Called for the \<Machine\> node only.
6993 *
6994 * @param pElmParent Pointer to the parent element.
6995 * @param pllGroups Pointer to the groups list.
6996 */
6997void MachineConfigFile::buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups)
6998{
6999 if ( m->sv < SettingsVersion_v1_13 || pllGroups->size() == 0
7000 || (pllGroups->size() == 1 && pllGroups->front() == "/"))
7001 return;
7002
7003 xml::ElementNode *pElmGroups = pElmParent->createChild("Groups");
7004 for (StringsList::const_iterator it = pllGroups->begin();
7005 it != pllGroups->end();
7006 ++it)
7007 {
7008 const Utf8Str &group = *it;
7009 xml::ElementNode *pElmGroup = pElmGroups->createChild("Group");
7010 pElmGroup->setAttribute("name", group);
7011 }
7012}
7013
7014/**
7015 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
7016 * for the root snapshot of a machine, if present; elmParent then points to the \<Snapshots\> node under the
7017 * \<Machine\> node to which \<Snapshot\> must be added. This may then recurse for child snapshots.
7018 *
7019 * @param depth
7020 * @param elmParent
7021 * @param snap
7022 */
7023void MachineConfigFile::buildSnapshotXML(uint32_t depth,
7024 xml::ElementNode &elmParent,
7025 const Snapshot &snap)
7026{
7027 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
7028 throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
7029
7030 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
7031
7032 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
7033 pelmSnapshot->setAttribute("name", snap.strName);
7034 pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(snap.timestamp));
7035
7036 if (snap.strStateFile.length())
7037 pelmSnapshot->setAttributePath("stateFile", snap.strStateFile);
7038
7039 if (snap.strDescription.length())
7040 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
7041
7042 // We only skip removable media for OVF, but OVF never includes snapshots.
7043 buildHardwareXML(*pelmSnapshot, snap.hardware, 0 /* fl */, NULL /* pllElementsWithUuidAttributes */);
7044 buildDebuggingXML(pelmSnapshot, &snap.debugging);
7045 buildAutostartXML(pelmSnapshot, &snap.autostart);
7046 // note: Groups exist only for Machine, not for Snapshot
7047
7048 if (snap.llChildSnapshots.size())
7049 {
7050 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
7051 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
7052 it != snap.llChildSnapshots.end();
7053 ++it)
7054 {
7055 const Snapshot &child = *it;
7056 buildSnapshotXML(depth + 1, *pelmChildren, child);
7057 }
7058 }
7059}
7060
7061/**
7062 * Builds the XML DOM tree for the machine config under the given XML element.
7063 *
7064 * This has been separated out from write() so it can be called from elsewhere,
7065 * such as the OVF code, to build machine XML in an existing XML tree.
7066 *
7067 * As a result, this gets called from two locations:
7068 *
7069 * -- MachineConfigFile::write();
7070 *
7071 * -- Appliance::buildXMLForOneVirtualSystem()
7072 *
7073 * In fl, the following flag bits are recognized:
7074 *
7075 * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will
7076 * be written, if present. This is not set when called from OVF because OVF
7077 * has its own variant of a media registry. This flag is ignored unless the
7078 * settings version is at least v1.11 (VirtualBox 4.0).
7079 *
7080 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
7081 * of the machine and write out \<Snapshot\> and possibly more snapshots under
7082 * that, if snapshots are present. Otherwise all snapshots are suppressed
7083 * (when called from OVF).
7084 *
7085 * -- BuildMachineXML_WriteVBoxVersionAttribute: If set, add a settingsVersion
7086 * attribute to the machine tag with the vbox settings version. This is for
7087 * the OVF export case in which we don't have the settings version set in
7088 * the root element.
7089 *
7090 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
7091 * (DVDs, floppies) are silently skipped. This is for the OVF export case
7092 * until we support copying ISO and RAW media as well. This flag is ignored
7093 * unless the settings version is at least v1.9, which is always the case
7094 * when this gets called for OVF export.
7095 *
7096 * -- BuildMachineXML_SuppressSavedState: If set, the Machine/stateFile
7097 * attribute is never set. This is also for the OVF export case because we
7098 * cannot save states with OVF.
7099 *
7100 * @param elmMachine XML \<Machine\> element to add attributes and elements to.
7101 * @param fl Flags.
7102 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
7103 * see buildStorageControllersXML() for details.
7104 */
7105void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
7106 uint32_t fl,
7107 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
7108{
7109 if (fl & BuildMachineXML_WriteVBoxVersionAttribute)
7110 {
7111 // add settings version attribute to machine element
7112 setVersionAttribute(elmMachine);
7113 LogRel(("Exporting settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
7114 }
7115
7116 elmMachine.setAttribute("uuid", uuid.toStringCurly());
7117 elmMachine.setAttribute("name", machineUserData.strName);
7118 if (machineUserData.fDirectoryIncludesUUID)
7119 elmMachine.setAttribute("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
7120 if (!machineUserData.fNameSync)
7121 elmMachine.setAttribute("nameSync", machineUserData.fNameSync);
7122 if (machineUserData.strDescription.length())
7123 elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
7124 elmMachine.setAttribute("OSType", machineUserData.strOsType);
7125 if ( strStateFile.length()
7126 && !(fl & BuildMachineXML_SuppressSavedState)
7127 )
7128 elmMachine.setAttributePath("stateFile", strStateFile);
7129
7130 if ((fl & BuildMachineXML_IncludeSnapshots)
7131 && !uuidCurrentSnapshot.isZero()
7132 && uuidCurrentSnapshot.isValid())
7133 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
7134
7135 if (machineUserData.strSnapshotFolder.length())
7136 elmMachine.setAttributePath("snapshotFolder", machineUserData.strSnapshotFolder);
7137 if (!fCurrentStateModified)
7138 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
7139 elmMachine.setAttribute("lastStateChange", stringifyTimestamp(timeLastStateChange));
7140 if (fAborted)
7141 elmMachine.setAttribute("aborted", fAborted);
7142 if (machineUserData.strVMPriority.length())
7143 elmMachine.setAttribute("processPriority", machineUserData.strVMPriority);
7144 // Please keep the icon last so that one doesn't have to check if there
7145 // is anything in the line after this very long attribute in the XML.
7146 if (machineUserData.ovIcon.size())
7147 {
7148 Utf8Str strIcon;
7149 toBase64(strIcon, machineUserData.ovIcon);
7150 elmMachine.setAttribute("icon", strIcon);
7151 }
7152 if ( m->sv >= SettingsVersion_v1_9
7153 && ( machineUserData.fTeleporterEnabled
7154 || machineUserData.uTeleporterPort
7155 || !machineUserData.strTeleporterAddress.isEmpty()
7156 || !machineUserData.strTeleporterPassword.isEmpty()
7157 )
7158 )
7159 {
7160 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
7161 pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled);
7162 pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort);
7163 pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress);
7164 pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword);
7165 }
7166
7167 if ( m->sv >= SettingsVersion_v1_11
7168 && ( machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
7169 || machineUserData.uFaultTolerancePort
7170 || machineUserData.uFaultToleranceInterval
7171 || !machineUserData.strFaultToleranceAddress.isEmpty()
7172 )
7173 )
7174 {
7175 xml::ElementNode *pelmFaultTolerance = elmMachine.createChild("FaultTolerance");
7176 switch (machineUserData.enmFaultToleranceState)
7177 {
7178 case FaultToleranceState_Inactive:
7179 pelmFaultTolerance->setAttribute("state", "inactive");
7180 break;
7181 case FaultToleranceState_Master:
7182 pelmFaultTolerance->setAttribute("state", "master");
7183 break;
7184 case FaultToleranceState_Standby:
7185 pelmFaultTolerance->setAttribute("state", "standby");
7186 break;
7187#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
7188 case FaultToleranceState_32BitHack: /* (compiler warnings) */
7189 AssertFailedBreak();
7190#endif
7191 }
7192
7193 pelmFaultTolerance->setAttribute("port", machineUserData.uFaultTolerancePort);
7194 pelmFaultTolerance->setAttribute("address", machineUserData.strFaultToleranceAddress);
7195 pelmFaultTolerance->setAttribute("interval", machineUserData.uFaultToleranceInterval);
7196 pelmFaultTolerance->setAttribute("password", machineUserData.strFaultTolerancePassword);
7197 }
7198
7199 if ( (fl & BuildMachineXML_MediaRegistry)
7200 && (m->sv >= SettingsVersion_v1_11)
7201 )
7202 buildMediaRegistry(elmMachine, mediaRegistry);
7203
7204 buildExtraData(elmMachine, mapExtraDataItems);
7205
7206 if ( (fl & BuildMachineXML_IncludeSnapshots)
7207 && llFirstSnapshot.size())
7208 buildSnapshotXML(1, elmMachine, llFirstSnapshot.front());
7209
7210 buildHardwareXML(elmMachine, hardwareMachine, fl, pllElementsWithUuidAttributes);
7211 buildDebuggingXML(&elmMachine, &debugging);
7212 buildAutostartXML(&elmMachine, &autostart);
7213 buildGroupsXML(&elmMachine, &machineUserData.llGroups);
7214}
7215
7216/**
7217 * Returns true only if the given AudioDriverType is supported on
7218 * the current host platform. For example, this would return false
7219 * for AudioDriverType_DirectSound when compiled on a Linux host.
7220 * @param drv AudioDriverType_* enum to test.
7221 * @return true only if the current host supports that driver.
7222 */
7223/*static*/
7224bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
7225{
7226 switch (drv)
7227 {
7228 case AudioDriverType_Null:
7229#ifdef RT_OS_WINDOWS
7230 case AudioDriverType_DirectSound:
7231#endif
7232#ifdef VBOX_WITH_AUDIO_OSS
7233 case AudioDriverType_OSS:
7234#endif
7235#ifdef VBOX_WITH_AUDIO_ALSA
7236 case AudioDriverType_ALSA:
7237#endif
7238#ifdef VBOX_WITH_AUDIO_PULSE
7239 case AudioDriverType_Pulse:
7240#endif
7241#ifdef RT_OS_DARWIN
7242 case AudioDriverType_CoreAudio:
7243#endif
7244#ifdef RT_OS_OS2
7245 case AudioDriverType_MMPM:
7246#endif
7247 return true;
7248 default: break; /* Shut up MSC. */
7249 }
7250
7251 return false;
7252}
7253
7254/**
7255 * Returns the AudioDriverType_* which should be used by default on this
7256 * host platform. On Linux, this will check at runtime whether PulseAudio
7257 * or ALSA are actually supported on the first call.
7258 *
7259 * @return Default audio driver type for this host platform.
7260 */
7261/*static*/
7262AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
7263{
7264#if defined(RT_OS_WINDOWS)
7265 return AudioDriverType_DirectSound;
7266
7267#elif defined(RT_OS_LINUX)
7268 /* On Linux, we need to check at runtime what's actually supported. */
7269 static RTCLockMtx s_mtx;
7270 static AudioDriverType_T s_enmLinuxDriver = AudioDriverType_Null;
7271 RTCLock lock(s_mtx);
7272 if (s_enmLinuxDriver == AudioDriverType_Null)
7273 {
7274# ifdef VBOX_WITH_AUDIO_PULSE
7275 /* Check for the pulse library & that the pulse audio daemon is running. */
7276 if (RTProcIsRunningByName("pulseaudio") &&
7277 RTLdrIsLoadable("libpulse.so.0"))
7278 s_enmLinuxDriver = AudioDriverType_Pulse;
7279 else
7280# endif /* VBOX_WITH_AUDIO_PULSE */
7281# ifdef VBOX_WITH_AUDIO_ALSA
7282 /* Check if we can load the ALSA library */
7283 if (RTLdrIsLoadable("libasound.so.2"))
7284 s_enmLinuxDriver = AudioDriverType_ALSA;
7285 else
7286# endif /* VBOX_WITH_AUDIO_ALSA */
7287 s_enmLinuxDriver = AudioDriverType_OSS;
7288 }
7289 return s_enmLinuxDriver;
7290
7291#elif defined(RT_OS_DARWIN)
7292 return AudioDriverType_CoreAudio;
7293
7294#elif defined(RT_OS_OS2)
7295 return AudioDriverType_MMPM;
7296
7297#else /* All other platforms. */
7298# ifdef VBOX_WITH_AUDIO_OSS
7299 return AudioDriverType_OSS;
7300# else
7301 /* Return NULL driver as a fallback if nothing of the above is available. */
7302 return AudioDriverType_Null;
7303# endif
7304#endif
7305}
7306
7307/**
7308 * Called from write() before calling ConfigFileBase::createStubDocument().
7309 * This adjusts the settings version in m->sv if incompatible settings require
7310 * a settings bump, whereas otherwise we try to preserve the settings version
7311 * to avoid breaking compatibility with older versions.
7312 *
7313 * We do the checks in here in reverse order: newest first, oldest last, so
7314 * that we avoid unnecessary checks since some of these are expensive.
7315 */
7316void MachineConfigFile::bumpSettingsVersionIfNeeded()
7317{
7318 if (m->sv < SettingsVersion_v1_17)
7319 {
7320 // VirtualBox 6.0 adds nested hardware virtualization, using native API (NEM).
7321 if ( hardwareMachine.fNestedHWVirt
7322 || hardwareMachine.fUseNativeApi)
7323 {
7324 m->sv = SettingsVersion_v1_17;
7325 return;
7326 }
7327 if (hardwareMachine.llSharedFolders.size())
7328 for (SharedFoldersList::const_iterator it = hardwareMachine.llSharedFolders.begin();
7329 it != hardwareMachine.llSharedFolders.end();
7330 ++it)
7331 if (it->strAutoMountPoint.isNotEmpty())
7332 {
7333 m->sv = SettingsVersion_v1_17;
7334 return;
7335 }
7336
7337 /*
7338 * Check if any serial port uses a non 16550A serial port.
7339 */
7340 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
7341 it != hardwareMachine.llSerialPorts.end();
7342 ++it)
7343 {
7344 const SerialPort &port = *it;
7345 if (port.uartType != UartType_U16550A)
7346 {
7347 m->sv = SettingsVersion_v1_17;
7348 return;
7349 }
7350 }
7351 }
7352
7353 if (m->sv < SettingsVersion_v1_16)
7354 {
7355 // VirtualBox 5.1 adds a NVMe storage controller, paravirt debug
7356 // options, cpu profile, APIC settings (CPU capability and BIOS).
7357
7358 if ( hardwareMachine.strParavirtDebug.isNotEmpty()
7359 || (!hardwareMachine.strCpuProfile.equals("host") && hardwareMachine.strCpuProfile.isNotEmpty())
7360 || hardwareMachine.biosSettings.apicMode != APICMode_APIC
7361 || !hardwareMachine.fAPIC
7362 || hardwareMachine.fX2APIC
7363 || hardwareMachine.fIBPBOnVMExit
7364 || hardwareMachine.fIBPBOnVMEntry
7365 || hardwareMachine.fSpecCtrl
7366 || hardwareMachine.fSpecCtrlByHost
7367 || !hardwareMachine.fL1DFlushOnSched
7368 || hardwareMachine.fL1DFlushOnVMEntry)
7369 {
7370 m->sv = SettingsVersion_v1_16;
7371 return;
7372 }
7373
7374 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7375 it != hardwareMachine.storage.llStorageControllers.end();
7376 ++it)
7377 {
7378 const StorageController &sctl = *it;
7379
7380 if (sctl.controllerType == StorageControllerType_NVMe)
7381 {
7382 m->sv = SettingsVersion_v1_16;
7383 return;
7384 }
7385 }
7386
7387 for (CpuIdLeafsList::const_iterator it = hardwareMachine.llCpuIdLeafs.begin();
7388 it != hardwareMachine.llCpuIdLeafs.end();
7389 ++it)
7390 if (it->idxSub != 0)
7391 {
7392 m->sv = SettingsVersion_v1_16;
7393 return;
7394 }
7395 }
7396
7397 if (m->sv < SettingsVersion_v1_15)
7398 {
7399 // VirtualBox 5.0 adds paravirt providers, explicit AHCI port hotplug
7400 // setting, USB storage controller, xHCI, serial port TCP backend
7401 // and VM process priority.
7402
7403 /*
7404 * Check simple configuration bits first, loopy stuff afterwards.
7405 */
7406 if ( hardwareMachine.paravirtProvider != ParavirtProvider_Legacy
7407 || hardwareMachine.uCpuIdPortabilityLevel != 0
7408 || machineUserData.strVMPriority.length())
7409 {
7410 m->sv = SettingsVersion_v1_15;
7411 return;
7412 }
7413
7414 /*
7415 * Check whether the hotpluggable flag of all storage devices differs
7416 * from the default for old settings.
7417 * AHCI ports are hotpluggable by default every other device is not.
7418 * Also check if there are USB storage controllers.
7419 */
7420 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7421 it != hardwareMachine.storage.llStorageControllers.end();
7422 ++it)
7423 {
7424 const StorageController &sctl = *it;
7425
7426 if (sctl.controllerType == StorageControllerType_USB)
7427 {
7428 m->sv = SettingsVersion_v1_15;
7429 return;
7430 }
7431
7432 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
7433 it2 != sctl.llAttachedDevices.end();
7434 ++it2)
7435 {
7436 const AttachedDevice &att = *it2;
7437
7438 if ( ( att.fHotPluggable
7439 && sctl.controllerType != StorageControllerType_IntelAhci)
7440 || ( !att.fHotPluggable
7441 && sctl.controllerType == StorageControllerType_IntelAhci))
7442 {
7443 m->sv = SettingsVersion_v1_15;
7444 return;
7445 }
7446 }
7447 }
7448
7449 /*
7450 * Check if there is an xHCI (USB3) USB controller.
7451 */
7452 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
7453 it != hardwareMachine.usbSettings.llUSBControllers.end();
7454 ++it)
7455 {
7456 const USBController &ctrl = *it;
7457 if (ctrl.enmType == USBControllerType_XHCI)
7458 {
7459 m->sv = SettingsVersion_v1_15;
7460 return;
7461 }
7462 }
7463
7464 /*
7465 * Check if any serial port uses the TCP backend.
7466 */
7467 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
7468 it != hardwareMachine.llSerialPorts.end();
7469 ++it)
7470 {
7471 const SerialPort &port = *it;
7472 if (port.portMode == PortMode_TCP)
7473 {
7474 m->sv = SettingsVersion_v1_15;
7475 return;
7476 }
7477 }
7478 }
7479
7480 if (m->sv < SettingsVersion_v1_14)
7481 {
7482 // VirtualBox 4.3 adds default frontend setting, graphics controller
7483 // setting, explicit long mode setting, (video) capturing and NAT networking.
7484 if ( !hardwareMachine.strDefaultFrontend.isEmpty()
7485 || hardwareMachine.graphicsControllerType != GraphicsControllerType_VBoxVGA
7486 || hardwareMachine.enmLongMode != Hardware::LongMode_Legacy
7487 || machineUserData.ovIcon.size() > 0
7488 || hardwareMachine.recordingSettings.fEnabled)
7489 {
7490 m->sv = SettingsVersion_v1_14;
7491 return;
7492 }
7493 NetworkAdaptersList::const_iterator netit;
7494 for (netit = hardwareMachine.llNetworkAdapters.begin();
7495 netit != hardwareMachine.llNetworkAdapters.end();
7496 ++netit)
7497 {
7498 if (netit->mode == NetworkAttachmentType_NATNetwork)
7499 {
7500 m->sv = SettingsVersion_v1_14;
7501 break;
7502 }
7503 }
7504 }
7505
7506 if (m->sv < SettingsVersion_v1_14)
7507 {
7508 unsigned cOhciCtrls = 0;
7509 unsigned cEhciCtrls = 0;
7510 bool fNonStdName = false;
7511
7512 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
7513 it != hardwareMachine.usbSettings.llUSBControllers.end();
7514 ++it)
7515 {
7516 const USBController &ctrl = *it;
7517
7518 switch (ctrl.enmType)
7519 {
7520 case USBControllerType_OHCI:
7521 cOhciCtrls++;
7522 if (ctrl.strName != "OHCI")
7523 fNonStdName = true;
7524 break;
7525 case USBControllerType_EHCI:
7526 cEhciCtrls++;
7527 if (ctrl.strName != "EHCI")
7528 fNonStdName = true;
7529 break;
7530 default:
7531 /* Anything unknown forces a bump. */
7532 fNonStdName = true;
7533 }
7534
7535 /* Skip checking other controllers if the settings bump is necessary. */
7536 if (cOhciCtrls > 1 || cEhciCtrls > 1 || fNonStdName)
7537 {
7538 m->sv = SettingsVersion_v1_14;
7539 break;
7540 }
7541 }
7542 }
7543
7544 if (m->sv < SettingsVersion_v1_13)
7545 {
7546 // VirtualBox 4.2 adds tracing, autostart, UUID in directory and groups.
7547 if ( !debugging.areDefaultSettings()
7548 || !autostart.areDefaultSettings()
7549 || machineUserData.fDirectoryIncludesUUID
7550 || machineUserData.llGroups.size() > 1
7551 || machineUserData.llGroups.front() != "/")
7552 m->sv = SettingsVersion_v1_13;
7553 }
7554
7555 if (m->sv < SettingsVersion_v1_13)
7556 {
7557 // VirtualBox 4.2 changes the units for bandwidth group limits.
7558 for (BandwidthGroupList::const_iterator it = hardwareMachine.ioSettings.llBandwidthGroups.begin();
7559 it != hardwareMachine.ioSettings.llBandwidthGroups.end();
7560 ++it)
7561 {
7562 const BandwidthGroup &gr = *it;
7563 if (gr.cMaxBytesPerSec % _1M)
7564 {
7565 // Bump version if a limit cannot be expressed in megabytes
7566 m->sv = SettingsVersion_v1_13;
7567 break;
7568 }
7569 }
7570 }
7571
7572 if (m->sv < SettingsVersion_v1_12)
7573 {
7574 // VirtualBox 4.1 adds PCI passthrough and emulated USB Smart Card reader
7575 if ( hardwareMachine.pciAttachments.size()
7576 || hardwareMachine.fEmulatedUSBCardReader)
7577 m->sv = SettingsVersion_v1_12;
7578 }
7579
7580 if (m->sv < SettingsVersion_v1_12)
7581 {
7582 // VirtualBox 4.1 adds a promiscuous mode policy to the network
7583 // adapters and a generic network driver transport.
7584 NetworkAdaptersList::const_iterator netit;
7585 for (netit = hardwareMachine.llNetworkAdapters.begin();
7586 netit != hardwareMachine.llNetworkAdapters.end();
7587 ++netit)
7588 {
7589 if ( netit->enmPromiscModePolicy != NetworkAdapterPromiscModePolicy_Deny
7590 || netit->mode == NetworkAttachmentType_Generic
7591 || !netit->areGenericDriverDefaultSettings()
7592 )
7593 {
7594 m->sv = SettingsVersion_v1_12;
7595 break;
7596 }
7597 }
7598 }
7599
7600 if (m->sv < SettingsVersion_v1_11)
7601 {
7602 // VirtualBox 4.0 adds HD audio, CPU priorities, fault tolerance,
7603 // per-machine media registries, VRDE, JRockitVE, bandwidth groups,
7604 // ICH9 chipset
7605 if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
7606 || hardwareMachine.ulCpuExecutionCap != 100
7607 || machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
7608 || machineUserData.uFaultTolerancePort
7609 || machineUserData.uFaultToleranceInterval
7610 || !machineUserData.strFaultToleranceAddress.isEmpty()
7611 || mediaRegistry.llHardDisks.size()
7612 || mediaRegistry.llDvdImages.size()
7613 || mediaRegistry.llFloppyImages.size()
7614 || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty()
7615 || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty()
7616 || machineUserData.strOsType == "JRockitVE"
7617 || hardwareMachine.ioSettings.llBandwidthGroups.size()
7618 || hardwareMachine.chipsetType == ChipsetType_ICH9
7619 )
7620 m->sv = SettingsVersion_v1_11;
7621 }
7622
7623 if (m->sv < SettingsVersion_v1_10)
7624 {
7625 /* If the properties contain elements other than "TCP/Ports" and "TCP/Address",
7626 * then increase the version to at least VBox 3.2, which can have video channel properties.
7627 */
7628 unsigned cOldProperties = 0;
7629
7630 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
7631 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7632 cOldProperties++;
7633 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
7634 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7635 cOldProperties++;
7636
7637 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
7638 m->sv = SettingsVersion_v1_10;
7639 }
7640
7641 if (m->sv < SettingsVersion_v1_11)
7642 {
7643 /* If the properties contain elements other than "TCP/Ports", "TCP/Address",
7644 * "VideoChannel/Enabled" and "VideoChannel/Quality" then increase the version to VBox 4.0.
7645 */
7646 unsigned cOldProperties = 0;
7647
7648 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
7649 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7650 cOldProperties++;
7651 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
7652 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7653 cOldProperties++;
7654 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
7655 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7656 cOldProperties++;
7657 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Quality");
7658 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7659 cOldProperties++;
7660
7661 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
7662 m->sv = SettingsVersion_v1_11;
7663 }
7664
7665 // settings version 1.9 is required if there is not exactly one DVD
7666 // or more than one floppy drive present or the DVD is not at the secondary
7667 // master; this check is a bit more complicated
7668 //
7669 // settings version 1.10 is required if the host cache should be disabled
7670 //
7671 // settings version 1.11 is required for bandwidth limits and if more than
7672 // one controller of each type is present.
7673 if (m->sv < SettingsVersion_v1_11)
7674 {
7675 // count attached DVDs and floppies (only if < v1.9)
7676 size_t cDVDs = 0;
7677 size_t cFloppies = 0;
7678
7679 // count storage controllers (if < v1.11)
7680 size_t cSata = 0;
7681 size_t cScsiLsi = 0;
7682 size_t cScsiBuslogic = 0;
7683 size_t cSas = 0;
7684 size_t cIde = 0;
7685 size_t cFloppy = 0;
7686
7687 // need to run thru all the storage controllers and attached devices to figure this out
7688 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7689 it != hardwareMachine.storage.llStorageControllers.end();
7690 ++it)
7691 {
7692 const StorageController &sctl = *it;
7693
7694 // count storage controllers of each type; 1.11 is required if more than one
7695 // controller of one type is present
7696 switch (sctl.storageBus)
7697 {
7698 case StorageBus_IDE:
7699 cIde++;
7700 break;
7701 case StorageBus_SATA:
7702 cSata++;
7703 break;
7704 case StorageBus_SAS:
7705 cSas++;
7706 break;
7707 case StorageBus_SCSI:
7708 if (sctl.controllerType == StorageControllerType_LsiLogic)
7709 cScsiLsi++;
7710 else
7711 cScsiBuslogic++;
7712 break;
7713 case StorageBus_Floppy:
7714 cFloppy++;
7715 break;
7716 default:
7717 // Do nothing
7718 break;
7719 }
7720
7721 if ( cSata > 1
7722 || cScsiLsi > 1
7723 || cScsiBuslogic > 1
7724 || cSas > 1
7725 || cIde > 1
7726 || cFloppy > 1)
7727 {
7728 m->sv = SettingsVersion_v1_11;
7729 break; // abort the loop -- we will not raise the version further
7730 }
7731
7732 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
7733 it2 != sctl.llAttachedDevices.end();
7734 ++it2)
7735 {
7736 const AttachedDevice &att = *it2;
7737
7738 // Bandwidth limitations are new in VirtualBox 4.0 (1.11)
7739 if (m->sv < SettingsVersion_v1_11)
7740 {
7741 if (att.strBwGroup.length() != 0)
7742 {
7743 m->sv = SettingsVersion_v1_11;
7744 break; // abort the loop -- we will not raise the version further
7745 }
7746 }
7747
7748 // disabling the host IO cache requires settings version 1.10
7749 if ( (m->sv < SettingsVersion_v1_10)
7750 && (!sctl.fUseHostIOCache)
7751 )
7752 m->sv = SettingsVersion_v1_10;
7753
7754 // we can only write the StorageController/@Instance attribute with v1.9
7755 if ( (m->sv < SettingsVersion_v1_9)
7756 && (sctl.ulInstance != 0)
7757 )
7758 m->sv = SettingsVersion_v1_9;
7759
7760 if (m->sv < SettingsVersion_v1_9)
7761 {
7762 if (att.deviceType == DeviceType_DVD)
7763 {
7764 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
7765 || (att.lPort != 1) // DVDs not at secondary master?
7766 || (att.lDevice != 0)
7767 )
7768 m->sv = SettingsVersion_v1_9;
7769
7770 ++cDVDs;
7771 }
7772 else if (att.deviceType == DeviceType_Floppy)
7773 ++cFloppies;
7774 }
7775 }
7776
7777 if (m->sv >= SettingsVersion_v1_11)
7778 break; // abort the loop -- we will not raise the version further
7779 }
7780
7781 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
7782 // so any deviation from that will require settings version 1.9
7783 if ( (m->sv < SettingsVersion_v1_9)
7784 && ( (cDVDs != 1)
7785 || (cFloppies > 1)
7786 )
7787 )
7788 m->sv = SettingsVersion_v1_9;
7789 }
7790
7791 // VirtualBox 3.2: Check for non default I/O settings
7792 if (m->sv < SettingsVersion_v1_10)
7793 {
7794 if ( (hardwareMachine.ioSettings.fIOCacheEnabled != true)
7795 || (hardwareMachine.ioSettings.ulIOCacheSize != 5)
7796 // and page fusion
7797 || (hardwareMachine.fPageFusionEnabled)
7798 // and CPU hotplug, RTC timezone control, HID type and HPET
7799 || machineUserData.fRTCUseUTC
7800 || hardwareMachine.fCpuHotPlug
7801 || hardwareMachine.pointingHIDType != PointingHIDType_PS2Mouse
7802 || hardwareMachine.keyboardHIDType != KeyboardHIDType_PS2Keyboard
7803 || hardwareMachine.fHPETEnabled
7804 )
7805 m->sv = SettingsVersion_v1_10;
7806 }
7807
7808 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main
7809 // VirtualBox 4.0 adds network bandwitdth
7810 if (m->sv < SettingsVersion_v1_11)
7811 {
7812 NetworkAdaptersList::const_iterator netit;
7813 for (netit = hardwareMachine.llNetworkAdapters.begin();
7814 netit != hardwareMachine.llNetworkAdapters.end();
7815 ++netit)
7816 {
7817 if ( (m->sv < SettingsVersion_v1_12)
7818 && (netit->strBandwidthGroup.isNotEmpty())
7819 )
7820 {
7821 /* New in VirtualBox 4.1 */
7822 m->sv = SettingsVersion_v1_12;
7823 break;
7824 }
7825 else if ( (m->sv < SettingsVersion_v1_10)
7826 && (netit->fEnabled)
7827 && (netit->mode == NetworkAttachmentType_NAT)
7828 && ( netit->nat.u32Mtu != 0
7829 || netit->nat.u32SockRcv != 0
7830 || netit->nat.u32SockSnd != 0
7831 || netit->nat.u32TcpRcv != 0
7832 || netit->nat.u32TcpSnd != 0
7833 || !netit->nat.fDNSPassDomain
7834 || netit->nat.fDNSProxy
7835 || netit->nat.fDNSUseHostResolver
7836 || netit->nat.fAliasLog
7837 || netit->nat.fAliasProxyOnly
7838 || netit->nat.fAliasUseSamePorts
7839 || netit->nat.strTFTPPrefix.length()
7840 || netit->nat.strTFTPBootFile.length()
7841 || netit->nat.strTFTPNextServer.length()
7842 || netit->nat.mapRules.size()
7843 )
7844 )
7845 {
7846 m->sv = SettingsVersion_v1_10;
7847 // no break because we still might need v1.11 above
7848 }
7849 else if ( (m->sv < SettingsVersion_v1_10)
7850 && (netit->fEnabled)
7851 && (netit->ulBootPriority != 0)
7852 )
7853 {
7854 m->sv = SettingsVersion_v1_10;
7855 // no break because we still might need v1.11 above
7856 }
7857 }
7858 }
7859
7860 // all the following require settings version 1.9
7861 if ( (m->sv < SettingsVersion_v1_9)
7862 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
7863 || machineUserData.fTeleporterEnabled
7864 || machineUserData.uTeleporterPort
7865 || !machineUserData.strTeleporterAddress.isEmpty()
7866 || !machineUserData.strTeleporterPassword.isEmpty()
7867 || (!hardwareMachine.uuid.isZero() && hardwareMachine.uuid.isValid())
7868 )
7869 )
7870 m->sv = SettingsVersion_v1_9;
7871
7872 // "accelerate 2d video" requires settings version 1.8
7873 if ( (m->sv < SettingsVersion_v1_8)
7874 && (hardwareMachine.fAccelerate2DVideo)
7875 )
7876 m->sv = SettingsVersion_v1_8;
7877
7878 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
7879 if ( m->sv < SettingsVersion_v1_4
7880 && hardwareMachine.strVersion != "1"
7881 )
7882 m->sv = SettingsVersion_v1_4;
7883}
7884
7885/**
7886 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
7887 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
7888 * in particular if the file cannot be written.
7889 */
7890void MachineConfigFile::write(const com::Utf8Str &strFilename)
7891{
7892 try
7893 {
7894 // createStubDocument() sets the settings version to at least 1.7; however,
7895 // we might need to enfore a later settings version if incompatible settings
7896 // are present:
7897 bumpSettingsVersionIfNeeded();
7898
7899 m->strFilename = strFilename;
7900 specialBackupIfFirstBump();
7901 createStubDocument();
7902
7903 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
7904 buildMachineXML(*pelmMachine,
7905 MachineConfigFile::BuildMachineXML_IncludeSnapshots
7906 | MachineConfigFile::BuildMachineXML_MediaRegistry,
7907 // but not BuildMachineXML_WriteVBoxVersionAttribute
7908 NULL); /* pllElementsWithUuidAttributes */
7909
7910 // now go write the XML
7911 xml::XmlFileWriter writer(*m->pDoc);
7912 writer.write(m->strFilename.c_str(), true /*fSafe*/);
7913
7914 m->fFileExists = true;
7915 clearDocument();
7916 }
7917 catch (...)
7918 {
7919 clearDocument();
7920 throw;
7921 }
7922}
Note: See TracBrowser for help on using the repository browser.

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