VirtualBox

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

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

Main/Update check: Settings fixes, added @todos. ​​bugref:7983

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