VirtualBox

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

Last change on this file since 102455 was 102455, checked in by vboxsync, 5 months ago

Main: Removed some more BUGBUG entries and documented empty / non-implemented code areas where needed. bugref:10384

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

© 2023 Oracle
ContactPrivacy policyTerms of Use