VirtualBox

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

Last change on this file since 88192 was 87241, checked in by vboxsync, 4 years ago

AMD IOMMU: bugref:9654 Main/API: AMD IOMMU support.

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

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