VirtualBox

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

Last change on this file since 101381 was 101381, checked in by vboxsync, 8 months ago

Main: More guest OS id marking. bugref:10384

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

© 2023 Oracle
ContactPrivacy policyTerms of Use