VirtualBox

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

Last change on this file since 94369 was 93561, checked in by vboxsync, 3 years ago

Work around inability of XPCOM to handle enums starting with a digit.

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