VirtualBox

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

Last change on this file since 101418 was 101418, checked in by vboxsync, 7 months ago

Main: Added VM setting (AutoSerialNumGen) for enabling automatic VM serial number generation jiraref:VBP-238

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

© 2023 Oracle
ContactPrivacy policyTerms of Use