VirtualBox

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

Last change on this file since 109067 was 108973, checked in by vboxsync, 3 weeks ago

Main: bugref:10877 Add GIC ITS setting to the VM configuration and API.

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