VirtualBox

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

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

doc/manual,include/VBox,Frontends/{VBoxManage,VirtualBox/src},Main/{include,SharedFolder,Console,Machine,VirtualBox,VirtualBox.xidl}: Added global shared folders and adjusted fetching and handling of folders between shared folder types bugref:3544

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

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