VirtualBox

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

Last change on this file since 107401 was 107267, checked in by vboxsync, 6 weeks ago

Main/API,UI: bugref:10810 Based on Brent's patch, added UsbNet support into Main API and UI

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

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