VirtualBox

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

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

Main/Settings: Remove the unnecessary additional settings version. bugref:9932

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