VirtualBox

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

Last change on this file since 107664 was 107610, checked in by vboxsync, 4 months ago

Main/xml/Settings.cpp: Missing member initializer, bugref:3409

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

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