VirtualBox

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

Last change on this file since 101024 was 99723, checked in by vboxsync, 19 months ago

Audio/Settings: Also use the PulseAudio backend when we find pipewire-pulse running (instead of falling back to ALSA), which acts as a PulseAudio-compatible daemon for Pipewire-enabled applications. Thanks to Aroun! ticketref:21575

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

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