VirtualBox

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

Last change on this file since 107770 was 107714, checked in by vboxsync, 4 months ago

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