VirtualBox

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

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

CloudNet: ​bugref:9469 Replace local gateway with DrvCloudTunnel. Build 25519 encryption support in libssh. Add missing version bump in machine settings to 1.18 if cloud attachment is used.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 343.5 KB
Line 
1/* $Id: Settings.cpp 93312 2022-01-18 13:15:12Z 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
4116 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
4117 }
4118
4119 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
4120 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
4121 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
4122 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
4123
4124 if (pelmAdapter->getAttributeValue("promiscuousModePolicy", strTemp))
4125 {
4126 if (strTemp == "Deny")
4127 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
4128 else if (strTemp == "AllowNetwork")
4129 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
4130 else if (strTemp == "AllowAll")
4131 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
4132 else
4133 throw ConfigFileError(this, pelmAdapter,
4134 N_("Invalid value '%s' in Adapter/@promiscuousModePolicy attribute"), strTemp.c_str());
4135 }
4136
4137 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
4138 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
4139 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
4140 pelmAdapter->getAttributeValue("bandwidthGroup", nic.strBandwidthGroup);
4141
4142 xml::ElementNodesList llNetworkModes;
4143 pelmAdapter->getChildElements(llNetworkModes);
4144 xml::ElementNodesList::iterator it;
4145 /* We should have only active mode descriptor and disabled modes set */
4146 if (llNetworkModes.size() > 2)
4147 {
4148 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
4149 }
4150 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
4151 {
4152 const xml::ElementNode *pelmNode = *it;
4153 if (pelmNode->nameEquals("DisabledModes"))
4154 {
4155 xml::ElementNodesList llDisabledNetworkModes;
4156 xml::ElementNodesList::iterator itDisabled;
4157 pelmNode->getChildElements(llDisabledNetworkModes);
4158 /* run over disabled list and load settings */
4159 for (itDisabled = llDisabledNetworkModes.begin();
4160 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
4161 {
4162 const xml::ElementNode *pelmDisabledNode = *itDisabled;
4163 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
4164 }
4165 }
4166 else
4167 readAttachedNetworkMode(*pelmNode, true, nic);
4168 }
4169 // else: default is NetworkAttachmentType_Null
4170
4171 ll.push_back(nic);
4172 }
4173}
4174
4175void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
4176{
4177 NetworkAttachmentType_T enmAttachmentType = NetworkAttachmentType_Null;
4178
4179 if (elmMode.nameEquals("NAT"))
4180 {
4181 enmAttachmentType = NetworkAttachmentType_NAT;
4182
4183 elmMode.getAttributeValue("network", nic.nat.strNetwork);
4184 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
4185 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
4186 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
4187 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
4188 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
4189 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
4190 elmMode.getAttributeValue("localhost-reachable", nic.nat.fLocalhostReachable);
4191 const xml::ElementNode *pelmDNS;
4192 if ((pelmDNS = elmMode.findChildElement("DNS")))
4193 {
4194 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDNSPassDomain);
4195 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDNSProxy);
4196 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDNSUseHostResolver);
4197 }
4198 const xml::ElementNode *pelmAlias;
4199 if ((pelmAlias = elmMode.findChildElement("Alias")))
4200 {
4201 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
4202 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
4203 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
4204 }
4205 const xml::ElementNode *pelmTFTP;
4206 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
4207 {
4208 pelmTFTP->getAttributeValue("prefix", nic.nat.strTFTPPrefix);
4209 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTFTPBootFile);
4210 pelmTFTP->getAttributeValue("next-server", nic.nat.strTFTPNextServer);
4211 }
4212
4213 readNATForwardRulesMap(elmMode, nic.nat.mapRules);
4214 }
4215 else if ( elmMode.nameEquals("HostInterface")
4216 || elmMode.nameEquals("BridgedInterface"))
4217 {
4218 enmAttachmentType = NetworkAttachmentType_Bridged;
4219
4220 // optional network name, cannot be required or we have trouble with
4221 // settings which are saved before configuring the network name
4222 elmMode.getAttributeValue("name", nic.strBridgedName);
4223 }
4224 else if (elmMode.nameEquals("InternalNetwork"))
4225 {
4226 enmAttachmentType = NetworkAttachmentType_Internal;
4227
4228 // optional network name, cannot be required or we have trouble with
4229 // settings which are saved before configuring the network name
4230 elmMode.getAttributeValue("name", nic.strInternalNetworkName);
4231 }
4232 else if (elmMode.nameEquals("HostOnlyInterface"))
4233 {
4234 enmAttachmentType = NetworkAttachmentType_HostOnly;
4235
4236 // optional network name, cannot be required or we have trouble with
4237 // settings which are saved before configuring the network name
4238 elmMode.getAttributeValue("name", nic.strHostOnlyName);
4239 }
4240#ifdef VBOX_WITH_VMNET
4241 else if (elmMode.nameEquals("HostOnlyNetwork"))
4242 {
4243 enmAttachmentType = NetworkAttachmentType_HostOnlyNetwork;
4244
4245 // optional network name, cannot be required or we have trouble with
4246 // settings which are saved before configuring the network name
4247 elmMode.getAttributeValue("name", nic.strHostOnlyNetworkName);
4248 }
4249#endif /* VBOX_WITH_VMNET */
4250 else if (elmMode.nameEquals("GenericInterface"))
4251 {
4252 enmAttachmentType = NetworkAttachmentType_Generic;
4253
4254 elmMode.getAttributeValue("driver", nic.strGenericDriver); // optional network attachment driver
4255
4256 // get all properties
4257 xml::NodesLoop nl(elmMode);
4258 const xml::ElementNode *pelmModeChild;
4259 while ((pelmModeChild = nl.forAllNodes()))
4260 {
4261 if (pelmModeChild->nameEquals("Property"))
4262 {
4263 Utf8Str strPropName, strPropValue;
4264 if ( pelmModeChild->getAttributeValue("name", strPropName)
4265 && pelmModeChild->getAttributeValue("value", strPropValue) )
4266 nic.genericProperties[strPropName] = strPropValue;
4267 else
4268 throw ConfigFileError(this, pelmModeChild, N_("Required GenericInterface/Property/@name or @value attribute is missing"));
4269 }
4270 }
4271 }
4272 else if (elmMode.nameEquals("NATNetwork"))
4273 {
4274 enmAttachmentType = NetworkAttachmentType_NATNetwork;
4275
4276 // optional network name, cannot be required or we have trouble with
4277 // settings which are saved before configuring the network name
4278 elmMode.getAttributeValue("name", nic.strNATNetworkName);
4279 }
4280 else if (elmMode.nameEquals("VDE"))
4281 {
4282 // inofficial hack (VDE networking was never part of the official
4283 // settings, so it's not mentioned in VirtualBox-settings.xsd)
4284 enmAttachmentType = NetworkAttachmentType_Generic;
4285
4286 com::Utf8Str strVDEName;
4287 elmMode.getAttributeValue("network", strVDEName); // optional network name
4288 nic.strGenericDriver = "VDE";
4289 nic.genericProperties["network"] = strVDEName;
4290 }
4291#ifdef VBOX_WITH_VMNET
4292 else if (elmMode.nameEquals("HostOnlyNetwork"))
4293 {
4294 enmAttachmentType = NetworkAttachmentType_HostOnly;
4295
4296 // optional network name, cannot be required or we have trouble with
4297 // settings which are saved before configuring the network name
4298 elmMode.getAttributeValue("name", nic.strHostOnlyNetworkName);
4299 }
4300#endif /* VBOX_WITH_VMNET */
4301#ifdef VBOX_WITH_CLOUD_NET
4302 else if (elmMode.nameEquals("CloudNetwork"))
4303 {
4304 enmAttachmentType = NetworkAttachmentType_Cloud;
4305
4306 // optional network name, cannot be required or we have trouble with
4307 // settings which are saved before configuring the network name
4308 elmMode.getAttributeValue("name", nic.strCloudNetworkName);
4309 }
4310#endif /* VBOX_WITH_CLOUD_NET */
4311
4312 if (fEnabled && enmAttachmentType != NetworkAttachmentType_Null)
4313 nic.mode = enmAttachmentType;
4314}
4315
4316/**
4317 * Called from MachineConfigFile::readHardware() to read serial port information.
4318 * @param elmUART
4319 * @param ll
4320 */
4321void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
4322 SerialPortsList &ll)
4323{
4324 xml::NodesLoop nl1(elmUART, "Port");
4325 const xml::ElementNode *pelmPort;
4326 while ((pelmPort = nl1.forAllNodes()))
4327 {
4328 SerialPort port;
4329 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
4330 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
4331
4332 // slot must be unique
4333 for (SerialPortsList::const_iterator it = ll.begin();
4334 it != ll.end();
4335 ++it)
4336 if ((*it).ulSlot == port.ulSlot)
4337 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
4338
4339 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
4340 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
4341 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
4342 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
4343 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
4344 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
4345
4346 Utf8Str strPortMode;
4347 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
4348 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
4349 if (strPortMode == "RawFile")
4350 port.portMode = PortMode_RawFile;
4351 else if (strPortMode == "HostPipe")
4352 port.portMode = PortMode_HostPipe;
4353 else if (strPortMode == "HostDevice")
4354 port.portMode = PortMode_HostDevice;
4355 else if (strPortMode == "Disconnected")
4356 port.portMode = PortMode_Disconnected;
4357 else if (strPortMode == "TCP")
4358 port.portMode = PortMode_TCP;
4359 else
4360 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
4361
4362 pelmPort->getAttributeValue("path", port.strPath);
4363 pelmPort->getAttributeValue("server", port.fServer);
4364
4365 Utf8Str strUartType;
4366 if (pelmPort->getAttributeValue("uartType", strUartType))
4367 {
4368 if (strUartType == "16450")
4369 port.uartType = UartType_U16450;
4370 else if (strUartType == "16550A")
4371 port.uartType = UartType_U16550A;
4372 else if (strUartType == "16750")
4373 port.uartType = UartType_U16750;
4374 else
4375 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@uartType attribute"), strUartType.c_str());
4376 }
4377
4378 ll.push_back(port);
4379 }
4380}
4381
4382/**
4383 * Called from MachineConfigFile::readHardware() to read parallel port information.
4384 * @param elmLPT
4385 * @param ll
4386 */
4387void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
4388 ParallelPortsList &ll)
4389{
4390 xml::NodesLoop nl1(elmLPT, "Port");
4391 const xml::ElementNode *pelmPort;
4392 while ((pelmPort = nl1.forAllNodes()))
4393 {
4394 ParallelPort port;
4395 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
4396 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
4397
4398 // slot must be unique
4399 for (ParallelPortsList::const_iterator it = ll.begin();
4400 it != ll.end();
4401 ++it)
4402 if ((*it).ulSlot == port.ulSlot)
4403 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
4404
4405 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
4406 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
4407 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
4408 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
4409 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
4410 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
4411
4412 pelmPort->getAttributeValue("path", port.strPath);
4413
4414 ll.push_back(port);
4415 }
4416}
4417
4418/**
4419 * Called from MachineConfigFile::readHardware() to read audio adapter information
4420 * and maybe fix driver information depending on the current host hardware.
4421 *
4422 * @param elmAudioAdapter "AudioAdapter" XML element.
4423 * @param aa
4424 */
4425void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
4426 AudioAdapter &aa)
4427{
4428 if (m->sv >= SettingsVersion_v1_15)
4429 {
4430 // get all properties
4431 xml::NodesLoop nl1(elmAudioAdapter, "Property");
4432 const xml::ElementNode *pelmModeChild;
4433 while ((pelmModeChild = nl1.forAllNodes()))
4434 {
4435 Utf8Str strPropName, strPropValue;
4436 if ( pelmModeChild->getAttributeValue("name", strPropName)
4437 && pelmModeChild->getAttributeValue("value", strPropValue) )
4438 aa.properties[strPropName] = strPropValue;
4439 else
4440 throw ConfigFileError(this, pelmModeChild, N_("Required AudioAdapter/Property/@name or @value attribute "
4441 "is missing"));
4442 }
4443 }
4444
4445 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
4446 elmAudioAdapter.getAttributeValue("enabledIn", aa.fEnabledIn);
4447 elmAudioAdapter.getAttributeValue("enabledOut", aa.fEnabledOut);
4448
4449 Utf8Str strTemp;
4450 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
4451 {
4452 if (strTemp == "SB16")
4453 aa.controllerType = AudioControllerType_SB16;
4454 else if (strTemp == "AC97")
4455 aa.controllerType = AudioControllerType_AC97;
4456 else if (strTemp == "HDA")
4457 aa.controllerType = AudioControllerType_HDA;
4458 else
4459 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
4460 }
4461
4462 if (elmAudioAdapter.getAttributeValue("codec", strTemp))
4463 {
4464 if (strTemp == "SB16")
4465 aa.codecType = AudioCodecType_SB16;
4466 else if (strTemp == "STAC9700")
4467 aa.codecType = AudioCodecType_STAC9700;
4468 else if (strTemp == "AD1980")
4469 aa.codecType = AudioCodecType_AD1980;
4470 else if (strTemp == "STAC9221")
4471 aa.codecType = AudioCodecType_STAC9221;
4472 else
4473 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@codec attribute"), strTemp.c_str());
4474 }
4475 else
4476 {
4477 /* No codec attribute provided; use defaults. */
4478 switch (aa.controllerType)
4479 {
4480 case AudioControllerType_AC97:
4481 aa.codecType = AudioCodecType_STAC9700;
4482 break;
4483 case AudioControllerType_SB16:
4484 aa.codecType = AudioCodecType_SB16;
4485 break;
4486 case AudioControllerType_HDA:
4487 aa.codecType = AudioCodecType_STAC9221;
4488 break;
4489 default:
4490 Assert(false); /* We just checked the controller type above. */
4491 }
4492 }
4493
4494 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
4495 {
4496 // settings before 1.3 used lower case so make sure this is case-insensitive
4497 strTemp.toUpper();
4498 if (strTemp == "NULL")
4499 aa.driverType = AudioDriverType_Null;
4500 else if (strTemp == "WINMM")
4501 aa.driverType = AudioDriverType_WinMM;
4502 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
4503 aa.driverType = AudioDriverType_DirectSound;
4504 else if (strTemp == "SOLAUDIO") /* Deprecated -- Solaris will use OSS by default now. */
4505 aa.driverType = AudioDriverType_SolAudio;
4506 else if (strTemp == "ALSA")
4507 aa.driverType = AudioDriverType_ALSA;
4508 else if (strTemp == "PULSE")
4509 aa.driverType = AudioDriverType_Pulse;
4510 else if (strTemp == "OSS")
4511 aa.driverType = AudioDriverType_OSS;
4512 else if (strTemp == "COREAUDIO")
4513 aa.driverType = AudioDriverType_CoreAudio;
4514 else if (strTemp == "MMPM")
4515 aa.driverType = AudioDriverType_MMPM;
4516 else
4517 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
4518
4519 // now check if this is actually supported on the current host platform;
4520 // people might be opening a file created on a Windows host, and that
4521 // VM should still start on a Linux host
4522 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
4523 aa.driverType = getHostDefaultAudioDriver();
4524 }
4525}
4526
4527/**
4528 * Called from MachineConfigFile::readHardware() to read guest property information.
4529 * @param elmGuestProperties
4530 * @param hw
4531 */
4532void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
4533 Hardware &hw)
4534{
4535 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
4536 const xml::ElementNode *pelmProp;
4537 while ((pelmProp = nl1.forAllNodes()))
4538 {
4539 GuestProperty prop;
4540 pelmProp->getAttributeValue("name", prop.strName);
4541 pelmProp->getAttributeValue("value", prop.strValue);
4542
4543 pelmProp->getAttributeValue("timestamp", prop.timestamp);
4544 pelmProp->getAttributeValue("flags", prop.strFlags);
4545 hw.llGuestProperties.push_back(prop);
4546 }
4547}
4548
4549/**
4550 * Helper function to read attributes that are common to \<SATAController\> (pre-1.7)
4551 * and \<StorageController\>.
4552 * @param elmStorageController
4553 * @param sctl
4554 */
4555void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
4556 StorageController &sctl)
4557{
4558 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
4559 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
4560}
4561
4562/**
4563 * Reads in a \<Hardware\> block and stores it in the given structure. Used
4564 * both directly from readMachine and from readSnapshot, since snapshots
4565 * have their own hardware sections.
4566 *
4567 * For legacy pre-1.7 settings we also need a storage structure because
4568 * the IDE and SATA controllers used to be defined under \<Hardware\>.
4569 *
4570 * @param elmHardware
4571 * @param hw
4572 */
4573void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
4574 Hardware &hw)
4575{
4576 if (m->sv >= SettingsVersion_v1_16)
4577 {
4578 /* Starting with VirtualBox 5.1 the default is Default, before it was
4579 * Legacy. This needs to matched by areParavirtDefaultSettings(). */
4580 hw.paravirtProvider = ParavirtProvider_Default;
4581 /* The new default is disabled, before it was enabled by default. */
4582 hw.vrdeSettings.fEnabled = false;
4583 /* The new default is disabled, before it was enabled by default. */
4584 hw.audioAdapter.fEnabled = false;
4585 }
4586
4587 if (m->sv >= SettingsVersion_v1_17)
4588 {
4589 /* Starting with VirtualBox 5.2 the default is disabled, before it was
4590 * enabled. This needs to matched by AudioAdapter::areDefaultSettings(). */
4591 hw.audioAdapter.fEnabledIn = false;
4592 /* The new default is disabled, before it was enabled by default. */
4593 hw.audioAdapter.fEnabledOut = false;
4594 }
4595
4596 if (!elmHardware.getAttributeValue("version", hw.strVersion))
4597 {
4598 /* KLUDGE ALERT! For a while during the 3.1 development this was not
4599 written because it was thought to have a default value of "2". For
4600 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
4601 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
4602 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
4603 missing the hardware version, then it probably should be "2" instead
4604 of "1". */
4605 if (m->sv < SettingsVersion_v1_7)
4606 hw.strVersion = "1";
4607 else
4608 hw.strVersion = "2";
4609 }
4610 Utf8Str strUUID;
4611 if (elmHardware.getAttributeValue("uuid", strUUID))
4612 parseUUID(hw.uuid, strUUID, &elmHardware);
4613
4614 xml::NodesLoop nl1(elmHardware);
4615 const xml::ElementNode *pelmHwChild;
4616 while ((pelmHwChild = nl1.forAllNodes()))
4617 {
4618 if (pelmHwChild->nameEquals("CPU"))
4619 {
4620 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
4621 {
4622 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
4623 const xml::ElementNode *pelmCPUChild;
4624 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
4625 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
4626 }
4627
4628 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
4629 pelmHwChild->getAttributeValue("executionCap", hw.ulCpuExecutionCap);
4630
4631 const xml::ElementNode *pelmCPUChild;
4632 if (hw.fCpuHotPlug)
4633 {
4634 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
4635 readCpuTree(*pelmCPUChild, hw.llCpus);
4636 }
4637
4638 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
4639 {
4640 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
4641 }
4642 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
4643 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
4644 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
4645 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
4646 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
4647 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
4648 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUX")))
4649 pelmCPUChild->getAttributeValue("enabled", hw.fUnrestrictedExecution);
4650 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtForce")))
4651 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirtForce);
4652 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUseNativeApi")))
4653 pelmCPUChild->getAttributeValue("enabled", hw.fUseNativeApi);
4654 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVirtVmsaveVmload")))
4655 pelmCPUChild->getAttributeValue("enabled", hw.fVirtVmsaveVmload);
4656
4657 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
4658 {
4659 /* The default for pre 3.1 was false, so we must respect that. */
4660 if (m->sv < SettingsVersion_v1_9)
4661 hw.fPAE = false;
4662 }
4663 else
4664 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
4665
4666 bool fLongMode;
4667 if ( (pelmCPUChild = pelmHwChild->findChildElement("LongMode"))
4668 && pelmCPUChild->getAttributeValue("enabled", fLongMode) )
4669 hw.enmLongMode = fLongMode ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled;
4670 else
4671 hw.enmLongMode = Hardware::LongMode_Legacy;
4672
4673 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
4674 {
4675 bool fSyntheticCpu = false;
4676 pelmCPUChild->getAttributeValue("enabled", fSyntheticCpu);
4677 hw.uCpuIdPortabilityLevel = fSyntheticCpu ? 1 : 0;
4678 }
4679 pelmHwChild->getAttributeValue("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
4680 pelmHwChild->getAttributeValue("CpuProfile", hw.strCpuProfile);
4681
4682 if ((pelmCPUChild = pelmHwChild->findChildElement("TripleFaultReset")))
4683 pelmCPUChild->getAttributeValue("enabled", hw.fTripleFaultReset);
4684
4685 if ((pelmCPUChild = pelmHwChild->findChildElement("APIC")))
4686 pelmCPUChild->getAttributeValue("enabled", hw.fAPIC);
4687 if ((pelmCPUChild = pelmHwChild->findChildElement("X2APIC")))
4688 pelmCPUChild->getAttributeValue("enabled", hw.fX2APIC);
4689 if (hw.fX2APIC)
4690 hw.fAPIC = true;
4691 pelmCPUChild = pelmHwChild->findChildElement("IBPBOn");
4692 if (pelmCPUChild)
4693 {
4694 pelmCPUChild->getAttributeValue("vmexit", hw.fIBPBOnVMExit);
4695 pelmCPUChild->getAttributeValue("vmentry", hw.fIBPBOnVMEntry);
4696 }
4697 pelmCPUChild = pelmHwChild->findChildElement("SpecCtrl");
4698 if (pelmCPUChild)
4699 pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrl);
4700 pelmCPUChild = pelmHwChild->findChildElement("SpecCtrlByHost");
4701 if (pelmCPUChild)
4702 pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrlByHost);
4703 pelmCPUChild = pelmHwChild->findChildElement("L1DFlushOn");
4704 if (pelmCPUChild)
4705 {
4706 pelmCPUChild->getAttributeValue("scheduling", hw.fL1DFlushOnSched);
4707 pelmCPUChild->getAttributeValue("vmentry", hw.fL1DFlushOnVMEntry);
4708 }
4709 pelmCPUChild = pelmHwChild->findChildElement("MDSClearOn");
4710 if (pelmCPUChild)
4711 {
4712 pelmCPUChild->getAttributeValue("scheduling", hw.fMDSClearOnSched);
4713 pelmCPUChild->getAttributeValue("vmentry", hw.fMDSClearOnVMEntry);
4714 }
4715 pelmCPUChild = pelmHwChild->findChildElement("NestedHWVirt");
4716 if (pelmCPUChild)
4717 pelmCPUChild->getAttributeValue("enabled", hw.fNestedHWVirt);
4718
4719 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
4720 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
4721 }
4722 else if (pelmHwChild->nameEquals("Memory"))
4723 {
4724 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
4725 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
4726 }
4727 else if (pelmHwChild->nameEquals("Firmware"))
4728 {
4729 Utf8Str strFirmwareType;
4730 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
4731 {
4732 if ( (strFirmwareType == "BIOS")
4733 || (strFirmwareType == "1") // some trunk builds used the number here
4734 )
4735 hw.firmwareType = FirmwareType_BIOS;
4736 else if ( (strFirmwareType == "EFI")
4737 || (strFirmwareType == "2") // some trunk builds used the number here
4738 )
4739 hw.firmwareType = FirmwareType_EFI;
4740 else if ( strFirmwareType == "EFI32")
4741 hw.firmwareType = FirmwareType_EFI32;
4742 else if ( strFirmwareType == "EFI64")
4743 hw.firmwareType = FirmwareType_EFI64;
4744 else if ( strFirmwareType == "EFIDUAL")
4745 hw.firmwareType = FirmwareType_EFIDUAL;
4746 else
4747 throw ConfigFileError(this,
4748 pelmHwChild,
4749 N_("Invalid value '%s' in Firmware/@type"),
4750 strFirmwareType.c_str());
4751 }
4752 }
4753 else if (pelmHwChild->nameEquals("HID"))
4754 {
4755 Utf8Str strHIDType;
4756 if (pelmHwChild->getAttributeValue("Keyboard", strHIDType))
4757 {
4758 if (strHIDType == "None")
4759 hw.keyboardHIDType = KeyboardHIDType_None;
4760 else if (strHIDType == "USBKeyboard")
4761 hw.keyboardHIDType = KeyboardHIDType_USBKeyboard;
4762 else if (strHIDType == "PS2Keyboard")
4763 hw.keyboardHIDType = KeyboardHIDType_PS2Keyboard;
4764 else if (strHIDType == "ComboKeyboard")
4765 hw.keyboardHIDType = KeyboardHIDType_ComboKeyboard;
4766 else
4767 throw ConfigFileError(this,
4768 pelmHwChild,
4769 N_("Invalid value '%s' in HID/Keyboard/@type"),
4770 strHIDType.c_str());
4771 }
4772 if (pelmHwChild->getAttributeValue("Pointing", strHIDType))
4773 {
4774 if (strHIDType == "None")
4775 hw.pointingHIDType = PointingHIDType_None;
4776 else if (strHIDType == "USBMouse")
4777 hw.pointingHIDType = PointingHIDType_USBMouse;
4778 else if (strHIDType == "USBTablet")
4779 hw.pointingHIDType = PointingHIDType_USBTablet;
4780 else if (strHIDType == "PS2Mouse")
4781 hw.pointingHIDType = PointingHIDType_PS2Mouse;
4782 else if (strHIDType == "ComboMouse")
4783 hw.pointingHIDType = PointingHIDType_ComboMouse;
4784 else if (strHIDType == "USBMultiTouch")
4785 hw.pointingHIDType = PointingHIDType_USBMultiTouch;
4786 else
4787 throw ConfigFileError(this,
4788 pelmHwChild,
4789 N_("Invalid value '%s' in HID/Pointing/@type"),
4790 strHIDType.c_str());
4791 }
4792 }
4793 else if (pelmHwChild->nameEquals("Chipset"))
4794 {
4795 Utf8Str strChipsetType;
4796 if (pelmHwChild->getAttributeValue("type", strChipsetType))
4797 {
4798 if (strChipsetType == "PIIX3")
4799 hw.chipsetType = ChipsetType_PIIX3;
4800 else if (strChipsetType == "ICH9")
4801 hw.chipsetType = ChipsetType_ICH9;
4802 else
4803 throw ConfigFileError(this,
4804 pelmHwChild,
4805 N_("Invalid value '%s' in Chipset/@type"),
4806 strChipsetType.c_str());
4807 }
4808 }
4809 else if (pelmHwChild->nameEquals("Iommu"))
4810 {
4811 Utf8Str strIommuType;
4812 if (pelmHwChild->getAttributeValue("type", strIommuType))
4813 {
4814 if (strIommuType == "None")
4815 hw.iommuType = IommuType_None;
4816 else if (strIommuType == "Automatic")
4817 hw.iommuType = IommuType_Automatic;
4818 else if (strIommuType == "AMD")
4819 hw.iommuType = IommuType_AMD;
4820 else if (strIommuType == "Intel")
4821 hw.iommuType = IommuType_Intel;
4822 else
4823 throw ConfigFileError(this,
4824 pelmHwChild,
4825 N_("Invalid value '%s' in Iommu/@type"),
4826 strIommuType.c_str());
4827 }
4828 }
4829 else if (pelmHwChild->nameEquals("Paravirt"))
4830 {
4831 Utf8Str strProvider;
4832 if (pelmHwChild->getAttributeValue("provider", strProvider))
4833 {
4834 if (strProvider == "None")
4835 hw.paravirtProvider = ParavirtProvider_None;
4836 else if (strProvider == "Default")
4837 hw.paravirtProvider = ParavirtProvider_Default;
4838 else if (strProvider == "Legacy")
4839 hw.paravirtProvider = ParavirtProvider_Legacy;
4840 else if (strProvider == "Minimal")
4841 hw.paravirtProvider = ParavirtProvider_Minimal;
4842 else if (strProvider == "HyperV")
4843 hw.paravirtProvider = ParavirtProvider_HyperV;
4844 else if (strProvider == "KVM")
4845 hw.paravirtProvider = ParavirtProvider_KVM;
4846 else
4847 throw ConfigFileError(this,
4848 pelmHwChild,
4849 N_("Invalid value '%s' in Paravirt/@provider attribute"),
4850 strProvider.c_str());
4851 }
4852
4853 pelmHwChild->getAttributeValue("debug", hw.strParavirtDebug);
4854 }
4855 else if (pelmHwChild->nameEquals("HPET"))
4856 {
4857 pelmHwChild->getAttributeValue("enabled", hw.fHPETEnabled);
4858 }
4859 else if (pelmHwChild->nameEquals("Boot"))
4860 {
4861 hw.mapBootOrder.clear();
4862
4863 xml::NodesLoop nl2(*pelmHwChild, "Order");
4864 const xml::ElementNode *pelmOrder;
4865 while ((pelmOrder = nl2.forAllNodes()))
4866 {
4867 uint32_t ulPos;
4868 Utf8Str strDevice;
4869 if (!pelmOrder->getAttributeValue("position", ulPos))
4870 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
4871
4872 if ( ulPos < 1
4873 || ulPos > SchemaDefs::MaxBootPosition
4874 )
4875 throw ConfigFileError(this,
4876 pelmOrder,
4877 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
4878 ulPos,
4879 SchemaDefs::MaxBootPosition + 1);
4880 // XML is 1-based but internal data is 0-based
4881 --ulPos;
4882
4883 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
4884 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
4885
4886 if (!pelmOrder->getAttributeValue("device", strDevice))
4887 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
4888
4889 DeviceType_T type;
4890 if (strDevice == "None")
4891 type = DeviceType_Null;
4892 else if (strDevice == "Floppy")
4893 type = DeviceType_Floppy;
4894 else if (strDevice == "DVD")
4895 type = DeviceType_DVD;
4896 else if (strDevice == "HardDisk")
4897 type = DeviceType_HardDisk;
4898 else if (strDevice == "Network")
4899 type = DeviceType_Network;
4900 else
4901 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
4902 hw.mapBootOrder[ulPos] = type;
4903 }
4904 }
4905 else if (pelmHwChild->nameEquals("Display"))
4906 {
4907 Utf8Str strGraphicsControllerType;
4908 if (!pelmHwChild->getAttributeValue("controller", strGraphicsControllerType))
4909 hw.graphicsAdapter.graphicsControllerType = GraphicsControllerType_VBoxVGA;
4910 else
4911 {
4912 strGraphicsControllerType.toUpper();
4913 GraphicsControllerType_T type;
4914 if (strGraphicsControllerType == "VBOXVGA")
4915 type = GraphicsControllerType_VBoxVGA;
4916 else if (strGraphicsControllerType == "VMSVGA")
4917 type = GraphicsControllerType_VMSVGA;
4918 else if (strGraphicsControllerType == "VBOXSVGA")
4919 type = GraphicsControllerType_VBoxSVGA;
4920 else if (strGraphicsControllerType == "NONE")
4921 type = GraphicsControllerType_Null;
4922 else
4923 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Display/@controller attribute"), strGraphicsControllerType.c_str());
4924 hw.graphicsAdapter.graphicsControllerType = type;
4925 }
4926 pelmHwChild->getAttributeValue("VRAMSize", hw.graphicsAdapter.ulVRAMSizeMB);
4927 if (!pelmHwChild->getAttributeValue("monitorCount", hw.graphicsAdapter.cMonitors))
4928 pelmHwChild->getAttributeValue("MonitorCount", hw.graphicsAdapter.cMonitors); // pre-v1.5 variant
4929 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.graphicsAdapter.fAccelerate3D))
4930 pelmHwChild->getAttributeValue("Accelerate3D", hw.graphicsAdapter.fAccelerate3D); // pre-v1.5 variant
4931 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.graphicsAdapter.fAccelerate2DVideo);
4932 }
4933 else if (pelmHwChild->nameEquals("VideoCapture"))
4934 {
4935 pelmHwChild->getAttributeValue("enabled", hw.recordingSettings.fEnabled);
4936
4937 /* Right now I don't want to bump the settings version, so just convert the enabled
4938 * screens to the former uint64t_t bit array and vice versa. */
4939 uint64_t u64VideoCaptureScreens;
4940 pelmHwChild->getAttributeValue("screens", u64VideoCaptureScreens);
4941
4942 /* At the moment we only support one capturing configuration, that is, all screens
4943 * have the same configuration. So load/save to/from screen 0. */
4944 Assert(hw.recordingSettings.mapScreens.size()); /* At least screen must be present. */
4945 RecordingScreenSettings &screen0Settings = hw.recordingSettings.mapScreens[0];
4946
4947 pelmHwChild->getAttributeValue("maxTime", screen0Settings.ulMaxTimeS);
4948 pelmHwChild->getAttributeValue("options", screen0Settings.strOptions);
4949 pelmHwChild->getAttributeValuePath("file", screen0Settings.File.strName);
4950 pelmHwChild->getAttributeValue("maxSize", screen0Settings.File.ulMaxSizeMB);
4951 pelmHwChild->getAttributeValue("horzRes", screen0Settings.Video.ulWidth);
4952 pelmHwChild->getAttributeValue("vertRes", screen0Settings.Video.ulHeight);
4953 pelmHwChild->getAttributeValue("rate", screen0Settings.Video.ulRate);
4954 pelmHwChild->getAttributeValue("fps", screen0Settings.Video.ulFPS);
4955
4956 for (unsigned i = 0; i < hw.graphicsAdapter.cMonitors; i++) /* Don't add more settings than we have monitors configured. */
4957 {
4958 /* Add screen i to config in any case. */
4959 hw.recordingSettings.mapScreens[i] = screen0Settings;
4960
4961 if (u64VideoCaptureScreens & RT_BIT_64(i)) /* Screen i enabled? */
4962 hw.recordingSettings.mapScreens[i].fEnabled = true;
4963 }
4964 }
4965 else if (pelmHwChild->nameEquals("RemoteDisplay"))
4966 {
4967 pelmHwChild->getAttributeValue("enabled", hw.vrdeSettings.fEnabled);
4968
4969 Utf8Str str;
4970 if (pelmHwChild->getAttributeValue("port", str))
4971 hw.vrdeSettings.mapProperties["TCP/Ports"] = str;
4972 if (pelmHwChild->getAttributeValue("netAddress", str))
4973 hw.vrdeSettings.mapProperties["TCP/Address"] = str;
4974
4975 Utf8Str strAuthType;
4976 if (pelmHwChild->getAttributeValue("authType", strAuthType))
4977 {
4978 // settings before 1.3 used lower case so make sure this is case-insensitive
4979 strAuthType.toUpper();
4980 if (strAuthType == "NULL")
4981 hw.vrdeSettings.authType = AuthType_Null;
4982 else if (strAuthType == "GUEST")
4983 hw.vrdeSettings.authType = AuthType_Guest;
4984 else if (strAuthType == "EXTERNAL")
4985 hw.vrdeSettings.authType = AuthType_External;
4986 else
4987 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
4988 }
4989
4990 pelmHwChild->getAttributeValue("authLibrary", hw.vrdeSettings.strAuthLibrary);
4991 pelmHwChild->getAttributeValue("authTimeout", hw.vrdeSettings.ulAuthTimeout);
4992 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
4993 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
4994
4995 /* 3.2 and 4.0 betas, 4.0 has this information in VRDEProperties. */
4996 const xml::ElementNode *pelmVideoChannel;
4997 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
4998 {
4999 bool fVideoChannel = false;
5000 pelmVideoChannel->getAttributeValue("enabled", fVideoChannel);
5001 hw.vrdeSettings.mapProperties["VideoChannel/Enabled"] = fVideoChannel? "true": "false";
5002
5003 uint32_t ulVideoChannelQuality = 75;
5004 pelmVideoChannel->getAttributeValue("quality", ulVideoChannelQuality);
5005 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
5006 char *pszBuffer = NULL;
5007 if (RTStrAPrintf(&pszBuffer, "%d", ulVideoChannelQuality) >= 0)
5008 {
5009 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = pszBuffer;
5010 RTStrFree(pszBuffer);
5011 }
5012 else
5013 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = "75";
5014 }
5015 pelmHwChild->getAttributeValue("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
5016
5017 const xml::ElementNode *pelmProperties = pelmHwChild->findChildElement("VRDEProperties");
5018 if (pelmProperties != NULL)
5019 {
5020 xml::NodesLoop nl(*pelmProperties);
5021 const xml::ElementNode *pelmProperty;
5022 while ((pelmProperty = nl.forAllNodes()))
5023 {
5024 if (pelmProperty->nameEquals("Property"))
5025 {
5026 /* <Property name="TCP/Ports" value="3000-3002"/> */
5027 Utf8Str strName, strValue;
5028 if ( pelmProperty->getAttributeValue("name", strName)
5029 && pelmProperty->getAttributeValue("value", strValue))
5030 hw.vrdeSettings.mapProperties[strName] = strValue;
5031 else
5032 throw ConfigFileError(this, pelmProperty, N_("Required VRDE Property/@name or @value attribute is missing"));
5033 }
5034 }
5035 }
5036 }
5037 else if (pelmHwChild->nameEquals("BIOS"))
5038 {
5039 const xml::ElementNode *pelmBIOSChild;
5040 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
5041 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
5042 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
5043 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
5044 if ((pelmBIOSChild = pelmHwChild->findChildElement("APIC")))
5045 {
5046 Utf8Str strAPIC;
5047 if (pelmBIOSChild->getAttributeValue("mode", strAPIC))
5048 {
5049 strAPIC.toUpper();
5050 if (strAPIC == "DISABLED")
5051 hw.biosSettings.apicMode = APICMode_Disabled;
5052 else if (strAPIC == "APIC")
5053 hw.biosSettings.apicMode = APICMode_APIC;
5054 else if (strAPIC == "X2APIC")
5055 hw.biosSettings.apicMode = APICMode_X2APIC;
5056 else
5057 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in APIC/@mode attribute"), strAPIC.c_str());
5058 }
5059 }
5060 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
5061 {
5062 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
5063 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
5064 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
5065 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
5066 }
5067 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
5068 {
5069 Utf8Str strBootMenuMode;
5070 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
5071 {
5072 // settings before 1.3 used lower case so make sure this is case-insensitive
5073 strBootMenuMode.toUpper();
5074 if (strBootMenuMode == "DISABLED")
5075 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
5076 else if (strBootMenuMode == "MENUONLY")
5077 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
5078 else if (strBootMenuMode == "MESSAGEANDMENU")
5079 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
5080 else
5081 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
5082 }
5083 }
5084 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
5085 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
5086 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
5087 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
5088 if ((pelmBIOSChild = pelmHwChild->findChildElement("NVRAM")))
5089 pelmBIOSChild->getAttributeValue("path", hw.nvramSettings.strNvramPath);
5090 if ((pelmBIOSChild = pelmHwChild->findChildElement("SmbiosUuidLittleEndian")))
5091 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fSmbiosUuidLittleEndian);
5092 else
5093 hw.biosSettings.fSmbiosUuidLittleEndian = false; /* Default for existing VMs. */
5094
5095 // legacy BIOS/IDEController (pre 1.7)
5096 if ( (m->sv < SettingsVersion_v1_7)
5097 && (pelmBIOSChild = pelmHwChild->findChildElement("IDEController"))
5098 )
5099 {
5100 StorageController sctl;
5101 sctl.strName = "IDE Controller";
5102 sctl.storageBus = StorageBus_IDE;
5103
5104 Utf8Str strType;
5105 if (pelmBIOSChild->getAttributeValue("type", strType))
5106 {
5107 if (strType == "PIIX3")
5108 sctl.controllerType = StorageControllerType_PIIX3;
5109 else if (strType == "PIIX4")
5110 sctl.controllerType = StorageControllerType_PIIX4;
5111 else if (strType == "ICH6")
5112 sctl.controllerType = StorageControllerType_ICH6;
5113 else
5114 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
5115 }
5116 sctl.ulPortCount = 2;
5117 hw.storage.llStorageControllers.push_back(sctl);
5118 }
5119 }
5120 else if (pelmHwChild->nameEquals("TrustedPlatformModule"))
5121 {
5122 Utf8Str strTpmType;
5123 if (pelmHwChild->getAttributeValue("type", strTpmType))
5124 {
5125 if (strTpmType == "None")
5126 hw.tpmSettings.tpmType = TpmType_None;
5127 else if (strTpmType == "v1_2")
5128 hw.tpmSettings.tpmType = TpmType_v1_2;
5129 else if (strTpmType == "v2_0")
5130 hw.tpmSettings.tpmType = TpmType_v2_0;
5131 else if (strTpmType == "Host")
5132 hw.tpmSettings.tpmType = TpmType_Host;
5133 else if (strTpmType == "Swtpm")
5134 hw.tpmSettings.tpmType = TpmType_Swtpm;
5135 else
5136 throw ConfigFileError(this,
5137 pelmHwChild,
5138 N_("Invalid value '%s' in TrustedPlatformModule/@type"),
5139 strTpmType.c_str());
5140 }
5141
5142 pelmHwChild->getAttributeValue("location", hw.tpmSettings.strLocation);
5143 }
5144 else if ( (m->sv <= SettingsVersion_v1_14)
5145 && pelmHwChild->nameEquals("USBController"))
5146 {
5147 bool fEnabled = false;
5148
5149 pelmHwChild->getAttributeValue("enabled", fEnabled);
5150 if (fEnabled)
5151 {
5152 /* Create OHCI controller with default name. */
5153 USBController ctrl;
5154
5155 ctrl.strName = "OHCI";
5156 ctrl.enmType = USBControllerType_OHCI;
5157 hw.usbSettings.llUSBControllers.push_back(ctrl);
5158 }
5159
5160 pelmHwChild->getAttributeValue("enabledEhci", fEnabled);
5161 if (fEnabled)
5162 {
5163 /* Create OHCI controller with default name. */
5164 USBController ctrl;
5165
5166 ctrl.strName = "EHCI";
5167 ctrl.enmType = USBControllerType_EHCI;
5168 hw.usbSettings.llUSBControllers.push_back(ctrl);
5169 }
5170
5171 readUSBDeviceFilters(*pelmHwChild,
5172 hw.usbSettings.llDeviceFilters);
5173 }
5174 else if (pelmHwChild->nameEquals("USB"))
5175 {
5176 const xml::ElementNode *pelmUSBChild;
5177
5178 if ((pelmUSBChild = pelmHwChild->findChildElement("Controllers")))
5179 {
5180 xml::NodesLoop nl2(*pelmUSBChild, "Controller");
5181 const xml::ElementNode *pelmCtrl;
5182
5183 while ((pelmCtrl = nl2.forAllNodes()))
5184 {
5185 USBController ctrl;
5186 com::Utf8Str strCtrlType;
5187
5188 pelmCtrl->getAttributeValue("name", ctrl.strName);
5189
5190 if (pelmCtrl->getAttributeValue("type", strCtrlType))
5191 {
5192 if (strCtrlType == "OHCI")
5193 ctrl.enmType = USBControllerType_OHCI;
5194 else if (strCtrlType == "EHCI")
5195 ctrl.enmType = USBControllerType_EHCI;
5196 else if (strCtrlType == "XHCI")
5197 ctrl.enmType = USBControllerType_XHCI;
5198 else
5199 throw ConfigFileError(this, pelmCtrl, N_("Invalid value '%s' for Controller/@type attribute"), strCtrlType.c_str());
5200 }
5201
5202 hw.usbSettings.llUSBControllers.push_back(ctrl);
5203 }
5204 }
5205
5206 if ((pelmUSBChild = pelmHwChild->findChildElement("DeviceFilters")))
5207 readUSBDeviceFilters(*pelmUSBChild, hw.usbSettings.llDeviceFilters);
5208 }
5209 else if ( m->sv < SettingsVersion_v1_7
5210 && pelmHwChild->nameEquals("SATAController"))
5211 {
5212 bool f;
5213 if ( pelmHwChild->getAttributeValue("enabled", f)
5214 && f)
5215 {
5216 StorageController sctl;
5217 sctl.strName = "SATA Controller";
5218 sctl.storageBus = StorageBus_SATA;
5219 sctl.controllerType = StorageControllerType_IntelAhci;
5220
5221 readStorageControllerAttributes(*pelmHwChild, sctl);
5222
5223 hw.storage.llStorageControllers.push_back(sctl);
5224 }
5225 }
5226 else if (pelmHwChild->nameEquals("Network"))
5227 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
5228 else if (pelmHwChild->nameEquals("RTC"))
5229 {
5230 Utf8Str strLocalOrUTC;
5231 machineUserData.fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
5232 && strLocalOrUTC == "UTC";
5233 }
5234 else if ( pelmHwChild->nameEquals("UART")
5235 || pelmHwChild->nameEquals("Uart") // used before 1.3
5236 )
5237 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
5238 else if ( pelmHwChild->nameEquals("LPT")
5239 || pelmHwChild->nameEquals("Lpt") // used before 1.3
5240 )
5241 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
5242 else if (pelmHwChild->nameEquals("AudioAdapter"))
5243 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
5244 else if (pelmHwChild->nameEquals("SharedFolders"))
5245 {
5246 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
5247 const xml::ElementNode *pelmFolder;
5248 while ((pelmFolder = nl2.forAllNodes()))
5249 {
5250 SharedFolder sf;
5251 pelmFolder->getAttributeValue("name", sf.strName);
5252 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
5253 pelmFolder->getAttributeValue("writable", sf.fWritable);
5254 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
5255 pelmFolder->getAttributeValue("autoMountPoint", sf.strAutoMountPoint);
5256 hw.llSharedFolders.push_back(sf);
5257 }
5258 }
5259 else if (pelmHwChild->nameEquals("Clipboard"))
5260 {
5261 Utf8Str strTemp;
5262 if (pelmHwChild->getAttributeValue("mode", strTemp))
5263 {
5264 if (strTemp == "Disabled")
5265 hw.clipboardMode = ClipboardMode_Disabled;
5266 else if (strTemp == "HostToGuest")
5267 hw.clipboardMode = ClipboardMode_HostToGuest;
5268 else if (strTemp == "GuestToHost")
5269 hw.clipboardMode = ClipboardMode_GuestToHost;
5270 else if (strTemp == "Bidirectional")
5271 hw.clipboardMode = ClipboardMode_Bidirectional;
5272 else
5273 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
5274 }
5275
5276 pelmHwChild->getAttributeValue("fileTransfersEnabled", hw.fClipboardFileTransfersEnabled);
5277 }
5278 else if (pelmHwChild->nameEquals("DragAndDrop"))
5279 {
5280 Utf8Str strTemp;
5281 if (pelmHwChild->getAttributeValue("mode", strTemp))
5282 {
5283 if (strTemp == "Disabled")
5284 hw.dndMode = DnDMode_Disabled;
5285 else if (strTemp == "HostToGuest")
5286 hw.dndMode = DnDMode_HostToGuest;
5287 else if (strTemp == "GuestToHost")
5288 hw.dndMode = DnDMode_GuestToHost;
5289 else if (strTemp == "Bidirectional")
5290 hw.dndMode = DnDMode_Bidirectional;
5291 else
5292 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in DragAndDrop/@mode attribute"), strTemp.c_str());
5293 }
5294 }
5295 else if (pelmHwChild->nameEquals("Guest"))
5296 {
5297 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
5298 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
5299 }
5300 else if (pelmHwChild->nameEquals("GuestProperties"))
5301 readGuestProperties(*pelmHwChild, hw);
5302 else if (pelmHwChild->nameEquals("IO"))
5303 {
5304 const xml::ElementNode *pelmBwGroups;
5305 const xml::ElementNode *pelmIOChild;
5306
5307 if ((pelmIOChild = pelmHwChild->findChildElement("IoCache")))
5308 {
5309 pelmIOChild->getAttributeValue("enabled", hw.ioSettings.fIOCacheEnabled);
5310 pelmIOChild->getAttributeValue("size", hw.ioSettings.ulIOCacheSize);
5311 }
5312
5313 if ((pelmBwGroups = pelmHwChild->findChildElement("BandwidthGroups")))
5314 {
5315 xml::NodesLoop nl2(*pelmBwGroups, "BandwidthGroup");
5316 const xml::ElementNode *pelmBandwidthGroup;
5317 while ((pelmBandwidthGroup = nl2.forAllNodes()))
5318 {
5319 BandwidthGroup gr;
5320 Utf8Str strTemp;
5321
5322 pelmBandwidthGroup->getAttributeValue("name", gr.strName);
5323
5324 if (pelmBandwidthGroup->getAttributeValue("type", strTemp))
5325 {
5326 if (strTemp == "Disk")
5327 gr.enmType = BandwidthGroupType_Disk;
5328 else if (strTemp == "Network")
5329 gr.enmType = BandwidthGroupType_Network;
5330 else
5331 throw ConfigFileError(this, pelmBandwidthGroup, N_("Invalid value '%s' in BandwidthGroup/@type attribute"), strTemp.c_str());
5332 }
5333 else
5334 throw ConfigFileError(this, pelmBandwidthGroup, N_("Missing BandwidthGroup/@type attribute"));
5335
5336 if (!pelmBandwidthGroup->getAttributeValue("maxBytesPerSec", gr.cMaxBytesPerSec))
5337 {
5338 pelmBandwidthGroup->getAttributeValue("maxMbPerSec", gr.cMaxBytesPerSec);
5339 gr.cMaxBytesPerSec *= _1M;
5340 }
5341 hw.ioSettings.llBandwidthGroups.push_back(gr);
5342 }
5343 }
5344 }
5345 else if (pelmHwChild->nameEquals("HostPci"))
5346 {
5347 const xml::ElementNode *pelmDevices;
5348
5349 if ((pelmDevices = pelmHwChild->findChildElement("Devices")))
5350 {
5351 xml::NodesLoop nl2(*pelmDevices, "Device");
5352 const xml::ElementNode *pelmDevice;
5353 while ((pelmDevice = nl2.forAllNodes()))
5354 {
5355 HostPCIDeviceAttachment hpda;
5356
5357 if (!pelmDevice->getAttributeValue("host", hpda.uHostAddress))
5358 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@host attribute"));
5359
5360 if (!pelmDevice->getAttributeValue("guest", hpda.uGuestAddress))
5361 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@guest attribute"));
5362
5363 /* name is optional */
5364 pelmDevice->getAttributeValue("name", hpda.strDeviceName);
5365
5366 hw.pciAttachments.push_back(hpda);
5367 }
5368 }
5369 }
5370 else if (pelmHwChild->nameEquals("EmulatedUSB"))
5371 {
5372 const xml::ElementNode *pelmCardReader;
5373
5374 if ((pelmCardReader = pelmHwChild->findChildElement("CardReader")))
5375 {
5376 pelmCardReader->getAttributeValue("enabled", hw.fEmulatedUSBCardReader);
5377 }
5378 }
5379 else if (pelmHwChild->nameEquals("Frontend"))
5380 {
5381 const xml::ElementNode *pelmDefault;
5382
5383 if ((pelmDefault = pelmHwChild->findChildElement("Default")))
5384 {
5385 pelmDefault->getAttributeValue("type", hw.strDefaultFrontend);
5386 }
5387 }
5388 else if (pelmHwChild->nameEquals("StorageControllers"))
5389 readStorageControllers(*pelmHwChild, hw.storage);
5390 }
5391
5392 if (hw.ulMemorySizeMB == (uint32_t)-1)
5393 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
5394}
5395
5396/**
5397 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
5398 * files which have a \<HardDiskAttachments\> node and storage controller settings
5399 * hidden in the \<Hardware\> settings. We set the StorageControllers fields just the
5400 * same, just from different sources.
5401 * @param elmHardDiskAttachments \<HardDiskAttachments\> XML node.
5402 * @param strg
5403 */
5404void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
5405 Storage &strg)
5406{
5407 StorageController *pIDEController = NULL;
5408 StorageController *pSATAController = NULL;
5409
5410 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
5411 it != strg.llStorageControllers.end();
5412 ++it)
5413 {
5414 StorageController &s = *it;
5415 if (s.storageBus == StorageBus_IDE)
5416 pIDEController = &s;
5417 else if (s.storageBus == StorageBus_SATA)
5418 pSATAController = &s;
5419 }
5420
5421 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
5422 const xml::ElementNode *pelmAttachment;
5423 while ((pelmAttachment = nl1.forAllNodes()))
5424 {
5425 AttachedDevice att;
5426 Utf8Str strUUID, strBus;
5427
5428 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
5429 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
5430 parseUUID(att.uuid, strUUID, pelmAttachment);
5431
5432 if (!pelmAttachment->getAttributeValue("bus", strBus))
5433 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
5434 // pre-1.7 'channel' is now port
5435 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
5436 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
5437 // pre-1.7 'device' is still device
5438 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
5439 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
5440
5441 att.deviceType = DeviceType_HardDisk;
5442
5443 if (strBus == "IDE")
5444 {
5445 if (!pIDEController)
5446 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
5447 pIDEController->llAttachedDevices.push_back(att);
5448 }
5449 else if (strBus == "SATA")
5450 {
5451 if (!pSATAController)
5452 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
5453 pSATAController->llAttachedDevices.push_back(att);
5454 }
5455 else
5456 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
5457 }
5458}
5459
5460/**
5461 * Reads in a \<StorageControllers\> block and stores it in the given Storage structure.
5462 * Used both directly from readMachine and from readSnapshot, since snapshots
5463 * have their own storage controllers sections.
5464 *
5465 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
5466 * for earlier versions.
5467 *
5468 * @param elmStorageControllers
5469 * @param strg
5470 */
5471void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
5472 Storage &strg)
5473{
5474 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
5475 const xml::ElementNode *pelmController;
5476 while ((pelmController = nlStorageControllers.forAllNodes()))
5477 {
5478 StorageController sctl;
5479
5480 if (!pelmController->getAttributeValue("name", sctl.strName))
5481 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
5482 // canonicalize storage controller names for configs in the switchover
5483 // period.
5484 if (m->sv < SettingsVersion_v1_9)
5485 {
5486 if (sctl.strName == "IDE")
5487 sctl.strName = "IDE Controller";
5488 else if (sctl.strName == "SATA")
5489 sctl.strName = "SATA Controller";
5490 else if (sctl.strName == "SCSI")
5491 sctl.strName = "SCSI Controller";
5492 }
5493
5494 pelmController->getAttributeValue("Instance", sctl.ulInstance);
5495 // default from constructor is 0
5496
5497 pelmController->getAttributeValue("Bootable", sctl.fBootable);
5498 // default from constructor is true which is true
5499 // for settings below version 1.11 because they allowed only
5500 // one controller per type.
5501
5502 Utf8Str strType;
5503 if (!pelmController->getAttributeValue("type", strType))
5504 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
5505
5506 if (strType == "AHCI")
5507 {
5508 sctl.storageBus = StorageBus_SATA;
5509 sctl.controllerType = StorageControllerType_IntelAhci;
5510 }
5511 else if (strType == "LsiLogic")
5512 {
5513 sctl.storageBus = StorageBus_SCSI;
5514 sctl.controllerType = StorageControllerType_LsiLogic;
5515 }
5516 else if (strType == "BusLogic")
5517 {
5518 sctl.storageBus = StorageBus_SCSI;
5519 sctl.controllerType = StorageControllerType_BusLogic;
5520 }
5521 else if (strType == "PIIX3")
5522 {
5523 sctl.storageBus = StorageBus_IDE;
5524 sctl.controllerType = StorageControllerType_PIIX3;
5525 }
5526 else if (strType == "PIIX4")
5527 {
5528 sctl.storageBus = StorageBus_IDE;
5529 sctl.controllerType = StorageControllerType_PIIX4;
5530 }
5531 else if (strType == "ICH6")
5532 {
5533 sctl.storageBus = StorageBus_IDE;
5534 sctl.controllerType = StorageControllerType_ICH6;
5535 }
5536 else if ( (m->sv >= SettingsVersion_v1_9)
5537 && (strType == "I82078")
5538 )
5539 {
5540 sctl.storageBus = StorageBus_Floppy;
5541 sctl.controllerType = StorageControllerType_I82078;
5542 }
5543 else if (strType == "LsiLogicSas")
5544 {
5545 sctl.storageBus = StorageBus_SAS;
5546 sctl.controllerType = StorageControllerType_LsiLogicSas;
5547 }
5548 else if (strType == "USB")
5549 {
5550 sctl.storageBus = StorageBus_USB;
5551 sctl.controllerType = StorageControllerType_USB;
5552 }
5553 else if (strType == "NVMe")
5554 {
5555 sctl.storageBus = StorageBus_PCIe;
5556 sctl.controllerType = StorageControllerType_NVMe;
5557 }
5558 else if (strType == "VirtioSCSI")
5559 {
5560 sctl.storageBus = StorageBus_VirtioSCSI;
5561 sctl.controllerType = StorageControllerType_VirtioSCSI;
5562 }
5563 else
5564 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
5565
5566 readStorageControllerAttributes(*pelmController, sctl);
5567
5568 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
5569 const xml::ElementNode *pelmAttached;
5570 while ((pelmAttached = nlAttached.forAllNodes()))
5571 {
5572 AttachedDevice att;
5573 Utf8Str strTemp;
5574 pelmAttached->getAttributeValue("type", strTemp);
5575
5576 att.fDiscard = false;
5577 att.fNonRotational = false;
5578 att.fHotPluggable = false;
5579 att.fPassThrough = false;
5580
5581 if (strTemp == "HardDisk")
5582 {
5583 att.deviceType = DeviceType_HardDisk;
5584 pelmAttached->getAttributeValue("nonrotational", att.fNonRotational);
5585 pelmAttached->getAttributeValue("discard", att.fDiscard);
5586 }
5587 else if (m->sv >= SettingsVersion_v1_9)
5588 {
5589 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
5590 if (strTemp == "DVD")
5591 {
5592 att.deviceType = DeviceType_DVD;
5593 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
5594 pelmAttached->getAttributeValue("tempeject", att.fTempEject);
5595 }
5596 else if (strTemp == "Floppy")
5597 att.deviceType = DeviceType_Floppy;
5598 }
5599
5600 if (att.deviceType != DeviceType_Null)
5601 {
5602 const xml::ElementNode *pelmImage;
5603 // all types can have images attached, but for HardDisk it's required
5604 if (!(pelmImage = pelmAttached->findChildElement("Image")))
5605 {
5606 if (att.deviceType == DeviceType_HardDisk)
5607 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
5608 else
5609 {
5610 // DVDs and floppies can also have <HostDrive> instead of <Image>
5611 const xml::ElementNode *pelmHostDrive;
5612 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
5613 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
5614 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
5615 }
5616 }
5617 else
5618 {
5619 if (!pelmImage->getAttributeValue("uuid", strTemp))
5620 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
5621 parseUUID(att.uuid, strTemp, pelmImage);
5622 }
5623
5624 if (!pelmAttached->getAttributeValue("port", att.lPort))
5625 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
5626 if (!pelmAttached->getAttributeValue("device", att.lDevice))
5627 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
5628
5629 /* AHCI controller ports are hotpluggable by default, keep compatibility with existing settings. */
5630 if (m->sv >= SettingsVersion_v1_15)
5631 pelmAttached->getAttributeValue("hotpluggable", att.fHotPluggable);
5632 else if (sctl.controllerType == StorageControllerType_IntelAhci)
5633 att.fHotPluggable = true;
5634
5635 pelmAttached->getAttributeValue("bandwidthGroup", att.strBwGroup);
5636 sctl.llAttachedDevices.push_back(att);
5637 }
5638 }
5639
5640 strg.llStorageControllers.push_back(sctl);
5641 }
5642}
5643
5644/**
5645 * This gets called for legacy pre-1.9 settings files after having parsed the
5646 * \<Hardware\> and \<StorageControllers\> sections to parse \<Hardware\> once more
5647 * for the \<DVDDrive\> and \<FloppyDrive\> sections.
5648 *
5649 * Before settings version 1.9, DVD and floppy drives were specified separately
5650 * under \<Hardware\>; we then need this extra loop to make sure the storage
5651 * controller structs are already set up so we can add stuff to them.
5652 *
5653 * @param elmHardware
5654 * @param strg
5655 */
5656void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
5657 Storage &strg)
5658{
5659 xml::NodesLoop nl1(elmHardware);
5660 const xml::ElementNode *pelmHwChild;
5661 while ((pelmHwChild = nl1.forAllNodes()))
5662 {
5663 if (pelmHwChild->nameEquals("DVDDrive"))
5664 {
5665 // create a DVD "attached device" and attach it to the existing IDE controller
5666 AttachedDevice att;
5667 att.deviceType = DeviceType_DVD;
5668 // legacy DVD drive is always secondary master (port 1, device 0)
5669 att.lPort = 1;
5670 att.lDevice = 0;
5671 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
5672 pelmHwChild->getAttributeValue("tempeject", att.fTempEject);
5673
5674 const xml::ElementNode *pDriveChild;
5675 Utf8Str strTmp;
5676 if ( (pDriveChild = pelmHwChild->findChildElement("Image")) != NULL
5677 && pDriveChild->getAttributeValue("uuid", strTmp))
5678 parseUUID(att.uuid, strTmp, pDriveChild);
5679 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
5680 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
5681
5682 // find the IDE controller and attach the DVD drive
5683 bool fFound = false;
5684 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
5685 it != strg.llStorageControllers.end();
5686 ++it)
5687 {
5688 StorageController &sctl = *it;
5689 if (sctl.storageBus == StorageBus_IDE)
5690 {
5691 sctl.llAttachedDevices.push_back(att);
5692 fFound = true;
5693 break;
5694 }
5695 }
5696
5697 if (!fFound)
5698 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
5699 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
5700 // which should have gotten parsed in <StorageControllers> before this got called
5701 }
5702 else if (pelmHwChild->nameEquals("FloppyDrive"))
5703 {
5704 bool fEnabled;
5705 if ( pelmHwChild->getAttributeValue("enabled", fEnabled)
5706 && fEnabled)
5707 {
5708 // create a new floppy controller and attach a floppy "attached device"
5709 StorageController sctl;
5710 sctl.strName = "Floppy Controller";
5711 sctl.storageBus = StorageBus_Floppy;
5712 sctl.controllerType = StorageControllerType_I82078;
5713 sctl.ulPortCount = 1;
5714
5715 AttachedDevice att;
5716 att.deviceType = DeviceType_Floppy;
5717 att.lPort = 0;
5718 att.lDevice = 0;
5719
5720 const xml::ElementNode *pDriveChild;
5721 Utf8Str strTmp;
5722 if ( (pDriveChild = pelmHwChild->findChildElement("Image"))
5723 && pDriveChild->getAttributeValue("uuid", strTmp) )
5724 parseUUID(att.uuid, strTmp, pDriveChild);
5725 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
5726 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
5727
5728 // store attachment with controller
5729 sctl.llAttachedDevices.push_back(att);
5730 // store controller with storage
5731 strg.llStorageControllers.push_back(sctl);
5732 }
5733 }
5734 }
5735}
5736
5737/**
5738 * Called for reading the \<Teleporter\> element under \<Machine\>.
5739 */
5740void MachineConfigFile::readTeleporter(const xml::ElementNode *pElmTeleporter,
5741 MachineUserData *pUserData)
5742{
5743 pElmTeleporter->getAttributeValue("enabled", pUserData->fTeleporterEnabled);
5744 pElmTeleporter->getAttributeValue("port", pUserData->uTeleporterPort);
5745 pElmTeleporter->getAttributeValue("address", pUserData->strTeleporterAddress);
5746 pElmTeleporter->getAttributeValue("password", pUserData->strTeleporterPassword);
5747
5748 if ( pUserData->strTeleporterPassword.isNotEmpty()
5749 && !VBoxIsPasswordHashed(&pUserData->strTeleporterPassword))
5750 VBoxHashPassword(&pUserData->strTeleporterPassword);
5751}
5752
5753/**
5754 * Called for reading the \<Debugging\> element under \<Machine\> or \<Snapshot\>.
5755 */
5756void MachineConfigFile::readDebugging(const xml::ElementNode *pElmDebugging, Debugging *pDbg)
5757{
5758 if (!pElmDebugging || m->sv < SettingsVersion_v1_13)
5759 return;
5760
5761 const xml::ElementNode * const pelmTracing = pElmDebugging->findChildElement("Tracing");
5762 if (pelmTracing)
5763 {
5764 pelmTracing->getAttributeValue("enabled", pDbg->fTracingEnabled);
5765 pelmTracing->getAttributeValue("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
5766 pelmTracing->getAttributeValue("config", pDbg->strTracingConfig);
5767 }
5768}
5769
5770/**
5771 * Called for reading the \<Autostart\> element under \<Machine\> or \<Snapshot\>.
5772 */
5773void MachineConfigFile::readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart)
5774{
5775 Utf8Str strAutostop;
5776
5777 if (!pElmAutostart || m->sv < SettingsVersion_v1_13)
5778 return;
5779
5780 pElmAutostart->getAttributeValue("enabled", pAutostart->fAutostartEnabled);
5781 pElmAutostart->getAttributeValue("delay", pAutostart->uAutostartDelay);
5782 pElmAutostart->getAttributeValue("autostop", strAutostop);
5783 if (strAutostop == "Disabled")
5784 pAutostart->enmAutostopType = AutostopType_Disabled;
5785 else if (strAutostop == "SaveState")
5786 pAutostart->enmAutostopType = AutostopType_SaveState;
5787 else if (strAutostop == "PowerOff")
5788 pAutostart->enmAutostopType = AutostopType_PowerOff;
5789 else if (strAutostop == "AcpiShutdown")
5790 pAutostart->enmAutostopType = AutostopType_AcpiShutdown;
5791 else
5792 throw ConfigFileError(this, pElmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str());
5793}
5794
5795/**
5796 * Called for reading the \<Groups\> element under \<Machine\>.
5797 */
5798void MachineConfigFile::readGroups(const xml::ElementNode *pElmGroups, StringsList *pllGroups)
5799{
5800 pllGroups->clear();
5801 if (!pElmGroups || m->sv < SettingsVersion_v1_13)
5802 {
5803 pllGroups->push_back("/");
5804 return;
5805 }
5806
5807 xml::NodesLoop nlGroups(*pElmGroups);
5808 const xml::ElementNode *pelmGroup;
5809 while ((pelmGroup = nlGroups.forAllNodes()))
5810 {
5811 if (pelmGroup->nameEquals("Group"))
5812 {
5813 Utf8Str strGroup;
5814 if (!pelmGroup->getAttributeValue("name", strGroup))
5815 throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing"));
5816 pllGroups->push_back(strGroup);
5817 }
5818 }
5819}
5820
5821/**
5822 * Called initially for the \<Snapshot\> element under \<Machine\>, if present,
5823 * to store the snapshot's data into the given Snapshot structure (which is
5824 * then the one in the Machine struct). This might then recurse if
5825 * a \<Snapshots\> (plural) element is found in the snapshot, which should
5826 * contain a list of child snapshots; such lists are maintained in the
5827 * Snapshot structure.
5828 *
5829 * @param curSnapshotUuid
5830 * @param depth
5831 * @param elmSnapshot
5832 * @param snap
5833 * @returns true if curSnapshotUuid is in this snapshot subtree, otherwise false
5834 */
5835bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid,
5836 uint32_t depth,
5837 const xml::ElementNode &elmSnapshot,
5838 Snapshot &snap)
5839{
5840 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
5841 throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
5842
5843 Utf8Str strTemp;
5844
5845 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
5846 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
5847 parseUUID(snap.uuid, strTemp, &elmSnapshot);
5848 bool foundCurrentSnapshot = (snap.uuid == curSnapshotUuid);
5849
5850 if (!elmSnapshot.getAttributeValue("name", snap.strName))
5851 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
5852
5853 // 3.1 dev builds added Description as an attribute, read it silently
5854 // and write it back as an element
5855 elmSnapshot.getAttributeValue("Description", snap.strDescription);
5856
5857 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
5858 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
5859 parseTimestamp(snap.timestamp, strTemp, &elmSnapshot);
5860
5861 elmSnapshot.getAttributeValuePath("stateFile", snap.strStateFile); // online snapshots only
5862
5863 // parse Hardware before the other elements because other things depend on it
5864 const xml::ElementNode *pelmHardware;
5865 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
5866 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
5867 readHardware(*pelmHardware, snap.hardware);
5868
5869 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
5870 const xml::ElementNode *pelmSnapshotChild;
5871 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
5872 {
5873 if (pelmSnapshotChild->nameEquals("Description"))
5874 snap.strDescription = pelmSnapshotChild->getValue();
5875 else if ( m->sv < SettingsVersion_v1_7
5876 && pelmSnapshotChild->nameEquals("HardDiskAttachments"))
5877 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.hardware.storage);
5878 else if ( m->sv >= SettingsVersion_v1_7
5879 && pelmSnapshotChild->nameEquals("StorageControllers"))
5880 readStorageControllers(*pelmSnapshotChild, snap.hardware.storage);
5881 else if (pelmSnapshotChild->nameEquals("Snapshots"))
5882 {
5883 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
5884 const xml::ElementNode *pelmChildSnapshot;
5885 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
5886 {
5887 if (pelmChildSnapshot->nameEquals("Snapshot"))
5888 {
5889 // recurse with this element and put the child at the
5890 // end of the list. XPCOM has very small stack, avoid
5891 // big local variables and use the list element.
5892 snap.llChildSnapshots.push_back(Snapshot::Empty);
5893 bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, snap.llChildSnapshots.back());
5894 foundCurrentSnapshot = foundCurrentSnapshot || found;
5895 }
5896 }
5897 }
5898 }
5899
5900 if (m->sv < SettingsVersion_v1_9)
5901 // go through Hardware once more to repair the settings controller structures
5902 // with data from old DVDDrive and FloppyDrive elements
5903 readDVDAndFloppies_pre1_9(*pelmHardware, snap.hardware.storage);
5904
5905 readDebugging(elmSnapshot.findChildElement("Debugging"), &snap.debugging);
5906 readAutostart(elmSnapshot.findChildElement("Autostart"), &snap.autostart);
5907 // note: Groups exist only for Machine, not for Snapshot
5908
5909 return foundCurrentSnapshot;
5910}
5911
5912const struct {
5913 const char *pcszOld;
5914 const char *pcszNew;
5915} aConvertOSTypes[] =
5916{
5917 { "unknown", "Other" },
5918 { "dos", "DOS" },
5919 { "win31", "Windows31" },
5920 { "win95", "Windows95" },
5921 { "win98", "Windows98" },
5922 { "winme", "WindowsMe" },
5923 { "winnt4", "WindowsNT4" },
5924 { "win2k", "Windows2000" },
5925 { "winxp", "WindowsXP" },
5926 { "win2k3", "Windows2003" },
5927 { "winvista", "WindowsVista" },
5928 { "win2k8", "Windows2008" },
5929 { "os2warp3", "OS2Warp3" },
5930 { "os2warp4", "OS2Warp4" },
5931 { "os2warp45", "OS2Warp45" },
5932 { "ecs", "OS2eCS" },
5933 { "linux22", "Linux22" },
5934 { "linux24", "Linux24" },
5935 { "linux26", "Linux26" },
5936 { "archlinux", "ArchLinux" },
5937 { "debian", "Debian" },
5938 { "opensuse", "OpenSUSE" },
5939 { "fedoracore", "Fedora" },
5940 { "gentoo", "Gentoo" },
5941 { "mandriva", "Mandriva" },
5942 { "redhat", "RedHat" },
5943 { "ubuntu", "Ubuntu" },
5944 { "xandros", "Xandros" },
5945 { "freebsd", "FreeBSD" },
5946 { "openbsd", "OpenBSD" },
5947 { "netbsd", "NetBSD" },
5948 { "netware", "Netware" },
5949 { "solaris", "Solaris" },
5950 { "opensolaris", "OpenSolaris" },
5951 { "l4", "L4" }
5952};
5953
5954void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
5955{
5956 for (unsigned u = 0;
5957 u < RT_ELEMENTS(aConvertOSTypes);
5958 ++u)
5959 {
5960 if (str == aConvertOSTypes[u].pcszOld)
5961 {
5962 str = aConvertOSTypes[u].pcszNew;
5963 break;
5964 }
5965 }
5966}
5967
5968/**
5969 * Called from the constructor to actually read in the \<Machine\> element
5970 * of a machine config file.
5971 * @param elmMachine
5972 */
5973void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
5974{
5975 Utf8Str strUUID;
5976 if ( elmMachine.getAttributeValue("uuid", strUUID)
5977 && elmMachine.getAttributeValue("name", machineUserData.strName))
5978 {
5979 parseUUID(uuid, strUUID, &elmMachine);
5980
5981 elmMachine.getAttributeValue("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
5982 elmMachine.getAttributeValue("nameSync", machineUserData.fNameSync);
5983
5984 Utf8Str str;
5985 elmMachine.getAttributeValue("Description", machineUserData.strDescription);
5986 elmMachine.getAttributeValue("OSType", machineUserData.strOsType);
5987 if (m->sv < SettingsVersion_v1_5)
5988 convertOldOSType_pre1_5(machineUserData.strOsType);
5989
5990 elmMachine.getAttributeValuePath("stateFile", strStateFile);
5991
5992 if (elmMachine.getAttributeValue("currentSnapshot", str))
5993 parseUUID(uuidCurrentSnapshot, str, &elmMachine);
5994
5995 elmMachine.getAttributeValuePath("snapshotFolder", machineUserData.strSnapshotFolder);
5996
5997 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
5998 fCurrentStateModified = true;
5999 if (elmMachine.getAttributeValue("lastStateChange", str))
6000 parseTimestamp(timeLastStateChange, str, &elmMachine);
6001 // constructor has called RTTimeNow(&timeLastStateChange) before
6002 if (elmMachine.getAttributeValue("aborted", fAborted))
6003 fAborted = true;
6004
6005 {
6006 Utf8Str strVMPriority;
6007 if (elmMachine.getAttributeValue("processPriority", strVMPriority))
6008 {
6009 if (strVMPriority == "Flat")
6010 machineUserData.enmVMPriority = VMProcPriority_Flat;
6011 else if (strVMPriority == "Low")
6012 machineUserData.enmVMPriority = VMProcPriority_Low;
6013 else if (strVMPriority == "Normal")
6014 machineUserData.enmVMPriority = VMProcPriority_Normal;
6015 else if (strVMPriority == "High")
6016 machineUserData.enmVMPriority = VMProcPriority_High;
6017 else
6018 machineUserData.enmVMPriority = VMProcPriority_Default;
6019 }
6020 }
6021
6022 str.setNull();
6023 elmMachine.getAttributeValue("icon", str);
6024 parseBase64(machineUserData.ovIcon, str, &elmMachine);
6025
6026 // parse Hardware before the other elements because other things depend on it
6027 const xml::ElementNode *pelmHardware;
6028 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
6029 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
6030 readHardware(*pelmHardware, hardwareMachine);
6031
6032 xml::NodesLoop nlRootChildren(elmMachine);
6033 const xml::ElementNode *pelmMachineChild;
6034 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
6035 {
6036 if (pelmMachineChild->nameEquals("ExtraData"))
6037 readExtraData(*pelmMachineChild,
6038 mapExtraDataItems);
6039 else if ( (m->sv < SettingsVersion_v1_7)
6040 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
6041 )
6042 readHardDiskAttachments_pre1_7(*pelmMachineChild, hardwareMachine.storage);
6043 else if ( (m->sv >= SettingsVersion_v1_7)
6044 && (pelmMachineChild->nameEquals("StorageControllers"))
6045 )
6046 readStorageControllers(*pelmMachineChild, hardwareMachine.storage);
6047 else if (pelmMachineChild->nameEquals("Snapshot"))
6048 {
6049 if (uuidCurrentSnapshot.isZero())
6050 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing"));
6051 bool foundCurrentSnapshot = false;
6052 Snapshot snap;
6053 // this will recurse into child snapshots, if necessary
6054 foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, 1, *pelmMachineChild, snap);
6055 if (!foundCurrentSnapshot)
6056 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute"));
6057 llFirstSnapshot.push_back(snap);
6058 }
6059 else if (pelmMachineChild->nameEquals("Description"))
6060 machineUserData.strDescription = pelmMachineChild->getValue();
6061 else if (pelmMachineChild->nameEquals("Teleporter"))
6062 readTeleporter(pelmMachineChild, &machineUserData);
6063 else if (pelmMachineChild->nameEquals("MediaRegistry"))
6064 readMediaRegistry(*pelmMachineChild, mediaRegistry);
6065 else if (pelmMachineChild->nameEquals("Debugging"))
6066 readDebugging(pelmMachineChild, &debugging);
6067 else if (pelmMachineChild->nameEquals("Autostart"))
6068 readAutostart(pelmMachineChild, &autostart);
6069 else if (pelmMachineChild->nameEquals("Groups"))
6070 readGroups(pelmMachineChild, &machineUserData.llGroups);
6071 }
6072
6073 if (m->sv < SettingsVersion_v1_9)
6074 // go through Hardware once more to repair the settings controller structures
6075 // with data from old DVDDrive and FloppyDrive elements
6076 readDVDAndFloppies_pre1_9(*pelmHardware, hardwareMachine.storage);
6077 }
6078 else
6079 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
6080}
6081
6082/**
6083 * Creates a \<Hardware\> node under elmParent and then writes out the XML
6084 * keys under that. Called for both the \<Machine\> node and for snapshots.
6085 * @param elmParent
6086 * @param hw
6087 * @param fl
6088 * @param pllElementsWithUuidAttributes
6089 */
6090void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
6091 const Hardware &hw,
6092 uint32_t fl,
6093 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
6094{
6095 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
6096
6097 if ( m->sv >= SettingsVersion_v1_4
6098 && (m->sv < SettingsVersion_v1_7 ? hw.strVersion != "1" : hw.strVersion != "2"))
6099 pelmHardware->setAttribute("version", hw.strVersion);
6100
6101 if ((m->sv >= SettingsVersion_v1_9)
6102 && !hw.uuid.isZero()
6103 && hw.uuid.isValid()
6104 )
6105 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
6106
6107 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
6108
6109 if (!hw.fHardwareVirt)
6110 pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
6111 if (!hw.fNestedPaging)
6112 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
6113 if (!hw.fVPID)
6114 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
6115 if (!hw.fUnrestrictedExecution)
6116 pelmCPU->createChild("HardwareVirtExUX")->setAttribute("enabled", hw.fUnrestrictedExecution);
6117 // PAE has too crazy default handling, must always save this setting.
6118 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
6119 if (m->sv >= SettingsVersion_v1_16)
6120 {
6121 if (hw.fIBPBOnVMEntry || hw.fIBPBOnVMExit)
6122 {
6123 xml::ElementNode *pelmChild = pelmCPU->createChild("IBPBOn");
6124 if (hw.fIBPBOnVMExit)
6125 pelmChild->setAttribute("vmexit", hw.fIBPBOnVMExit);
6126 if (hw.fIBPBOnVMEntry)
6127 pelmChild->setAttribute("vmentry", hw.fIBPBOnVMEntry);
6128 }
6129 if (hw.fSpecCtrl)
6130 pelmCPU->createChild("SpecCtrl")->setAttribute("enabled", hw.fSpecCtrl);
6131 if (hw.fSpecCtrlByHost)
6132 pelmCPU->createChild("SpecCtrlByHost")->setAttribute("enabled", hw.fSpecCtrlByHost);
6133 if (!hw.fL1DFlushOnSched || hw.fL1DFlushOnVMEntry)
6134 {
6135 xml::ElementNode *pelmChild = pelmCPU->createChild("L1DFlushOn");
6136 if (!hw.fL1DFlushOnSched)
6137 pelmChild->setAttribute("scheduling", hw.fL1DFlushOnSched);
6138 if (hw.fL1DFlushOnVMEntry)
6139 pelmChild->setAttribute("vmentry", hw.fL1DFlushOnVMEntry);
6140 }
6141 if (!hw.fMDSClearOnSched || hw.fMDSClearOnVMEntry)
6142 {
6143 xml::ElementNode *pelmChild = pelmCPU->createChild("MDSClearOn");
6144 if (!hw.fMDSClearOnSched)
6145 pelmChild->setAttribute("scheduling", hw.fMDSClearOnSched);
6146 if (hw.fMDSClearOnVMEntry)
6147 pelmChild->setAttribute("vmentry", hw.fMDSClearOnVMEntry);
6148 }
6149 }
6150 if (m->sv >= SettingsVersion_v1_17 && hw.fNestedHWVirt)
6151 pelmCPU->createChild("NestedHWVirt")->setAttribute("enabled", hw.fNestedHWVirt);
6152
6153 if (m->sv >= SettingsVersion_v1_18 && !hw.fVirtVmsaveVmload)
6154 pelmCPU->createChild("HardwareVirtExVirtVmsaveVmload")->setAttribute("enabled", hw.fVirtVmsaveVmload);
6155
6156 if (m->sv >= SettingsVersion_v1_14 && hw.enmLongMode != Hardware::LongMode_Legacy)
6157 {
6158 // LongMode has too crazy default handling, must always save this setting.
6159 pelmCPU->createChild("LongMode")->setAttribute("enabled", hw.enmLongMode == Hardware::LongMode_Enabled);
6160 }
6161
6162 if (hw.fTripleFaultReset)
6163 pelmCPU->createChild("TripleFaultReset")->setAttribute("enabled", hw.fTripleFaultReset);
6164 if (m->sv >= SettingsVersion_v1_14)
6165 {
6166 if (hw.fX2APIC)
6167 pelmCPU->createChild("X2APIC")->setAttribute("enabled", hw.fX2APIC);
6168 else if (!hw.fAPIC)
6169 pelmCPU->createChild("APIC")->setAttribute("enabled", hw.fAPIC);
6170 }
6171 if (hw.cCPUs > 1)
6172 pelmCPU->setAttribute("count", hw.cCPUs);
6173 if (hw.ulCpuExecutionCap != 100)
6174 pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap);
6175 if (hw.uCpuIdPortabilityLevel != 0)
6176 pelmCPU->setAttribute("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
6177 if (!hw.strCpuProfile.equals("host") && hw.strCpuProfile.isNotEmpty())
6178 pelmCPU->setAttribute("CpuProfile", hw.strCpuProfile);
6179
6180 // HardwareVirtExLargePages has too crazy default handling, must always save this setting.
6181 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
6182
6183 if (m->sv >= SettingsVersion_v1_9)
6184 {
6185 if (hw.fHardwareVirtForce)
6186 pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce);
6187 }
6188
6189 if (m->sv >= SettingsVersion_v1_9 && hw.fUseNativeApi)
6190 pelmCPU->createChild("HardwareVirtExUseNativeApi")->setAttribute("enabled", hw.fUseNativeApi);
6191
6192 if (m->sv >= SettingsVersion_v1_10)
6193 {
6194 if (hw.fCpuHotPlug)
6195 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
6196
6197 xml::ElementNode *pelmCpuTree = NULL;
6198 for (CpuList::const_iterator it = hw.llCpus.begin();
6199 it != hw.llCpus.end();
6200 ++it)
6201 {
6202 const Cpu &cpu = *it;
6203
6204 if (pelmCpuTree == NULL)
6205 pelmCpuTree = pelmCPU->createChild("CpuTree");
6206
6207 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
6208 pelmCpu->setAttribute("id", cpu.ulId);
6209 }
6210 }
6211
6212 xml::ElementNode *pelmCpuIdTree = NULL;
6213 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
6214 it != hw.llCpuIdLeafs.end();
6215 ++it)
6216 {
6217 const CpuIdLeaf &leaf = *it;
6218
6219 if (pelmCpuIdTree == NULL)
6220 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
6221
6222 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
6223 pelmCpuIdLeaf->setAttribute("id", leaf.idx);
6224 if (leaf.idxSub != 0)
6225 pelmCpuIdLeaf->setAttribute("subleaf", leaf.idxSub);
6226 pelmCpuIdLeaf->setAttribute("eax", leaf.uEax);
6227 pelmCpuIdLeaf->setAttribute("ebx", leaf.uEbx);
6228 pelmCpuIdLeaf->setAttribute("ecx", leaf.uEcx);
6229 pelmCpuIdLeaf->setAttribute("edx", leaf.uEdx);
6230 }
6231
6232 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
6233 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
6234 if (m->sv >= SettingsVersion_v1_10)
6235 {
6236 if (hw.fPageFusionEnabled)
6237 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
6238 }
6239
6240 if ( (m->sv >= SettingsVersion_v1_9)
6241 && (hw.firmwareType >= FirmwareType_EFI)
6242 )
6243 {
6244 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
6245 const char *pcszFirmware;
6246
6247 switch (hw.firmwareType)
6248 {
6249 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
6250 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
6251 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
6252 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
6253 default: pcszFirmware = "None"; break;
6254 }
6255 pelmFirmware->setAttribute("type", pcszFirmware);
6256 }
6257
6258 if ( m->sv >= SettingsVersion_v1_10
6259 && ( hw.pointingHIDType != PointingHIDType_PS2Mouse
6260 || hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard))
6261 {
6262 xml::ElementNode *pelmHID = pelmHardware->createChild("HID");
6263 const char *pcszHID;
6264
6265 if (hw.pointingHIDType != PointingHIDType_PS2Mouse)
6266 {
6267 switch (hw.pointingHIDType)
6268 {
6269 case PointingHIDType_USBMouse: pcszHID = "USBMouse"; break;
6270 case PointingHIDType_USBTablet: pcszHID = "USBTablet"; break;
6271 case PointingHIDType_PS2Mouse: pcszHID = "PS2Mouse"; break;
6272 case PointingHIDType_ComboMouse: pcszHID = "ComboMouse"; break;
6273 case PointingHIDType_USBMultiTouch: pcszHID = "USBMultiTouch";break;
6274 case PointingHIDType_None: pcszHID = "None"; break;
6275 default: Assert(false); pcszHID = "PS2Mouse"; break;
6276 }
6277 pelmHID->setAttribute("Pointing", pcszHID);
6278 }
6279
6280 if (hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard)
6281 {
6282 switch (hw.keyboardHIDType)
6283 {
6284 case KeyboardHIDType_USBKeyboard: pcszHID = "USBKeyboard"; break;
6285 case KeyboardHIDType_PS2Keyboard: pcszHID = "PS2Keyboard"; break;
6286 case KeyboardHIDType_ComboKeyboard: pcszHID = "ComboKeyboard"; break;
6287 case KeyboardHIDType_None: pcszHID = "None"; break;
6288 default: Assert(false); pcszHID = "PS2Keyboard"; break;
6289 }
6290 pelmHID->setAttribute("Keyboard", pcszHID);
6291 }
6292 }
6293
6294 if ( (m->sv >= SettingsVersion_v1_10)
6295 && hw.fHPETEnabled
6296 )
6297 {
6298 xml::ElementNode *pelmHPET = pelmHardware->createChild("HPET");
6299 pelmHPET->setAttribute("enabled", hw.fHPETEnabled);
6300 }
6301
6302 if ( (m->sv >= SettingsVersion_v1_11)
6303 )
6304 {
6305 if (hw.chipsetType != ChipsetType_PIIX3)
6306 {
6307 xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset");
6308 const char *pcszChipset;
6309
6310 switch (hw.chipsetType)
6311 {
6312 case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break;
6313 case ChipsetType_ICH9: pcszChipset = "ICH9"; break;
6314 default: Assert(false); pcszChipset = "PIIX3"; break;
6315 }
6316 pelmChipset->setAttribute("type", pcszChipset);
6317 }
6318 }
6319
6320 if ( (m->sv >= SettingsVersion_v1_15)
6321 && !hw.areParavirtDefaultSettings(m->sv)
6322 )
6323 {
6324 const char *pcszParavirtProvider;
6325 switch (hw.paravirtProvider)
6326 {
6327 case ParavirtProvider_None: pcszParavirtProvider = "None"; break;
6328 case ParavirtProvider_Default: pcszParavirtProvider = "Default"; break;
6329 case ParavirtProvider_Legacy: pcszParavirtProvider = "Legacy"; break;
6330 case ParavirtProvider_Minimal: pcszParavirtProvider = "Minimal"; break;
6331 case ParavirtProvider_HyperV: pcszParavirtProvider = "HyperV"; break;
6332 case ParavirtProvider_KVM: pcszParavirtProvider = "KVM"; break;
6333 default: Assert(false); pcszParavirtProvider = "None"; break;
6334 }
6335
6336 xml::ElementNode *pelmParavirt = pelmHardware->createChild("Paravirt");
6337 pelmParavirt->setAttribute("provider", pcszParavirtProvider);
6338
6339 if ( m->sv >= SettingsVersion_v1_16
6340 && hw.strParavirtDebug.isNotEmpty())
6341 pelmParavirt->setAttribute("debug", hw.strParavirtDebug);
6342 }
6343
6344 if ( m->sv >= SettingsVersion_v1_19
6345 && hw.iommuType != IommuType_None)
6346 {
6347 const char *pcszIommuType;
6348 switch (hw.iommuType)
6349 {
6350 case IommuType_None: pcszIommuType = "None"; break;
6351 case IommuType_Automatic: pcszIommuType = "Automatic"; break;
6352 case IommuType_AMD: pcszIommuType = "AMD"; break;
6353 case IommuType_Intel: pcszIommuType = "Intel"; break;
6354 default: Assert(false); pcszIommuType = "None"; break;
6355 }
6356
6357 xml::ElementNode *pelmIommu = pelmHardware->createChild("Iommu");
6358 pelmIommu->setAttribute("type", pcszIommuType);
6359 }
6360
6361 if (!hw.areBootOrderDefaultSettings())
6362 {
6363 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
6364 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
6365 it != hw.mapBootOrder.end();
6366 ++it)
6367 {
6368 uint32_t i = it->first;
6369 DeviceType_T type = it->second;
6370 const char *pcszDevice;
6371
6372 switch (type)
6373 {
6374 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
6375 case DeviceType_DVD: pcszDevice = "DVD"; break;
6376 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
6377 case DeviceType_Network: pcszDevice = "Network"; break;
6378 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
6379 }
6380
6381 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
6382 pelmOrder->setAttribute("position",
6383 i + 1); // XML is 1-based but internal data is 0-based
6384 pelmOrder->setAttribute("device", pcszDevice);
6385 }
6386 }
6387
6388 if (!hw.graphicsAdapter.areDefaultSettings())
6389 {
6390 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
6391 if (hw.graphicsAdapter.graphicsControllerType != GraphicsControllerType_VBoxVGA)
6392 {
6393 const char *pcszGraphics;
6394 switch (hw.graphicsAdapter.graphicsControllerType)
6395 {
6396 case GraphicsControllerType_VBoxVGA: pcszGraphics = "VBoxVGA"; break;
6397 case GraphicsControllerType_VMSVGA: pcszGraphics = "VMSVGA"; break;
6398 case GraphicsControllerType_VBoxSVGA: pcszGraphics = "VBoxSVGA"; break;
6399 default: /*case GraphicsControllerType_Null:*/ pcszGraphics = "None"; break;
6400 }
6401 pelmDisplay->setAttribute("controller", pcszGraphics);
6402 }
6403 if (hw.graphicsAdapter.ulVRAMSizeMB != 8)
6404 pelmDisplay->setAttribute("VRAMSize", hw.graphicsAdapter.ulVRAMSizeMB);
6405 if (hw.graphicsAdapter.cMonitors > 1)
6406 pelmDisplay->setAttribute("monitorCount", hw.graphicsAdapter.cMonitors);
6407 if (hw.graphicsAdapter.fAccelerate3D)
6408 pelmDisplay->setAttribute("accelerate3D", hw.graphicsAdapter.fAccelerate3D);
6409
6410 if (m->sv >= SettingsVersion_v1_8)
6411 {
6412 if (hw.graphicsAdapter.fAccelerate2DVideo)
6413 pelmDisplay->setAttribute("accelerate2DVideo", hw.graphicsAdapter.fAccelerate2DVideo);
6414 }
6415 }
6416
6417 if (m->sv >= SettingsVersion_v1_14 && !hw.recordingSettings.areDefaultSettings())
6418 {
6419 xml::ElementNode *pelmVideoCapture = pelmHardware->createChild("VideoCapture");
6420
6421 if (hw.recordingSettings.fEnabled)
6422 pelmVideoCapture->setAttribute("enabled", hw.recordingSettings.fEnabled);
6423
6424 /* Right now I don't want to bump the settings version, so just convert the enabled
6425 * screens to the former uint64t_t bit array and vice versa. */
6426 uint64_t u64VideoCaptureScreens = 0;
6427 RecordingScreenMap::const_iterator itScreen = hw.recordingSettings.mapScreens.begin();
6428 while (itScreen != hw.recordingSettings.mapScreens.end())
6429 {
6430 if (itScreen->second.fEnabled)
6431 u64VideoCaptureScreens |= RT_BIT_64(itScreen->first);
6432 ++itScreen;
6433 }
6434
6435 if (u64VideoCaptureScreens)
6436 pelmVideoCapture->setAttribute("screens", u64VideoCaptureScreens);
6437
6438 /* At the moment we only support one capturing configuration, that is, all screens
6439 * have the same configuration. So load/save to/from screen 0. */
6440 Assert(hw.recordingSettings.mapScreens.size());
6441 const RecordingScreenMap::const_iterator itScreen0Settings = hw.recordingSettings.mapScreens.find(0);
6442 Assert(itScreen0Settings != hw.recordingSettings.mapScreens.end());
6443
6444 if (itScreen0Settings->second.ulMaxTimeS)
6445 pelmVideoCapture->setAttribute("maxTime", itScreen0Settings->second.ulMaxTimeS);
6446 if (itScreen0Settings->second.strOptions.isNotEmpty())
6447 pelmVideoCapture->setAttributePath("options", itScreen0Settings->second.strOptions);
6448
6449 if (!itScreen0Settings->second.File.strName.isEmpty())
6450 pelmVideoCapture->setAttributePath("file", itScreen0Settings->second.File.strName);
6451 if (itScreen0Settings->second.File.ulMaxSizeMB)
6452 pelmVideoCapture->setAttribute("maxSize", itScreen0Settings->second.File.ulMaxSizeMB);
6453
6454 if ( itScreen0Settings->second.Video.ulWidth != 1024
6455 || itScreen0Settings->second.Video.ulHeight != 768)
6456 {
6457 pelmVideoCapture->setAttribute("horzRes", itScreen0Settings->second.Video.ulWidth);
6458 pelmVideoCapture->setAttribute("vertRes", itScreen0Settings->second.Video.ulHeight);
6459 }
6460 if (itScreen0Settings->second.Video.ulRate != 512)
6461 pelmVideoCapture->setAttribute("rate", itScreen0Settings->second.Video.ulRate);
6462 if (itScreen0Settings->second.Video.ulFPS)
6463 pelmVideoCapture->setAttribute("fps", itScreen0Settings->second.Video.ulFPS);
6464 }
6465
6466 if (!hw.vrdeSettings.areDefaultSettings(m->sv))
6467 {
6468 xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay");
6469 if (m->sv < SettingsVersion_v1_16 ? !hw.vrdeSettings.fEnabled : hw.vrdeSettings.fEnabled)
6470 pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled);
6471 if (m->sv < SettingsVersion_v1_11)
6472 {
6473 /* In VBox 4.0 these attributes are replaced with "Properties". */
6474 Utf8Str strPort;
6475 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports");
6476 if (it != hw.vrdeSettings.mapProperties.end())
6477 strPort = it->second;
6478 if (!strPort.length())
6479 strPort = "3389";
6480 pelmVRDE->setAttribute("port", strPort);
6481
6482 Utf8Str strAddress;
6483 it = hw.vrdeSettings.mapProperties.find("TCP/Address");
6484 if (it != hw.vrdeSettings.mapProperties.end())
6485 strAddress = it->second;
6486 if (strAddress.length())
6487 pelmVRDE->setAttribute("netAddress", strAddress);
6488 }
6489 if (hw.vrdeSettings.authType != AuthType_Null)
6490 {
6491 const char *pcszAuthType;
6492 switch (hw.vrdeSettings.authType)
6493 {
6494 case AuthType_Guest: pcszAuthType = "Guest"; break;
6495 case AuthType_External: pcszAuthType = "External"; break;
6496 default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break;
6497 }
6498 pelmVRDE->setAttribute("authType", pcszAuthType);
6499 }
6500
6501 if (hw.vrdeSettings.ulAuthTimeout != 0 && hw.vrdeSettings.ulAuthTimeout != 5000)
6502 pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout);
6503 if (hw.vrdeSettings.fAllowMultiConnection)
6504 pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
6505 if (hw.vrdeSettings.fReuseSingleConnection)
6506 pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
6507
6508 if (m->sv == SettingsVersion_v1_10)
6509 {
6510 xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel");
6511
6512 /* In 4.0 videochannel settings were replaced with properties, so look at properties. */
6513 Utf8Str str;
6514 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
6515 if (it != hw.vrdeSettings.mapProperties.end())
6516 str = it->second;
6517 bool fVideoChannel = RTStrICmp(str.c_str(), "true") == 0
6518 || RTStrCmp(str.c_str(), "1") == 0;
6519 pelmVideoChannel->setAttribute("enabled", fVideoChannel);
6520
6521 it = hw.vrdeSettings.mapProperties.find("VideoChannel/Quality");
6522 if (it != hw.vrdeSettings.mapProperties.end())
6523 str = it->second;
6524 uint32_t ulVideoChannelQuality = RTStrToUInt32(str.c_str()); /* This returns 0 on invalid string which is ok. */
6525 if (ulVideoChannelQuality == 0)
6526 ulVideoChannelQuality = 75;
6527 else
6528 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
6529 pelmVideoChannel->setAttribute("quality", ulVideoChannelQuality);
6530 }
6531 if (m->sv >= SettingsVersion_v1_11)
6532 {
6533 if (hw.vrdeSettings.strAuthLibrary.length())
6534 pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary);
6535 if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty())
6536 pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
6537 if (hw.vrdeSettings.mapProperties.size() > 0)
6538 {
6539 xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties");
6540 for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin();
6541 it != hw.vrdeSettings.mapProperties.end();
6542 ++it)
6543 {
6544 const Utf8Str &strName = it->first;
6545 const Utf8Str &strValue = it->second;
6546 xml::ElementNode *pelm = pelmProperties->createChild("Property");
6547 pelm->setAttribute("name", strName);
6548 pelm->setAttribute("value", strValue);
6549 }
6550 }
6551 }
6552 }
6553
6554 if (!hw.biosSettings.areDefaultSettings())
6555 {
6556 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
6557 if (!hw.biosSettings.fACPIEnabled)
6558 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
6559 if (hw.biosSettings.fIOAPICEnabled)
6560 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
6561 if (hw.biosSettings.apicMode != APICMode_APIC)
6562 {
6563 const char *pcszAPIC;
6564 switch (hw.biosSettings.apicMode)
6565 {
6566 case APICMode_Disabled:
6567 pcszAPIC = "Disabled";
6568 break;
6569 case APICMode_APIC:
6570 default:
6571 pcszAPIC = "APIC";
6572 break;
6573 case APICMode_X2APIC:
6574 pcszAPIC = "X2APIC";
6575 break;
6576 }
6577 pelmBIOS->createChild("APIC")->setAttribute("mode", pcszAPIC);
6578 }
6579
6580 if ( !hw.biosSettings.fLogoFadeIn
6581 || !hw.biosSettings.fLogoFadeOut
6582 || hw.biosSettings.ulLogoDisplayTime
6583 || !hw.biosSettings.strLogoImagePath.isEmpty())
6584 {
6585 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
6586 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
6587 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
6588 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
6589 if (!hw.biosSettings.strLogoImagePath.isEmpty())
6590 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
6591 }
6592
6593 if (hw.biosSettings.biosBootMenuMode != BIOSBootMenuMode_MessageAndMenu)
6594 {
6595 const char *pcszBootMenu;
6596 switch (hw.biosSettings.biosBootMenuMode)
6597 {
6598 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
6599 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
6600 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
6601 }
6602 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
6603 }
6604 if (hw.biosSettings.llTimeOffset)
6605 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
6606 if (hw.biosSettings.fPXEDebugEnabled)
6607 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
6608 if (!hw.nvramSettings.strNvramPath.isEmpty())
6609 pelmBIOS->createChild("NVRAM")->setAttribute("path", hw.nvramSettings.strNvramPath);
6610 if (hw.biosSettings.fSmbiosUuidLittleEndian)
6611 pelmBIOS->createChild("SmbiosUuidLittleEndian")->setAttribute("enabled", hw.biosSettings.fSmbiosUuidLittleEndian);
6612 }
6613
6614 if (!hw.tpmSettings.areDefaultSettings())
6615 {
6616 xml::ElementNode *pelmTpm = pelmHardware->createChild("TrustedPlatformModule");
6617
6618 const char *pcszTpm;
6619 switch (hw.tpmSettings.tpmType)
6620 {
6621 default:
6622 case TpmType_None:
6623 pcszTpm = "None";
6624 break;
6625 case TpmType_v1_2:
6626 pcszTpm = "v1_2";
6627 break;
6628 case TpmType_v2_0:
6629 pcszTpm = "v2_0";
6630 break;
6631 case TpmType_Host:
6632 pcszTpm = "Host";
6633 break;
6634 case TpmType_Swtpm:
6635 pcszTpm = "Swtpm";
6636 break;
6637 }
6638 pelmTpm->setAttribute("type", pcszTpm);
6639 pelmTpm->setAttribute("location", hw.tpmSettings.strLocation);
6640 }
6641
6642 if (m->sv < SettingsVersion_v1_9)
6643 {
6644 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
6645 // run thru the storage controllers to see if we have a DVD or floppy drives
6646 size_t cDVDs = 0;
6647 size_t cFloppies = 0;
6648
6649 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
6650 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
6651
6652 for (StorageControllersList::const_iterator it = hw.storage.llStorageControllers.begin();
6653 it != hw.storage.llStorageControllers.end();
6654 ++it)
6655 {
6656 const StorageController &sctl = *it;
6657 // in old settings format, the DVD drive could only have been under the IDE controller
6658 if (sctl.storageBus == StorageBus_IDE)
6659 {
6660 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
6661 it2 != sctl.llAttachedDevices.end();
6662 ++it2)
6663 {
6664 const AttachedDevice &att = *it2;
6665 if (att.deviceType == DeviceType_DVD)
6666 {
6667 if (cDVDs > 0)
6668 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
6669
6670 ++cDVDs;
6671
6672 pelmDVD->setAttribute("passthrough", att.fPassThrough);
6673 if (att.fTempEject)
6674 pelmDVD->setAttribute("tempeject", att.fTempEject);
6675
6676 if (!att.uuid.isZero() && att.uuid.isValid())
6677 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
6678 else if (att.strHostDriveSrc.length())
6679 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6680 }
6681 }
6682 }
6683 else if (sctl.storageBus == StorageBus_Floppy)
6684 {
6685 size_t cFloppiesHere = sctl.llAttachedDevices.size();
6686 if (cFloppiesHere > 1)
6687 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
6688 if (cFloppiesHere)
6689 {
6690 const AttachedDevice &att = sctl.llAttachedDevices.front();
6691 pelmFloppy->setAttribute("enabled", true);
6692
6693 if (!att.uuid.isZero() && att.uuid.isValid())
6694 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
6695 else if (att.strHostDriveSrc.length())
6696 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6697 }
6698
6699 cFloppies += cFloppiesHere;
6700 }
6701 }
6702
6703 if (cFloppies == 0)
6704 pelmFloppy->setAttribute("enabled", false);
6705 else if (cFloppies > 1)
6706 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
6707 }
6708
6709 if (m->sv < SettingsVersion_v1_14)
6710 {
6711 bool fOhciEnabled = false;
6712 bool fEhciEnabled = false;
6713 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
6714
6715 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
6716 it != hw.usbSettings.llUSBControllers.end();
6717 ++it)
6718 {
6719 const USBController &ctrl = *it;
6720
6721 switch (ctrl.enmType)
6722 {
6723 case USBControllerType_OHCI:
6724 fOhciEnabled = true;
6725 break;
6726 case USBControllerType_EHCI:
6727 fEhciEnabled = true;
6728 break;
6729 default:
6730 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
6731 }
6732 }
6733
6734 pelmUSB->setAttribute("enabled", fOhciEnabled);
6735 pelmUSB->setAttribute("enabledEhci", fEhciEnabled);
6736
6737 buildUSBDeviceFilters(*pelmUSB, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
6738 }
6739 else
6740 {
6741 if ( hw.usbSettings.llUSBControllers.size()
6742 || hw.usbSettings.llDeviceFilters.size())
6743 {
6744 xml::ElementNode *pelmUSB = pelmHardware->createChild("USB");
6745 if (hw.usbSettings.llUSBControllers.size())
6746 {
6747 xml::ElementNode *pelmCtrls = pelmUSB->createChild("Controllers");
6748
6749 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
6750 it != hw.usbSettings.llUSBControllers.end();
6751 ++it)
6752 {
6753 const USBController &ctrl = *it;
6754 com::Utf8Str strType;
6755 xml::ElementNode *pelmCtrl = pelmCtrls->createChild("Controller");
6756
6757 switch (ctrl.enmType)
6758 {
6759 case USBControllerType_OHCI:
6760 strType = "OHCI";
6761 break;
6762 case USBControllerType_EHCI:
6763 strType = "EHCI";
6764 break;
6765 case USBControllerType_XHCI:
6766 strType = "XHCI";
6767 break;
6768 default:
6769 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
6770 }
6771
6772 pelmCtrl->setAttribute("name", ctrl.strName);
6773 pelmCtrl->setAttribute("type", strType);
6774 }
6775 }
6776
6777 if (hw.usbSettings.llDeviceFilters.size())
6778 {
6779 xml::ElementNode *pelmFilters = pelmUSB->createChild("DeviceFilters");
6780 buildUSBDeviceFilters(*pelmFilters, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
6781 }
6782 }
6783 }
6784
6785 if ( hw.llNetworkAdapters.size()
6786 && !hw.areAllNetworkAdaptersDefaultSettings(m->sv))
6787 {
6788 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
6789 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
6790 it != hw.llNetworkAdapters.end();
6791 ++it)
6792 {
6793 const NetworkAdapter &nic = *it;
6794
6795 if (!nic.areDefaultSettings(m->sv))
6796 {
6797 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
6798 pelmAdapter->setAttribute("slot", nic.ulSlot);
6799 if (nic.fEnabled)
6800 pelmAdapter->setAttribute("enabled", nic.fEnabled);
6801 if (!nic.strMACAddress.isEmpty())
6802 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
6803 if ( (m->sv >= SettingsVersion_v1_16 && !nic.fCableConnected)
6804 || (m->sv < SettingsVersion_v1_16 && nic.fCableConnected))
6805 pelmAdapter->setAttribute("cable", nic.fCableConnected);
6806 if (nic.ulLineSpeed)
6807 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
6808 if (nic.ulBootPriority != 0)
6809 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
6810 if (nic.fTraceEnabled)
6811 {
6812 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
6813 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
6814 }
6815 if (nic.strBandwidthGroup.isNotEmpty())
6816 pelmAdapter->setAttribute("bandwidthGroup", nic.strBandwidthGroup);
6817
6818 const char *pszPolicy;
6819 switch (nic.enmPromiscModePolicy)
6820 {
6821 case NetworkAdapterPromiscModePolicy_Deny: pszPolicy = NULL; break;
6822 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPolicy = "AllowNetwork"; break;
6823 case NetworkAdapterPromiscModePolicy_AllowAll: pszPolicy = "AllowAll"; break;
6824 default: pszPolicy = NULL; AssertFailed(); break;
6825 }
6826 if (pszPolicy)
6827 pelmAdapter->setAttribute("promiscuousModePolicy", pszPolicy);
6828
6829 if ( (m->sv >= SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C973)
6830 || (m->sv < SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C970A))
6831 {
6832 const char *pcszType;
6833 switch (nic.type)
6834 {
6835 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
6836 case NetworkAdapterType_Am79C960: pcszType = "Am79C960"; break;
6837 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
6838 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
6839 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
6840 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
6841 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
6842 }
6843 pelmAdapter->setAttribute("type", pcszType);
6844 }
6845
6846 xml::ElementNode *pelmNAT;
6847 if (m->sv < SettingsVersion_v1_10)
6848 {
6849 switch (nic.mode)
6850 {
6851 case NetworkAttachmentType_NAT:
6852 pelmNAT = pelmAdapter->createChild("NAT");
6853 if (nic.nat.strNetwork.length())
6854 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6855 break;
6856
6857 case NetworkAttachmentType_Bridged:
6858 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
6859 break;
6860
6861 case NetworkAttachmentType_Internal:
6862 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
6863 break;
6864
6865 case NetworkAttachmentType_HostOnly:
6866 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
6867 break;
6868
6869 default: /*case NetworkAttachmentType_Null:*/
6870 break;
6871 }
6872 }
6873 else
6874 {
6875 /* m->sv >= SettingsVersion_v1_10 */
6876 if (!nic.areDisabledDefaultSettings(m->sv))
6877 {
6878 xml::ElementNode *pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
6879 if (nic.mode != NetworkAttachmentType_NAT)
6880 buildNetworkXML(NetworkAttachmentType_NAT, false, *pelmDisabledNode, nic);
6881 if (nic.mode != NetworkAttachmentType_Bridged)
6882 buildNetworkXML(NetworkAttachmentType_Bridged, false, *pelmDisabledNode, nic);
6883 if (nic.mode != NetworkAttachmentType_Internal)
6884 buildNetworkXML(NetworkAttachmentType_Internal, false, *pelmDisabledNode, nic);
6885 if (nic.mode != NetworkAttachmentType_HostOnly)
6886 buildNetworkXML(NetworkAttachmentType_HostOnly, false, *pelmDisabledNode, nic);
6887 if (nic.mode != NetworkAttachmentType_Generic)
6888 buildNetworkXML(NetworkAttachmentType_Generic, false, *pelmDisabledNode, nic);
6889 if (nic.mode != NetworkAttachmentType_NATNetwork)
6890 buildNetworkXML(NetworkAttachmentType_NATNetwork, false, *pelmDisabledNode, nic);
6891#ifdef VBOX_WITH_CLOUD_NET
6892 /// @todo Bump settings version!
6893 if (nic.mode != NetworkAttachmentType_Cloud)
6894 buildNetworkXML(NetworkAttachmentType_Cloud, false, *pelmDisabledNode, nic);
6895#endif /* VBOX_WITH_CLOUD_NET */
6896#ifdef VBOX_WITH_VMNET
6897 if (nic.mode != NetworkAttachmentType_HostOnlyNetwork)
6898 buildNetworkXML(NetworkAttachmentType_HostOnlyNetwork, false, *pelmDisabledNode, nic);
6899#endif /* VBOX_WITH_VMNET */
6900 }
6901 buildNetworkXML(nic.mode, true, *pelmAdapter, nic);
6902 }
6903 }
6904 }
6905 }
6906
6907 if (hw.llSerialPorts.size())
6908 {
6909 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
6910 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
6911 it != hw.llSerialPorts.end();
6912 ++it)
6913 {
6914 const SerialPort &port = *it;
6915 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6916 pelmPort->setAttribute("slot", port.ulSlot);
6917 pelmPort->setAttribute("enabled", port.fEnabled);
6918 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6919 pelmPort->setAttribute("IRQ", port.ulIRQ);
6920
6921 const char *pcszHostMode;
6922 switch (port.portMode)
6923 {
6924 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
6925 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
6926 case PortMode_TCP: pcszHostMode = "TCP"; break;
6927 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
6928 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
6929 }
6930 switch (port.portMode)
6931 {
6932 case PortMode_TCP:
6933 case PortMode_HostPipe:
6934 pelmPort->setAttribute("server", port.fServer);
6935 RT_FALL_THRU();
6936 case PortMode_HostDevice:
6937 case PortMode_RawFile:
6938 pelmPort->setAttribute("path", port.strPath);
6939 break;
6940
6941 default:
6942 break;
6943 }
6944 pelmPort->setAttribute("hostMode", pcszHostMode);
6945
6946 if ( m->sv >= SettingsVersion_v1_17
6947 && port.uartType != UartType_U16550A)
6948 {
6949 const char *pcszUartType;
6950
6951 switch (port.uartType)
6952 {
6953 case UartType_U16450: pcszUartType = "16450"; break;
6954 case UartType_U16550A: pcszUartType = "16550A"; break;
6955 case UartType_U16750: pcszUartType = "16750"; break;
6956 default: pcszUartType = "16550A"; break;
6957 }
6958 pelmPort->setAttribute("uartType", pcszUartType);
6959 }
6960 }
6961 }
6962
6963 if (hw.llParallelPorts.size())
6964 {
6965 xml::ElementNode *pelmPorts = pelmHardware->createChild("LPT");
6966 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
6967 it != hw.llParallelPorts.end();
6968 ++it)
6969 {
6970 const ParallelPort &port = *it;
6971 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6972 pelmPort->setAttribute("slot", port.ulSlot);
6973 pelmPort->setAttribute("enabled", port.fEnabled);
6974 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6975 pelmPort->setAttribute("IRQ", port.ulIRQ);
6976 if (port.strPath.length())
6977 pelmPort->setAttribute("path", port.strPath);
6978 }
6979 }
6980
6981 /* Always write the AudioAdapter config, intentionally not checking if
6982 * the settings are at the default, because that would be problematic
6983 * for the configured host driver type, which would automatically change
6984 * if the default host driver is detected differently. */
6985 {
6986 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
6987
6988 const char *pcszController;
6989 switch (hw.audioAdapter.controllerType)
6990 {
6991 case AudioControllerType_SB16:
6992 pcszController = "SB16";
6993 break;
6994 case AudioControllerType_HDA:
6995 if (m->sv >= SettingsVersion_v1_11)
6996 {
6997 pcszController = "HDA";
6998 break;
6999 }
7000 RT_FALL_THRU();
7001 case AudioControllerType_AC97:
7002 default:
7003 pcszController = NULL;
7004 break;
7005 }
7006 if (pcszController)
7007 pelmAudio->setAttribute("controller", pcszController);
7008
7009 const char *pcszCodec;
7010 switch (hw.audioAdapter.codecType)
7011 {
7012 /* Only write out the setting for non-default AC'97 codec
7013 * and leave the rest alone.
7014 */
7015#if 0
7016 case AudioCodecType_SB16:
7017 pcszCodec = "SB16";
7018 break;
7019 case AudioCodecType_STAC9221:
7020 pcszCodec = "STAC9221";
7021 break;
7022 case AudioCodecType_STAC9700:
7023 pcszCodec = "STAC9700";
7024 break;
7025#endif
7026 case AudioCodecType_AD1980:
7027 pcszCodec = "AD1980";
7028 break;
7029 default:
7030 /* Don't write out anything if unknown. */
7031 pcszCodec = NULL;
7032 }
7033 if (pcszCodec)
7034 pelmAudio->setAttribute("codec", pcszCodec);
7035
7036 const char *pcszDriver;
7037 switch (hw.audioAdapter.driverType)
7038 {
7039 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
7040 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
7041 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
7042 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
7043 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
7044 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
7045 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
7046 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
7047 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
7048 }
7049 /* Deliberately have the audio driver explicitly in the config file,
7050 * otherwise an unwritten default driver triggers auto-detection. */
7051 pelmAudio->setAttribute("driver", pcszDriver);
7052
7053 if (hw.audioAdapter.fEnabled || m->sv < SettingsVersion_v1_16)
7054 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
7055
7056 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledIn)
7057 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledIn))
7058 pelmAudio->setAttribute("enabledIn", hw.audioAdapter.fEnabledIn);
7059
7060 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledOut)
7061 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledOut))
7062 pelmAudio->setAttribute("enabledOut", hw.audioAdapter.fEnabledOut);
7063
7064 if (m->sv >= SettingsVersion_v1_15 && hw.audioAdapter.properties.size() > 0)
7065 {
7066 for (StringsMap::const_iterator it = hw.audioAdapter.properties.begin();
7067 it != hw.audioAdapter.properties.end();
7068 ++it)
7069 {
7070 const Utf8Str &strName = it->first;
7071 const Utf8Str &strValue = it->second;
7072 xml::ElementNode *pelm = pelmAudio->createChild("Property");
7073 pelm->setAttribute("name", strName);
7074 pelm->setAttribute("value", strValue);
7075 }
7076 }
7077 }
7078
7079 if (m->sv >= SettingsVersion_v1_10 && machineUserData.fRTCUseUTC)
7080 {
7081 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
7082 pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local");
7083 }
7084
7085 if (hw.llSharedFolders.size())
7086 {
7087 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
7088 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
7089 it != hw.llSharedFolders.end();
7090 ++it)
7091 {
7092 const SharedFolder &sf = *it;
7093 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
7094 pelmThis->setAttribute("name", sf.strName);
7095 pelmThis->setAttribute("hostPath", sf.strHostPath);
7096 pelmThis->setAttribute("writable", sf.fWritable);
7097 pelmThis->setAttribute("autoMount", sf.fAutoMount);
7098 if (sf.strAutoMountPoint.isNotEmpty())
7099 pelmThis->setAttribute("autoMountPoint", sf.strAutoMountPoint);
7100 }
7101 }
7102
7103 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
7104 if (pelmClip)
7105 {
7106 if (hw.clipboardMode != ClipboardMode_Disabled)
7107 {
7108 const char *pcszClip;
7109 switch (hw.clipboardMode)
7110 {
7111 default: /*case ClipboardMode_Disabled:*/ pcszClip = "Disabled"; break;
7112 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
7113 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
7114 case ClipboardMode_Bidirectional: pcszClip = "Bidirectional"; break;
7115 }
7116 pelmClip->setAttribute("mode", pcszClip);
7117 }
7118
7119 if (hw.fClipboardFileTransfersEnabled)
7120 pelmClip->setAttribute("fileTransfersEnabled", hw.fClipboardFileTransfersEnabled);
7121 }
7122
7123 if (hw.dndMode != DnDMode_Disabled)
7124 {
7125 xml::ElementNode *pelmDragAndDrop = pelmHardware->createChild("DragAndDrop");
7126 const char *pcszDragAndDrop;
7127 switch (hw.dndMode)
7128 {
7129 default: /*case DnDMode_Disabled:*/ pcszDragAndDrop = "Disabled"; break;
7130 case DnDMode_HostToGuest: pcszDragAndDrop = "HostToGuest"; break;
7131 case DnDMode_GuestToHost: pcszDragAndDrop = "GuestToHost"; break;
7132 case DnDMode_Bidirectional: pcszDragAndDrop = "Bidirectional"; break;
7133 }
7134 pelmDragAndDrop->setAttribute("mode", pcszDragAndDrop);
7135 }
7136
7137 if ( m->sv >= SettingsVersion_v1_10
7138 && !hw.ioSettings.areDefaultSettings())
7139 {
7140 xml::ElementNode *pelmIO = pelmHardware->createChild("IO");
7141 xml::ElementNode *pelmIOCache;
7142
7143 if (!hw.ioSettings.areDefaultSettings())
7144 {
7145 pelmIOCache = pelmIO->createChild("IoCache");
7146 if (!hw.ioSettings.fIOCacheEnabled)
7147 pelmIOCache->setAttribute("enabled", hw.ioSettings.fIOCacheEnabled);
7148 if (hw.ioSettings.ulIOCacheSize != 5)
7149 pelmIOCache->setAttribute("size", hw.ioSettings.ulIOCacheSize);
7150 }
7151
7152 if ( m->sv >= SettingsVersion_v1_11
7153 && hw.ioSettings.llBandwidthGroups.size())
7154 {
7155 xml::ElementNode *pelmBandwidthGroups = pelmIO->createChild("BandwidthGroups");
7156 for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin();
7157 it != hw.ioSettings.llBandwidthGroups.end();
7158 ++it)
7159 {
7160 const BandwidthGroup &gr = *it;
7161 const char *pcszType;
7162 xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup");
7163 pelmThis->setAttribute("name", gr.strName);
7164 switch (gr.enmType)
7165 {
7166 case BandwidthGroupType_Network: pcszType = "Network"; break;
7167 default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break;
7168 }
7169 pelmThis->setAttribute("type", pcszType);
7170 if (m->sv >= SettingsVersion_v1_13)
7171 pelmThis->setAttribute("maxBytesPerSec", gr.cMaxBytesPerSec);
7172 else
7173 pelmThis->setAttribute("maxMbPerSec", gr.cMaxBytesPerSec / _1M);
7174 }
7175 }
7176 }
7177
7178 if ( m->sv >= SettingsVersion_v1_12
7179 && hw.pciAttachments.size())
7180 {
7181 xml::ElementNode *pelmPCI = pelmHardware->createChild("HostPci");
7182 xml::ElementNode *pelmPCIDevices = pelmPCI->createChild("Devices");
7183
7184 for (HostPCIDeviceAttachmentList::const_iterator it = hw.pciAttachments.begin();
7185 it != hw.pciAttachments.end();
7186 ++it)
7187 {
7188 const HostPCIDeviceAttachment &hpda = *it;
7189
7190 xml::ElementNode *pelmThis = pelmPCIDevices->createChild("Device");
7191
7192 pelmThis->setAttribute("host", hpda.uHostAddress);
7193 pelmThis->setAttribute("guest", hpda.uGuestAddress);
7194 pelmThis->setAttribute("name", hpda.strDeviceName);
7195 }
7196 }
7197
7198 if ( m->sv >= SettingsVersion_v1_12
7199 && hw.fEmulatedUSBCardReader)
7200 {
7201 xml::ElementNode *pelmEmulatedUSB = pelmHardware->createChild("EmulatedUSB");
7202
7203 xml::ElementNode *pelmCardReader = pelmEmulatedUSB->createChild("CardReader");
7204 pelmCardReader->setAttribute("enabled", hw.fEmulatedUSBCardReader);
7205 }
7206
7207 if ( m->sv >= SettingsVersion_v1_14
7208 && !hw.strDefaultFrontend.isEmpty())
7209 {
7210 xml::ElementNode *pelmFrontend = pelmHardware->createChild("Frontend");
7211 xml::ElementNode *pelmDefault = pelmFrontend->createChild("Default");
7212 pelmDefault->setAttribute("type", hw.strDefaultFrontend);
7213 }
7214
7215 if (hw.ulMemoryBalloonSize)
7216 {
7217 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
7218 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
7219 }
7220
7221 if (hw.llGuestProperties.size())
7222 {
7223 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
7224 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
7225 it != hw.llGuestProperties.end();
7226 ++it)
7227 {
7228 const GuestProperty &prop = *it;
7229 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
7230 pelmProp->setAttribute("name", prop.strName);
7231 pelmProp->setAttribute("value", prop.strValue);
7232 pelmProp->setAttribute("timestamp", prop.timestamp);
7233 pelmProp->setAttribute("flags", prop.strFlags);
7234 }
7235 }
7236
7237 /* Starting with settings version of 6.0 (and only 6.1 and later does this, while
7238 * 5.2 and 6.0 understand it), place storage controller settings under hardware,
7239 * where it always should've been. */
7240 xml::ElementNode &elmStorageParent = (m->sv >= SettingsVersion_v1_17) ? *pelmHardware : elmParent;
7241 buildStorageControllersXML(elmStorageParent,
7242 hw.storage,
7243 !!(fl & BuildMachineXML_SkipRemovableMedia),
7244 pllElementsWithUuidAttributes);
7245}
7246
7247/**
7248 * Fill a \<Network\> node. Only relevant for XML version >= v1_10.
7249 * @param mode
7250 * @param fEnabled
7251 * @param elmParent
7252 * @param nic
7253 */
7254void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
7255 bool fEnabled,
7256 xml::ElementNode &elmParent,
7257 const NetworkAdapter &nic)
7258{
7259 switch (mode)
7260 {
7261 case NetworkAttachmentType_NAT:
7262 // For the currently active network attachment type we have to
7263 // generate the tag, otherwise the attachment type is lost.
7264 if (fEnabled || !nic.nat.areDefaultSettings(m->sv))
7265 {
7266 xml::ElementNode *pelmNAT = elmParent.createChild("NAT");
7267
7268 if (!nic.nat.areDefaultSettings(m->sv))
7269 {
7270 if (nic.nat.strNetwork.length())
7271 pelmNAT->setAttribute("network", nic.nat.strNetwork);
7272 if (nic.nat.strBindIP.length())
7273 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
7274 if (nic.nat.u32Mtu)
7275 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
7276 if (nic.nat.u32SockRcv)
7277 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
7278 if (nic.nat.u32SockSnd)
7279 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
7280 if (nic.nat.u32TcpRcv)
7281 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
7282 if (nic.nat.u32TcpSnd)
7283 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
7284 if (!nic.nat.areLocalhostReachableDefaultSettings(m->sv))
7285 pelmNAT->setAttribute("localhost-reachable", nic.nat.fLocalhostReachable);
7286 if (!nic.nat.areDNSDefaultSettings())
7287 {
7288 xml::ElementNode *pelmDNS = pelmNAT->createChild("DNS");
7289 if (!nic.nat.fDNSPassDomain)
7290 pelmDNS->setAttribute("pass-domain", nic.nat.fDNSPassDomain);
7291 if (nic.nat.fDNSProxy)
7292 pelmDNS->setAttribute("use-proxy", nic.nat.fDNSProxy);
7293 if (nic.nat.fDNSUseHostResolver)
7294 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDNSUseHostResolver);
7295 }
7296
7297 if (!nic.nat.areAliasDefaultSettings())
7298 {
7299 xml::ElementNode *pelmAlias = pelmNAT->createChild("Alias");
7300 if (nic.nat.fAliasLog)
7301 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
7302 if (nic.nat.fAliasProxyOnly)
7303 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
7304 if (nic.nat.fAliasUseSamePorts)
7305 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
7306 }
7307
7308 if (!nic.nat.areTFTPDefaultSettings())
7309 {
7310 xml::ElementNode *pelmTFTP;
7311 pelmTFTP = pelmNAT->createChild("TFTP");
7312 if (nic.nat.strTFTPPrefix.length())
7313 pelmTFTP->setAttribute("prefix", nic.nat.strTFTPPrefix);
7314 if (nic.nat.strTFTPBootFile.length())
7315 pelmTFTP->setAttribute("boot-file", nic.nat.strTFTPBootFile);
7316 if (nic.nat.strTFTPNextServer.length())
7317 pelmTFTP->setAttribute("next-server", nic.nat.strTFTPNextServer);
7318 }
7319 buildNATForwardRulesMap(*pelmNAT, nic.nat.mapRules);
7320 }
7321 }
7322 break;
7323
7324 case NetworkAttachmentType_Bridged:
7325 // For the currently active network attachment type we have to
7326 // generate the tag, otherwise the attachment type is lost.
7327 if (fEnabled || !nic.strBridgedName.isEmpty())
7328 {
7329 xml::ElementNode *pelmMode = elmParent.createChild("BridgedInterface");
7330 if (!nic.strBridgedName.isEmpty())
7331 pelmMode->setAttribute("name", nic.strBridgedName);
7332 }
7333 break;
7334
7335 case NetworkAttachmentType_Internal:
7336 // For the currently active network attachment type we have to
7337 // generate the tag, otherwise the attachment type is lost.
7338 if (fEnabled || !nic.strInternalNetworkName.isEmpty())
7339 {
7340 xml::ElementNode *pelmMode = elmParent.createChild("InternalNetwork");
7341 if (!nic.strInternalNetworkName.isEmpty())
7342 pelmMode->setAttribute("name", nic.strInternalNetworkName);
7343 }
7344 break;
7345
7346 case NetworkAttachmentType_HostOnly:
7347 // For the currently active network attachment type we have to
7348 // generate the tag, otherwise the attachment type is lost.
7349 if (fEnabled || !nic.strHostOnlyName.isEmpty())
7350 {
7351 xml::ElementNode *pelmMode = elmParent.createChild("HostOnlyInterface");
7352 if (!nic.strHostOnlyName.isEmpty())
7353 pelmMode->setAttribute("name", nic.strHostOnlyName);
7354 }
7355 break;
7356
7357#ifdef VBOX_WITH_VMNET
7358 case NetworkAttachmentType_HostOnlyNetwork:
7359 // For the currently active network attachment type we have to
7360 // generate the tag, otherwise the attachment type is lost.
7361 if (fEnabled || !nic.strHostOnlyNetworkName.isEmpty())
7362 {
7363 xml::ElementNode *pelmMode = elmParent.createChild("HostOnlyNetwork");
7364 if (!nic.strHostOnlyNetworkName.isEmpty())
7365 pelmMode->setAttribute("name", nic.strHostOnlyNetworkName);
7366 }
7367 break;
7368#endif /* VBOX_WITH_VMNET */
7369
7370 case NetworkAttachmentType_Generic:
7371 // For the currently active network attachment type we have to
7372 // generate the tag, otherwise the attachment type is lost.
7373 if (fEnabled || !nic.areGenericDriverDefaultSettings())
7374 {
7375 xml::ElementNode *pelmMode = elmParent.createChild("GenericInterface");
7376 if (!nic.areGenericDriverDefaultSettings())
7377 {
7378 pelmMode->setAttribute("driver", nic.strGenericDriver);
7379 for (StringsMap::const_iterator it = nic.genericProperties.begin();
7380 it != nic.genericProperties.end();
7381 ++it)
7382 {
7383 xml::ElementNode *pelmProp = pelmMode->createChild("Property");
7384 pelmProp->setAttribute("name", it->first);
7385 pelmProp->setAttribute("value", it->second);
7386 }
7387 }
7388 }
7389 break;
7390
7391 case NetworkAttachmentType_NATNetwork:
7392 // For the currently active network attachment type we have to
7393 // generate the tag, otherwise the attachment type is lost.
7394 if (fEnabled || !nic.strNATNetworkName.isEmpty())
7395 {
7396 xml::ElementNode *pelmMode = elmParent.createChild("NATNetwork");
7397 if (!nic.strNATNetworkName.isEmpty())
7398 pelmMode->setAttribute("name", nic.strNATNetworkName);
7399 }
7400 break;
7401
7402#ifdef VBOX_WITH_CLOUD_NET
7403 case NetworkAttachmentType_Cloud:
7404 // For the currently active network attachment type we have to
7405 // generate the tag, otherwise the attachment type is lost.
7406 if (fEnabled || !nic.strCloudNetworkName.isEmpty())
7407 {
7408 xml::ElementNode *pelmMode = elmParent.createChild("CloudNetwork");
7409 if (!nic.strCloudNetworkName.isEmpty())
7410 pelmMode->setAttribute("name", nic.strCloudNetworkName);
7411 }
7412 break;
7413#endif /* VBOX_WITH_CLOUD_NET */
7414
7415 default: /*case NetworkAttachmentType_Null:*/
7416 break;
7417 }
7418}
7419
7420/**
7421 * Creates a \<StorageControllers\> node under elmParent and then writes out the XML
7422 * keys under that. Called for both the \<Machine\> node and for snapshots.
7423 * @param elmParent
7424 * @param st
7425 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
7426 * an empty drive is always written instead. This is for the OVF export case.
7427 * This parameter is ignored unless the settings version is at least v1.9, which
7428 * is always the case when this gets called for OVF export.
7429 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
7430 * pointers to which we will append all elements that we created here that contain
7431 * UUID attributes. This allows the OVF export code to quickly replace the internal
7432 * media UUIDs with the UUIDs of the media that were exported.
7433 */
7434void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
7435 const Storage &st,
7436 bool fSkipRemovableMedia,
7437 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
7438{
7439 if (!st.llStorageControllers.size())
7440 return;
7441 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
7442
7443 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
7444 it != st.llStorageControllers.end();
7445 ++it)
7446 {
7447 const StorageController &sc = *it;
7448
7449 if ( (m->sv < SettingsVersion_v1_9)
7450 && (sc.controllerType == StorageControllerType_I82078)
7451 )
7452 // floppy controller already got written into <Hardware>/<FloppyController> in buildHardwareXML()
7453 // for pre-1.9 settings
7454 continue;
7455
7456 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
7457 com::Utf8Str name = sc.strName;
7458 if (m->sv < SettingsVersion_v1_8)
7459 {
7460 // pre-1.8 settings use shorter controller names, they are
7461 // expanded when reading the settings
7462 if (name == "IDE Controller")
7463 name = "IDE";
7464 else if (name == "SATA Controller")
7465 name = "SATA";
7466 else if (name == "SCSI Controller")
7467 name = "SCSI";
7468 }
7469 pelmController->setAttribute("name", sc.strName);
7470
7471 const char *pcszType;
7472 switch (sc.controllerType)
7473 {
7474 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
7475 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
7476 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
7477 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
7478 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
7479 case StorageControllerType_I82078: pcszType = "I82078"; break;
7480 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
7481 case StorageControllerType_USB: pcszType = "USB"; break;
7482 case StorageControllerType_NVMe: pcszType = "NVMe"; break;
7483 case StorageControllerType_VirtioSCSI: pcszType = "VirtioSCSI"; break;
7484 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
7485 }
7486 pelmController->setAttribute("type", pcszType);
7487
7488 pelmController->setAttribute("PortCount", sc.ulPortCount);
7489
7490 if (m->sv >= SettingsVersion_v1_9)
7491 if (sc.ulInstance)
7492 pelmController->setAttribute("Instance", sc.ulInstance);
7493
7494 if (m->sv >= SettingsVersion_v1_10)
7495 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
7496
7497 if (m->sv >= SettingsVersion_v1_11)
7498 pelmController->setAttribute("Bootable", sc.fBootable);
7499
7500 if (sc.controllerType == StorageControllerType_IntelAhci)
7501 {
7502 pelmController->setAttribute("IDE0MasterEmulationPort", 0);
7503 pelmController->setAttribute("IDE0SlaveEmulationPort", 1);
7504 pelmController->setAttribute("IDE1MasterEmulationPort", 2);
7505 pelmController->setAttribute("IDE1SlaveEmulationPort", 3);
7506 }
7507
7508 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
7509 it2 != sc.llAttachedDevices.end();
7510 ++it2)
7511 {
7512 const AttachedDevice &att = *it2;
7513
7514 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
7515 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
7516 // the floppy controller at the top of the loop
7517 if ( att.deviceType == DeviceType_DVD
7518 && m->sv < SettingsVersion_v1_9
7519 )
7520 continue;
7521
7522 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
7523
7524 pcszType = NULL;
7525
7526 switch (att.deviceType)
7527 {
7528 case DeviceType_HardDisk:
7529 pcszType = "HardDisk";
7530 if (att.fNonRotational)
7531 pelmDevice->setAttribute("nonrotational", att.fNonRotational);
7532 if (att.fDiscard)
7533 pelmDevice->setAttribute("discard", att.fDiscard);
7534 break;
7535
7536 case DeviceType_DVD:
7537 pcszType = "DVD";
7538 pelmDevice->setAttribute("passthrough", att.fPassThrough);
7539 if (att.fTempEject)
7540 pelmDevice->setAttribute("tempeject", att.fTempEject);
7541 break;
7542
7543 case DeviceType_Floppy:
7544 pcszType = "Floppy";
7545 break;
7546
7547 default: break; /* Shut up MSC. */
7548 }
7549
7550 pelmDevice->setAttribute("type", pcszType);
7551
7552 if (m->sv >= SettingsVersion_v1_15)
7553 pelmDevice->setAttribute("hotpluggable", att.fHotPluggable);
7554
7555 pelmDevice->setAttribute("port", att.lPort);
7556 pelmDevice->setAttribute("device", att.lDevice);
7557
7558 if (att.strBwGroup.length())
7559 pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup);
7560
7561 // attached image, if any
7562 if (!att.uuid.isZero()
7563 && att.uuid.isValid()
7564 && (att.deviceType == DeviceType_HardDisk
7565 || !fSkipRemovableMedia
7566 )
7567 )
7568 {
7569 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
7570 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
7571
7572 // if caller wants a list of UUID elements, give it to them
7573 if (pllElementsWithUuidAttributes)
7574 pllElementsWithUuidAttributes->push_back(pelmImage);
7575 }
7576 else if ( (m->sv >= SettingsVersion_v1_9)
7577 && (att.strHostDriveSrc.length())
7578 )
7579 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
7580 }
7581 }
7582}
7583
7584/**
7585 * Creates a \<Debugging\> node under elmParent and then writes out the XML
7586 * keys under that. Called for both the \<Machine\> node and for snapshots.
7587 *
7588 * @param pElmParent Pointer to the parent element.
7589 * @param pDbg Pointer to the debugging settings.
7590 */
7591void MachineConfigFile::buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg)
7592{
7593 if (m->sv < SettingsVersion_v1_13 || pDbg->areDefaultSettings())
7594 return;
7595
7596 xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");
7597 xml::ElementNode *pElmTracing = pElmDebugging->createChild("Tracing");
7598 pElmTracing->setAttribute("enabled", pDbg->fTracingEnabled);
7599 pElmTracing->setAttribute("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
7600 pElmTracing->setAttribute("config", pDbg->strTracingConfig);
7601}
7602
7603/**
7604 * Creates a \<Autostart\> node under elmParent and then writes out the XML
7605 * keys under that. Called for both the \<Machine\> node and for snapshots.
7606 *
7607 * @param pElmParent Pointer to the parent element.
7608 * @param pAutostart Pointer to the autostart settings.
7609 */
7610void MachineConfigFile::buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart)
7611{
7612 const char *pcszAutostop = NULL;
7613
7614 if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())
7615 return;
7616
7617 xml::ElementNode *pElmAutostart = pElmParent->createChild("Autostart");
7618 pElmAutostart->setAttribute("enabled", pAutostart->fAutostartEnabled);
7619 pElmAutostart->setAttribute("delay", pAutostart->uAutostartDelay);
7620
7621 switch (pAutostart->enmAutostopType)
7622 {
7623 case AutostopType_Disabled: pcszAutostop = "Disabled"; break;
7624 case AutostopType_SaveState: pcszAutostop = "SaveState"; break;
7625 case AutostopType_PowerOff: pcszAutostop = "PowerOff"; break;
7626 case AutostopType_AcpiShutdown: pcszAutostop = "AcpiShutdown"; break;
7627 default: Assert(false); pcszAutostop = "Disabled"; break;
7628 }
7629 pElmAutostart->setAttribute("autostop", pcszAutostop);
7630}
7631
7632/**
7633 * Creates a \<Groups\> node under elmParent and then writes out the XML
7634 * keys under that. Called for the \<Machine\> node only.
7635 *
7636 * @param pElmParent Pointer to the parent element.
7637 * @param pllGroups Pointer to the groups list.
7638 */
7639void MachineConfigFile::buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups)
7640{
7641 if ( m->sv < SettingsVersion_v1_13 || pllGroups->size() == 0
7642 || (pllGroups->size() == 1 && pllGroups->front() == "/"))
7643 return;
7644
7645 xml::ElementNode *pElmGroups = pElmParent->createChild("Groups");
7646 for (StringsList::const_iterator it = pllGroups->begin();
7647 it != pllGroups->end();
7648 ++it)
7649 {
7650 const Utf8Str &group = *it;
7651 xml::ElementNode *pElmGroup = pElmGroups->createChild("Group");
7652 pElmGroup->setAttribute("name", group);
7653 }
7654}
7655
7656/**
7657 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
7658 * for the root snapshot of a machine, if present; elmParent then points to the \<Snapshots\> node under the
7659 * \<Machine\> node to which \<Snapshot\> must be added. This may then recurse for child snapshots.
7660 *
7661 * @param depth
7662 * @param elmParent
7663 * @param snap
7664 */
7665void MachineConfigFile::buildSnapshotXML(uint32_t depth,
7666 xml::ElementNode &elmParent,
7667 const Snapshot &snap)
7668{
7669 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
7670 throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
7671
7672 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
7673
7674 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
7675 pelmSnapshot->setAttribute("name", snap.strName);
7676 pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(snap.timestamp));
7677
7678 if (snap.strStateFile.length())
7679 pelmSnapshot->setAttributePath("stateFile", snap.strStateFile);
7680
7681 if (snap.strDescription.length())
7682 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
7683
7684 // We only skip removable media for OVF, but OVF never includes snapshots.
7685 buildHardwareXML(*pelmSnapshot, snap.hardware, 0 /* fl */, NULL /* pllElementsWithUuidAttributes */);
7686 buildDebuggingXML(pelmSnapshot, &snap.debugging);
7687 buildAutostartXML(pelmSnapshot, &snap.autostart);
7688 // note: Groups exist only for Machine, not for Snapshot
7689
7690 if (snap.llChildSnapshots.size())
7691 {
7692 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
7693 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
7694 it != snap.llChildSnapshots.end();
7695 ++it)
7696 {
7697 const Snapshot &child = *it;
7698 buildSnapshotXML(depth + 1, *pelmChildren, child);
7699 }
7700 }
7701}
7702
7703/**
7704 * Builds the XML DOM tree for the machine config under the given XML element.
7705 *
7706 * This has been separated out from write() so it can be called from elsewhere,
7707 * such as the OVF code, to build machine XML in an existing XML tree.
7708 *
7709 * As a result, this gets called from two locations:
7710 *
7711 * -- MachineConfigFile::write();
7712 *
7713 * -- Appliance::buildXMLForOneVirtualSystem()
7714 *
7715 * In fl, the following flag bits are recognized:
7716 *
7717 * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will
7718 * be written, if present. This is not set when called from OVF because OVF
7719 * has its own variant of a media registry. This flag is ignored unless the
7720 * settings version is at least v1.11 (VirtualBox 4.0).
7721 *
7722 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
7723 * of the machine and write out \<Snapshot\> and possibly more snapshots under
7724 * that, if snapshots are present. Otherwise all snapshots are suppressed
7725 * (when called from OVF).
7726 *
7727 * -- BuildMachineXML_WriteVBoxVersionAttribute: If set, add a settingsVersion
7728 * attribute to the machine tag with the vbox settings version. This is for
7729 * the OVF export case in which we don't have the settings version set in
7730 * the root element.
7731 *
7732 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
7733 * (DVDs, floppies) are silently skipped. This is for the OVF export case
7734 * until we support copying ISO and RAW media as well. This flag is ignored
7735 * unless the settings version is at least v1.9, which is always the case
7736 * when this gets called for OVF export.
7737 *
7738 * -- BuildMachineXML_SuppressSavedState: If set, the Machine/stateFile
7739 * attribute is never set. This is also for the OVF export case because we
7740 * cannot save states with OVF.
7741 *
7742 * @param elmMachine XML \<Machine\> element to add attributes and elements to.
7743 * @param fl Flags.
7744 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
7745 * see buildStorageControllersXML() for details.
7746 */
7747void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
7748 uint32_t fl,
7749 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
7750{
7751 if (fl & BuildMachineXML_WriteVBoxVersionAttribute)
7752 {
7753 // add settings version attribute to machine element
7754 setVersionAttribute(elmMachine);
7755 LogRel(("Exporting settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
7756 }
7757
7758 elmMachine.setAttribute("uuid", uuid.toStringCurly());
7759 elmMachine.setAttribute("name", machineUserData.strName);
7760 if (machineUserData.fDirectoryIncludesUUID)
7761 elmMachine.setAttribute("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
7762 if (!machineUserData.fNameSync)
7763 elmMachine.setAttribute("nameSync", machineUserData.fNameSync);
7764 if (machineUserData.strDescription.length())
7765 elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
7766 elmMachine.setAttribute("OSType", machineUserData.strOsType);
7767 if ( strStateFile.length()
7768 && !(fl & BuildMachineXML_SuppressSavedState)
7769 )
7770 elmMachine.setAttributePath("stateFile", strStateFile);
7771
7772 if ((fl & BuildMachineXML_IncludeSnapshots)
7773 && !uuidCurrentSnapshot.isZero()
7774 && uuidCurrentSnapshot.isValid())
7775 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
7776
7777 if (machineUserData.strSnapshotFolder.length())
7778 elmMachine.setAttributePath("snapshotFolder", machineUserData.strSnapshotFolder);
7779 if (!fCurrentStateModified)
7780 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
7781 elmMachine.setAttribute("lastStateChange", stringifyTimestamp(timeLastStateChange));
7782 if (fAborted)
7783 elmMachine.setAttribute("aborted", fAborted);
7784
7785 switch (machineUserData.enmVMPriority)
7786 {
7787 case VMProcPriority_Flat:
7788 elmMachine.setAttribute("processPriority", "Flat");
7789 break;
7790 case VMProcPriority_Low:
7791 elmMachine.setAttribute("processPriority", "Low");
7792 break;
7793 case VMProcPriority_Normal:
7794 elmMachine.setAttribute("processPriority", "Normal");
7795 break;
7796 case VMProcPriority_High:
7797 elmMachine.setAttribute("processPriority", "High");
7798 break;
7799 default:
7800 break;
7801 }
7802 // Please keep the icon last so that one doesn't have to check if there
7803 // is anything in the line after this very long attribute in the XML.
7804 if (machineUserData.ovIcon.size())
7805 {
7806 Utf8Str strIcon;
7807 toBase64(strIcon, machineUserData.ovIcon);
7808 elmMachine.setAttribute("icon", strIcon);
7809 }
7810 if ( m->sv >= SettingsVersion_v1_9
7811 && ( machineUserData.fTeleporterEnabled
7812 || machineUserData.uTeleporterPort
7813 || !machineUserData.strTeleporterAddress.isEmpty()
7814 || !machineUserData.strTeleporterPassword.isEmpty()
7815 )
7816 )
7817 {
7818 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
7819 pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled);
7820 pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort);
7821 pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress);
7822 pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword);
7823 }
7824
7825 if ( (fl & BuildMachineXML_MediaRegistry)
7826 && (m->sv >= SettingsVersion_v1_11)
7827 )
7828 buildMediaRegistry(elmMachine, mediaRegistry);
7829
7830 buildExtraData(elmMachine, mapExtraDataItems);
7831
7832 if ( (fl & BuildMachineXML_IncludeSnapshots)
7833 && llFirstSnapshot.size())
7834 buildSnapshotXML(1, elmMachine, llFirstSnapshot.front());
7835
7836 buildHardwareXML(elmMachine, hardwareMachine, fl, pllElementsWithUuidAttributes);
7837 buildDebuggingXML(&elmMachine, &debugging);
7838 buildAutostartXML(&elmMachine, &autostart);
7839 buildGroupsXML(&elmMachine, &machineUserData.llGroups);
7840}
7841
7842/**
7843 * Returns true only if the given AudioDriverType is supported on
7844 * the current host platform. For example, this would return false
7845 * for AudioDriverType_DirectSound when compiled on a Linux host.
7846 * @param drv AudioDriverType_* enum to test.
7847 * @return true only if the current host supports that driver.
7848 */
7849/*static*/
7850bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
7851{
7852 switch (drv)
7853 {
7854 case AudioDriverType_Null:
7855#ifdef RT_OS_WINDOWS
7856 case AudioDriverType_DirectSound:
7857#endif
7858#ifdef VBOX_WITH_AUDIO_OSS
7859 case AudioDriverType_OSS:
7860#endif
7861#ifdef VBOX_WITH_AUDIO_ALSA
7862 case AudioDriverType_ALSA:
7863#endif
7864#ifdef VBOX_WITH_AUDIO_PULSE
7865 case AudioDriverType_Pulse:
7866#endif
7867#ifdef RT_OS_DARWIN
7868 case AudioDriverType_CoreAudio:
7869#endif
7870#ifdef RT_OS_OS2
7871 case AudioDriverType_MMPM:
7872#endif
7873 return true;
7874 default: break; /* Shut up MSC. */
7875 }
7876
7877 return false;
7878}
7879
7880/**
7881 * Returns the AudioDriverType_* which should be used by default on this
7882 * host platform. On Linux, this will check at runtime whether PulseAudio
7883 * or ALSA are actually supported on the first call.
7884 *
7885 * @return Default audio driver type for this host platform.
7886 */
7887/*static*/
7888AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
7889{
7890#if defined(RT_OS_WINDOWS)
7891 return AudioDriverType_DirectSound;
7892
7893#elif defined(RT_OS_LINUX)
7894 /* On Linux, we need to check at runtime what's actually supported. */
7895 static RTCLockMtx s_mtx;
7896 static AudioDriverType_T s_enmLinuxDriver = AudioDriverType_Null;
7897 RTCLock lock(s_mtx);
7898 if (s_enmLinuxDriver == AudioDriverType_Null)
7899 {
7900# ifdef VBOX_WITH_AUDIO_PULSE
7901 /* Check for the pulse library & that the pulse audio daemon is running. */
7902 if (RTProcIsRunningByName("pulseaudio") &&
7903 RTLdrIsLoadable("libpulse.so.0"))
7904 s_enmLinuxDriver = AudioDriverType_Pulse;
7905 else
7906# endif /* VBOX_WITH_AUDIO_PULSE */
7907# ifdef VBOX_WITH_AUDIO_ALSA
7908 /* Check if we can load the ALSA library */
7909 if (RTLdrIsLoadable("libasound.so.2"))
7910 s_enmLinuxDriver = AudioDriverType_ALSA;
7911 else
7912# endif /* VBOX_WITH_AUDIO_ALSA */
7913 s_enmLinuxDriver = AudioDriverType_OSS;
7914 }
7915 return s_enmLinuxDriver;
7916
7917#elif defined(RT_OS_DARWIN)
7918 return AudioDriverType_CoreAudio;
7919
7920#elif defined(RT_OS_OS2)
7921 return AudioDriverType_MMPM;
7922
7923#else /* All other platforms. */
7924# ifdef VBOX_WITH_AUDIO_OSS
7925 return AudioDriverType_OSS;
7926# else
7927 /* Return NULL driver as a fallback if nothing of the above is available. */
7928 return AudioDriverType_Null;
7929# endif
7930#endif
7931}
7932
7933/**
7934 * Called from write() before calling ConfigFileBase::createStubDocument().
7935 * This adjusts the settings version in m->sv if incompatible settings require
7936 * a settings bump, whereas otherwise we try to preserve the settings version
7937 * to avoid breaking compatibility with older versions.
7938 *
7939 * We do the checks in here in reverse order: newest first, oldest last, so
7940 * that we avoid unnecessary checks since some of these are expensive.
7941 */
7942void MachineConfigFile::bumpSettingsVersionIfNeeded()
7943{
7944 if (m->sv < SettingsVersion_v1_19)
7945 {
7946 // VirtualBox 7.0 adds iommu device.
7947 if (hardwareMachine.iommuType != IommuType_None)
7948 {
7949 m->sv = SettingsVersion_v1_19;
7950 return;
7951 }
7952
7953 // VirtualBox 7.0 adds a Trusted Platform Module.
7954 if ( hardwareMachine.tpmSettings.tpmType != TpmType_None
7955 || hardwareMachine.tpmSettings.strLocation.isNotEmpty())
7956 {
7957 m->sv = SettingsVersion_v1_19;
7958 return;
7959 }
7960
7961 NetworkAdaptersList::const_iterator netit;
7962 for (netit = hardwareMachine.llNetworkAdapters.begin();
7963 netit != hardwareMachine.llNetworkAdapters.end();
7964 ++netit)
7965 {
7966 // VirtualBox 7.0 adds a flag if NAT can reach localhost.
7967 if ( netit->fEnabled
7968 && netit->mode == NetworkAttachmentType_NAT
7969 && !netit->nat.fLocalhostReachable)
7970 {
7971 m->sv = SettingsVersion_v1_19;
7972 break;
7973 }
7974
7975#ifdef VBOX_WITH_VMNET
7976 // VirtualBox 7.0 adds a host-only network attachment.
7977 if (netit->mode == NetworkAttachmentType_HostOnlyNetwork)
7978 {
7979 m->sv = SettingsVersion_v1_19;
7980 break;
7981 }
7982#endif /* VBOX_WITH_VMNET */
7983 }
7984 }
7985
7986 if (m->sv < SettingsVersion_v1_18)
7987 {
7988 if (!hardwareMachine.nvramSettings.strNvramPath.isEmpty())
7989 {
7990 m->sv = SettingsVersion_v1_18;
7991 return;
7992 }
7993
7994 // VirtualBox 6.1 adds AMD-V virtualized VMSAVE/VMLOAD setting.
7995 if (hardwareMachine.fVirtVmsaveVmload == false)
7996 {
7997 m->sv = SettingsVersion_v1_18;
7998 return;
7999 }
8000
8001 // VirtualBox 6.1 adds a virtio-scsi storage controller.
8002 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
8003 it != hardwareMachine.storage.llStorageControllers.end();
8004 ++it)
8005 {
8006 const StorageController &sctl = *it;
8007
8008 if (sctl.controllerType == StorageControllerType_VirtioSCSI)
8009 {
8010 m->sv = SettingsVersion_v1_18;
8011 return;
8012 }
8013 }
8014
8015#ifdef VBOX_WITH_CLOUD_NET
8016 NetworkAdaptersList::const_iterator netit;
8017 for (netit = hardwareMachine.llNetworkAdapters.begin();
8018 netit != hardwareMachine.llNetworkAdapters.end();
8019 ++netit)
8020 {
8021 // VirtualBox 6.1 adds support for cloud networks.
8022 if ( netit->fEnabled
8023 && netit->mode == NetworkAttachmentType_Cloud)
8024 {
8025 m->sv = SettingsVersion_v1_18;
8026 break;
8027 }
8028
8029 }
8030#endif /* VBOX_WITH_CLOUD_NET */
8031 }
8032
8033 if (m->sv < SettingsVersion_v1_17)
8034 {
8035 if (machineUserData.enmVMPriority != VMProcPriority_Default)
8036 {
8037 m->sv = SettingsVersion_v1_17;
8038 return;
8039 }
8040
8041 // VirtualBox 6.0 adds nested hardware virtualization, using native API (NEM).
8042 if ( hardwareMachine.fNestedHWVirt
8043 || hardwareMachine.fUseNativeApi)
8044 {
8045 m->sv = SettingsVersion_v1_17;
8046 return;
8047 }
8048 if (hardwareMachine.llSharedFolders.size())
8049 for (SharedFoldersList::const_iterator it = hardwareMachine.llSharedFolders.begin();
8050 it != hardwareMachine.llSharedFolders.end();
8051 ++it)
8052 if (it->strAutoMountPoint.isNotEmpty())
8053 {
8054 m->sv = SettingsVersion_v1_17;
8055 return;
8056 }
8057
8058 /*
8059 * Check if any serial port uses a non 16550A serial port.
8060 */
8061 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
8062 it != hardwareMachine.llSerialPorts.end();
8063 ++it)
8064 {
8065 const SerialPort &port = *it;
8066 if (port.uartType != UartType_U16550A)
8067 {
8068 m->sv = SettingsVersion_v1_17;
8069 return;
8070 }
8071 }
8072 }
8073
8074 if (m->sv < SettingsVersion_v1_16)
8075 {
8076 // VirtualBox 5.1 adds a NVMe storage controller, paravirt debug
8077 // options, cpu profile, APIC settings (CPU capability and BIOS).
8078
8079 if ( hardwareMachine.strParavirtDebug.isNotEmpty()
8080 || (!hardwareMachine.strCpuProfile.equals("host") && hardwareMachine.strCpuProfile.isNotEmpty())
8081 || hardwareMachine.biosSettings.apicMode != APICMode_APIC
8082 || !hardwareMachine.fAPIC
8083 || hardwareMachine.fX2APIC
8084 || hardwareMachine.fIBPBOnVMExit
8085 || hardwareMachine.fIBPBOnVMEntry
8086 || hardwareMachine.fSpecCtrl
8087 || hardwareMachine.fSpecCtrlByHost
8088 || !hardwareMachine.fL1DFlushOnSched
8089 || hardwareMachine.fL1DFlushOnVMEntry
8090 || !hardwareMachine.fMDSClearOnSched
8091 || hardwareMachine.fMDSClearOnVMEntry)
8092 {
8093 m->sv = SettingsVersion_v1_16;
8094 return;
8095 }
8096
8097 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
8098 it != hardwareMachine.storage.llStorageControllers.end();
8099 ++it)
8100 {
8101 const StorageController &sctl = *it;
8102
8103 if (sctl.controllerType == StorageControllerType_NVMe)
8104 {
8105 m->sv = SettingsVersion_v1_16;
8106 return;
8107 }
8108 }
8109
8110 for (CpuIdLeafsList::const_iterator it = hardwareMachine.llCpuIdLeafs.begin();
8111 it != hardwareMachine.llCpuIdLeafs.end();
8112 ++it)
8113 if (it->idxSub != 0)
8114 {
8115 m->sv = SettingsVersion_v1_16;
8116 return;
8117 }
8118 }
8119
8120 if (m->sv < SettingsVersion_v1_15)
8121 {
8122 // VirtualBox 5.0 adds paravirt providers, explicit AHCI port hotplug
8123 // setting, USB storage controller, xHCI, serial port TCP backend
8124 // and VM process priority.
8125
8126 /*
8127 * Check simple configuration bits first, loopy stuff afterwards.
8128 */
8129 if ( hardwareMachine.paravirtProvider != ParavirtProvider_Legacy
8130 || hardwareMachine.uCpuIdPortabilityLevel != 0)
8131 {
8132 m->sv = SettingsVersion_v1_15;
8133 return;
8134 }
8135
8136 /*
8137 * Check whether the hotpluggable flag of all storage devices differs
8138 * from the default for old settings.
8139 * AHCI ports are hotpluggable by default every other device is not.
8140 * Also check if there are USB storage controllers.
8141 */
8142 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
8143 it != hardwareMachine.storage.llStorageControllers.end();
8144 ++it)
8145 {
8146 const StorageController &sctl = *it;
8147
8148 if (sctl.controllerType == StorageControllerType_USB)
8149 {
8150 m->sv = SettingsVersion_v1_15;
8151 return;
8152 }
8153
8154 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
8155 it2 != sctl.llAttachedDevices.end();
8156 ++it2)
8157 {
8158 const AttachedDevice &att = *it2;
8159
8160 if ( ( att.fHotPluggable
8161 && sctl.controllerType != StorageControllerType_IntelAhci)
8162 || ( !att.fHotPluggable
8163 && sctl.controllerType == StorageControllerType_IntelAhci))
8164 {
8165 m->sv = SettingsVersion_v1_15;
8166 return;
8167 }
8168 }
8169 }
8170
8171 /*
8172 * Check if there is an xHCI (USB3) USB controller.
8173 */
8174 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
8175 it != hardwareMachine.usbSettings.llUSBControllers.end();
8176 ++it)
8177 {
8178 const USBController &ctrl = *it;
8179 if (ctrl.enmType == USBControllerType_XHCI)
8180 {
8181 m->sv = SettingsVersion_v1_15;
8182 return;
8183 }
8184 }
8185
8186 /*
8187 * Check if any serial port uses the TCP backend.
8188 */
8189 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
8190 it != hardwareMachine.llSerialPorts.end();
8191 ++it)
8192 {
8193 const SerialPort &port = *it;
8194 if (port.portMode == PortMode_TCP)
8195 {
8196 m->sv = SettingsVersion_v1_15;
8197 return;
8198 }
8199 }
8200 }
8201
8202 if (m->sv < SettingsVersion_v1_14)
8203 {
8204 // VirtualBox 4.3 adds default frontend setting, graphics controller
8205 // setting, explicit long mode setting, (video) capturing and NAT networking.
8206 if ( !hardwareMachine.strDefaultFrontend.isEmpty()
8207 || hardwareMachine.graphicsAdapter.graphicsControllerType != GraphicsControllerType_VBoxVGA
8208 || hardwareMachine.enmLongMode != Hardware::LongMode_Legacy
8209 || machineUserData.ovIcon.size() > 0
8210 || hardwareMachine.recordingSettings.fEnabled)
8211 {
8212 m->sv = SettingsVersion_v1_14;
8213 return;
8214 }
8215 NetworkAdaptersList::const_iterator netit;
8216 for (netit = hardwareMachine.llNetworkAdapters.begin();
8217 netit != hardwareMachine.llNetworkAdapters.end();
8218 ++netit)
8219 {
8220 if (netit->mode == NetworkAttachmentType_NATNetwork)
8221 {
8222 m->sv = SettingsVersion_v1_14;
8223 break;
8224 }
8225 }
8226 }
8227
8228 if (m->sv < SettingsVersion_v1_14)
8229 {
8230 unsigned cOhciCtrls = 0;
8231 unsigned cEhciCtrls = 0;
8232 bool fNonStdName = false;
8233
8234 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
8235 it != hardwareMachine.usbSettings.llUSBControllers.end();
8236 ++it)
8237 {
8238 const USBController &ctrl = *it;
8239
8240 switch (ctrl.enmType)
8241 {
8242 case USBControllerType_OHCI:
8243 cOhciCtrls++;
8244 if (ctrl.strName != "OHCI")
8245 fNonStdName = true;
8246 break;
8247 case USBControllerType_EHCI:
8248 cEhciCtrls++;
8249 if (ctrl.strName != "EHCI")
8250 fNonStdName = true;
8251 break;
8252 default:
8253 /* Anything unknown forces a bump. */
8254 fNonStdName = true;
8255 }
8256
8257 /* Skip checking other controllers if the settings bump is necessary. */
8258 if (cOhciCtrls > 1 || cEhciCtrls > 1 || fNonStdName)
8259 {
8260 m->sv = SettingsVersion_v1_14;
8261 break;
8262 }
8263 }
8264 }
8265
8266 if (m->sv < SettingsVersion_v1_13)
8267 {
8268 // VirtualBox 4.2 adds tracing, autostart, UUID in directory and groups.
8269 if ( !debugging.areDefaultSettings()
8270 || !autostart.areDefaultSettings()
8271 || machineUserData.fDirectoryIncludesUUID
8272 || machineUserData.llGroups.size() > 1
8273 || machineUserData.llGroups.front() != "/")
8274 m->sv = SettingsVersion_v1_13;
8275 }
8276
8277 if (m->sv < SettingsVersion_v1_13)
8278 {
8279 // VirtualBox 4.2 changes the units for bandwidth group limits.
8280 for (BandwidthGroupList::const_iterator it = hardwareMachine.ioSettings.llBandwidthGroups.begin();
8281 it != hardwareMachine.ioSettings.llBandwidthGroups.end();
8282 ++it)
8283 {
8284 const BandwidthGroup &gr = *it;
8285 if (gr.cMaxBytesPerSec % _1M)
8286 {
8287 // Bump version if a limit cannot be expressed in megabytes
8288 m->sv = SettingsVersion_v1_13;
8289 break;
8290 }
8291 }
8292 }
8293
8294 if (m->sv < SettingsVersion_v1_12)
8295 {
8296 // VirtualBox 4.1 adds PCI passthrough and emulated USB Smart Card reader
8297 if ( hardwareMachine.pciAttachments.size()
8298 || hardwareMachine.fEmulatedUSBCardReader)
8299 m->sv = SettingsVersion_v1_12;
8300 }
8301
8302 if (m->sv < SettingsVersion_v1_12)
8303 {
8304 // VirtualBox 4.1 adds a promiscuous mode policy to the network
8305 // adapters and a generic network driver transport.
8306 NetworkAdaptersList::const_iterator netit;
8307 for (netit = hardwareMachine.llNetworkAdapters.begin();
8308 netit != hardwareMachine.llNetworkAdapters.end();
8309 ++netit)
8310 {
8311 if ( netit->enmPromiscModePolicy != NetworkAdapterPromiscModePolicy_Deny
8312 || netit->mode == NetworkAttachmentType_Generic
8313 || !netit->areGenericDriverDefaultSettings()
8314 )
8315 {
8316 m->sv = SettingsVersion_v1_12;
8317 break;
8318 }
8319 }
8320 }
8321
8322 if (m->sv < SettingsVersion_v1_11)
8323 {
8324 // VirtualBox 4.0 adds HD audio, CPU priorities, ~fault tolerance~,
8325 // per-machine media registries, VRDE, JRockitVE, bandwidth groups,
8326 // ICH9 chipset
8327 if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
8328 || hardwareMachine.ulCpuExecutionCap != 100
8329 || mediaRegistry.llHardDisks.size()
8330 || mediaRegistry.llDvdImages.size()
8331 || mediaRegistry.llFloppyImages.size()
8332 || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty()
8333 || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty()
8334 || machineUserData.strOsType == "JRockitVE"
8335 || hardwareMachine.ioSettings.llBandwidthGroups.size()
8336 || hardwareMachine.chipsetType == ChipsetType_ICH9
8337 )
8338 m->sv = SettingsVersion_v1_11;
8339 }
8340
8341 if (m->sv < SettingsVersion_v1_10)
8342 {
8343 /* If the properties contain elements other than "TCP/Ports" and "TCP/Address",
8344 * then increase the version to at least VBox 3.2, which can have video channel properties.
8345 */
8346 unsigned cOldProperties = 0;
8347
8348 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
8349 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
8350 cOldProperties++;
8351 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
8352 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
8353 cOldProperties++;
8354
8355 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
8356 m->sv = SettingsVersion_v1_10;
8357 }
8358
8359 if (m->sv < SettingsVersion_v1_11)
8360 {
8361 /* If the properties contain elements other than "TCP/Ports", "TCP/Address",
8362 * "VideoChannel/Enabled" and "VideoChannel/Quality" then increase the version to VBox 4.0.
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 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
8373 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
8374 cOldProperties++;
8375 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Quality");
8376 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
8377 cOldProperties++;
8378
8379 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
8380 m->sv = SettingsVersion_v1_11;
8381 }
8382
8383 // settings version 1.9 is required if there is not exactly one DVD
8384 // or more than one floppy drive present or the DVD is not at the secondary
8385 // master; this check is a bit more complicated
8386 //
8387 // settings version 1.10 is required if the host cache should be disabled
8388 //
8389 // settings version 1.11 is required for bandwidth limits and if more than
8390 // one controller of each type is present.
8391 if (m->sv < SettingsVersion_v1_11)
8392 {
8393 // count attached DVDs and floppies (only if < v1.9)
8394 size_t cDVDs = 0;
8395 size_t cFloppies = 0;
8396
8397 // count storage controllers (if < v1.11)
8398 size_t cSata = 0;
8399 size_t cScsiLsi = 0;
8400 size_t cScsiBuslogic = 0;
8401 size_t cSas = 0;
8402 size_t cIde = 0;
8403 size_t cFloppy = 0;
8404
8405 // need to run thru all the storage controllers and attached devices to figure this out
8406 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
8407 it != hardwareMachine.storage.llStorageControllers.end();
8408 ++it)
8409 {
8410 const StorageController &sctl = *it;
8411
8412 // count storage controllers of each type; 1.11 is required if more than one
8413 // controller of one type is present
8414 switch (sctl.storageBus)
8415 {
8416 case StorageBus_IDE:
8417 cIde++;
8418 break;
8419 case StorageBus_SATA:
8420 cSata++;
8421 break;
8422 case StorageBus_SAS:
8423 cSas++;
8424 break;
8425 case StorageBus_SCSI:
8426 if (sctl.controllerType == StorageControllerType_LsiLogic)
8427 cScsiLsi++;
8428 else
8429 cScsiBuslogic++;
8430 break;
8431 case StorageBus_Floppy:
8432 cFloppy++;
8433 break;
8434 default:
8435 // Do nothing
8436 break;
8437 }
8438
8439 if ( cSata > 1
8440 || cScsiLsi > 1
8441 || cScsiBuslogic > 1
8442 || cSas > 1
8443 || cIde > 1
8444 || cFloppy > 1)
8445 {
8446 m->sv = SettingsVersion_v1_11;
8447 break; // abort the loop -- we will not raise the version further
8448 }
8449
8450 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
8451 it2 != sctl.llAttachedDevices.end();
8452 ++it2)
8453 {
8454 const AttachedDevice &att = *it2;
8455
8456 // Bandwidth limitations are new in VirtualBox 4.0 (1.11)
8457 if (m->sv < SettingsVersion_v1_11)
8458 {
8459 if (att.strBwGroup.length() != 0)
8460 {
8461 m->sv = SettingsVersion_v1_11;
8462 break; // abort the loop -- we will not raise the version further
8463 }
8464 }
8465
8466 // disabling the host IO cache requires settings version 1.10
8467 if ( (m->sv < SettingsVersion_v1_10)
8468 && (!sctl.fUseHostIOCache)
8469 )
8470 m->sv = SettingsVersion_v1_10;
8471
8472 // we can only write the StorageController/@Instance attribute with v1.9
8473 if ( (m->sv < SettingsVersion_v1_9)
8474 && (sctl.ulInstance != 0)
8475 )
8476 m->sv = SettingsVersion_v1_9;
8477
8478 if (m->sv < SettingsVersion_v1_9)
8479 {
8480 if (att.deviceType == DeviceType_DVD)
8481 {
8482 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
8483 || (att.lPort != 1) // DVDs not at secondary master?
8484 || (att.lDevice != 0)
8485 )
8486 m->sv = SettingsVersion_v1_9;
8487
8488 ++cDVDs;
8489 }
8490 else if (att.deviceType == DeviceType_Floppy)
8491 ++cFloppies;
8492 }
8493 }
8494
8495 if (m->sv >= SettingsVersion_v1_11)
8496 break; // abort the loop -- we will not raise the version further
8497 }
8498
8499 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
8500 // so any deviation from that will require settings version 1.9
8501 if ( (m->sv < SettingsVersion_v1_9)
8502 && ( (cDVDs != 1)
8503 || (cFloppies > 1)
8504 )
8505 )
8506 m->sv = SettingsVersion_v1_9;
8507 }
8508
8509 // VirtualBox 3.2: Check for non default I/O settings
8510 if (m->sv < SettingsVersion_v1_10)
8511 {
8512 if ( (hardwareMachine.ioSettings.fIOCacheEnabled != true)
8513 || (hardwareMachine.ioSettings.ulIOCacheSize != 5)
8514 // and page fusion
8515 || (hardwareMachine.fPageFusionEnabled)
8516 // and CPU hotplug, RTC timezone control, HID type and HPET
8517 || machineUserData.fRTCUseUTC
8518 || hardwareMachine.fCpuHotPlug
8519 || hardwareMachine.pointingHIDType != PointingHIDType_PS2Mouse
8520 || hardwareMachine.keyboardHIDType != KeyboardHIDType_PS2Keyboard
8521 || hardwareMachine.fHPETEnabled
8522 )
8523 m->sv = SettingsVersion_v1_10;
8524 }
8525
8526 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main
8527 // VirtualBox 4.0 adds network bandwitdth
8528 if (m->sv < SettingsVersion_v1_11)
8529 {
8530 NetworkAdaptersList::const_iterator netit;
8531 for (netit = hardwareMachine.llNetworkAdapters.begin();
8532 netit != hardwareMachine.llNetworkAdapters.end();
8533 ++netit)
8534 {
8535 if ( (m->sv < SettingsVersion_v1_12)
8536 && (netit->strBandwidthGroup.isNotEmpty())
8537 )
8538 {
8539 /* New in VirtualBox 4.1 */
8540 m->sv = SettingsVersion_v1_12;
8541 break;
8542 }
8543 else if ( (m->sv < SettingsVersion_v1_10)
8544 && (netit->fEnabled)
8545 && (netit->mode == NetworkAttachmentType_NAT)
8546 && ( netit->nat.u32Mtu != 0
8547 || netit->nat.u32SockRcv != 0
8548 || netit->nat.u32SockSnd != 0
8549 || netit->nat.u32TcpRcv != 0
8550 || netit->nat.u32TcpSnd != 0
8551 || !netit->nat.fDNSPassDomain
8552 || netit->nat.fDNSProxy
8553 || netit->nat.fDNSUseHostResolver
8554 || netit->nat.fAliasLog
8555 || netit->nat.fAliasProxyOnly
8556 || netit->nat.fAliasUseSamePorts
8557 || netit->nat.strTFTPPrefix.length()
8558 || netit->nat.strTFTPBootFile.length()
8559 || netit->nat.strTFTPNextServer.length()
8560 || netit->nat.mapRules.size()
8561 )
8562 )
8563 {
8564 m->sv = SettingsVersion_v1_10;
8565 // no break because we still might need v1.11 above
8566 }
8567 else if ( (m->sv < SettingsVersion_v1_10)
8568 && (netit->fEnabled)
8569 && (netit->ulBootPriority != 0)
8570 )
8571 {
8572 m->sv = SettingsVersion_v1_10;
8573 // no break because we still might need v1.11 above
8574 }
8575 }
8576 }
8577
8578 // all the following require settings version 1.9
8579 if ( (m->sv < SettingsVersion_v1_9)
8580 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
8581 || machineUserData.fTeleporterEnabled
8582 || machineUserData.uTeleporterPort
8583 || !machineUserData.strTeleporterAddress.isEmpty()
8584 || !machineUserData.strTeleporterPassword.isEmpty()
8585 || (!hardwareMachine.uuid.isZero() && hardwareMachine.uuid.isValid())
8586 )
8587 )
8588 m->sv = SettingsVersion_v1_9;
8589
8590 // "accelerate 2d video" requires settings version 1.8
8591 if ( (m->sv < SettingsVersion_v1_8)
8592 && (hardwareMachine.graphicsAdapter.fAccelerate2DVideo)
8593 )
8594 m->sv = SettingsVersion_v1_8;
8595
8596 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
8597 if ( m->sv < SettingsVersion_v1_4
8598 && hardwareMachine.strVersion != "1"
8599 )
8600 m->sv = SettingsVersion_v1_4;
8601}
8602
8603/**
8604 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
8605 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
8606 * in particular if the file cannot be written.
8607 */
8608void MachineConfigFile::write(const com::Utf8Str &strFilename)
8609{
8610 try
8611 {
8612 // createStubDocument() sets the settings version to at least 1.7; however,
8613 // we might need to enfore a later settings version if incompatible settings
8614 // are present:
8615 bumpSettingsVersionIfNeeded();
8616
8617 m->strFilename = strFilename;
8618 specialBackupIfFirstBump();
8619 createStubDocument();
8620
8621 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
8622 buildMachineXML(*pelmMachine,
8623 MachineConfigFile::BuildMachineXML_IncludeSnapshots
8624 | MachineConfigFile::BuildMachineXML_MediaRegistry,
8625 // but not BuildMachineXML_WriteVBoxVersionAttribute
8626 NULL); /* pllElementsWithUuidAttributes */
8627
8628 // now go write the XML
8629 xml::XmlFileWriter writer(*m->pDoc);
8630 writer.write(m->strFilename.c_str(), true /*fSafe*/);
8631
8632 m->fFileExists = true;
8633 clearDocument();
8634 }
8635 catch (...)
8636 {
8637 clearDocument();
8638 throw;
8639 }
8640}
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