VirtualBox

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

Last change on this file since 76598 was 76598, checked in by vboxsync, 6 years ago

Main: GCC precompiled headers hacking.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 314.2 KB
Line 
1/* $Id: Settings.cpp 76598 2019-01-02 00:59:08Z 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-2019 Oracle Corporation
62 *
63 * This file is part of VirtualBox Open Source Edition (OSE), as
64 * available from http://www.virtualbox.org. This file is free software;
65 * you can redistribute it and/or modify it under the terms of the GNU
66 * General Public License (GPL) as published by the Free Software
67 * Foundation, in version 2 as it comes in the "COPYING" file of the
68 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
69 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
70 */
71
72#define LOG_GROUP LOG_GROUP_MAIN
73#include "VBox/com/string.h"
74#include "VBox/settings.h"
75#include <iprt/base64.h>
76#include <iprt/cpp/lock.h>
77#include <iprt/cpp/xml.h>
78#include <iprt/ctype.h>
79#include <iprt/err.h>
80#include <iprt/file.h>
81#include <iprt/ldr.h>
82#include <iprt/process.h>
83#include <iprt/stream.h>
84#include <iprt/uri.h>
85
86// generated header
87#include "SchemaDefs.h"
88
89#include "HashedPw.h"
90#include "LoggingNew.h"
91
92using namespace com;
93using namespace settings;
94
95////////////////////////////////////////////////////////////////////////////////
96//
97// Defines
98//
99////////////////////////////////////////////////////////////////////////////////
100
101/** VirtualBox XML settings namespace */
102#define VBOX_XML_NAMESPACE "http://www.virtualbox.org/"
103
104/** VirtualBox XML schema location (relative URI) */
105#define VBOX_XML_SCHEMA "VirtualBox-settings.xsd"
106
107/** VirtualBox XML settings version number substring ("x.y") */
108#define VBOX_XML_VERSION "1.12"
109
110/** VirtualBox OVF settings import default version number substring ("x.y").
111 *
112 * Think twice before changing this, as all VirtualBox versions before 5.1
113 * wrote the settings version when exporting, but totally ignored it on
114 * importing (while it should have been a mandatory attribute), so 3rd party
115 * software out there creates OVF files with the VirtualBox specific settings
116 * but lacking the version attribute. This shouldn't happen any more, but
117 * breaking existing OVF files isn't nice. */
118#define VBOX_XML_IMPORT_VERSION "1.15"
119
120/** VirtualBox XML settings version platform substring */
121#if defined (RT_OS_DARWIN)
122# define VBOX_XML_PLATFORM "macosx"
123#elif defined (RT_OS_FREEBSD)
124# define VBOX_XML_PLATFORM "freebsd"
125#elif defined (RT_OS_LINUX)
126# define VBOX_XML_PLATFORM "linux"
127#elif defined (RT_OS_NETBSD)
128# define VBOX_XML_PLATFORM "netbsd"
129#elif defined (RT_OS_OPENBSD)
130# define VBOX_XML_PLATFORM "openbsd"
131#elif defined (RT_OS_OS2)
132# define VBOX_XML_PLATFORM "os2"
133#elif defined (RT_OS_SOLARIS)
134# define VBOX_XML_PLATFORM "solaris"
135#elif defined (RT_OS_WINDOWS)
136# define VBOX_XML_PLATFORM "windows"
137#else
138# error Unsupported platform!
139#endif
140
141/** VirtualBox XML settings full version string ("x.y-platform") */
142#define VBOX_XML_VERSION_FULL VBOX_XML_VERSION "-" VBOX_XML_PLATFORM
143
144/** VirtualBox OVF import default settings full version string ("x.y-platform") */
145#define VBOX_XML_IMPORT_VERSION_FULL VBOX_XML_IMPORT_VERSION "-" VBOX_XML_PLATFORM
146
147////////////////////////////////////////////////////////////////////////////////
148//
149// Internal data
150//
151////////////////////////////////////////////////////////////////////////////////
152
153/**
154 * Opaque data structore for ConfigFileBase (only declared
155 * in header, defined only here).
156 */
157
158struct ConfigFileBase::Data
159{
160 Data()
161 : pDoc(NULL),
162 pelmRoot(NULL),
163 sv(SettingsVersion_Null),
164 svRead(SettingsVersion_Null)
165 {}
166
167 ~Data()
168 {
169 cleanup();
170 }
171
172 RTCString strFilename;
173 bool fFileExists;
174
175 xml::Document *pDoc;
176 xml::ElementNode *pelmRoot;
177
178 com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux"
179 SettingsVersion_T sv; // e.g. SettingsVersion_v1_7
180
181 SettingsVersion_T svRead; // settings version that the original file had when it was read,
182 // or SettingsVersion_Null if none
183
184 void copyFrom(const Data &d)
185 {
186 strFilename = d.strFilename;
187 fFileExists = d.fFileExists;
188 strSettingsVersionFull = d.strSettingsVersionFull;
189 sv = d.sv;
190 svRead = d.svRead;
191 }
192
193 void cleanup()
194 {
195 if (pDoc)
196 {
197 delete pDoc;
198 pDoc = NULL;
199 pelmRoot = NULL;
200 }
201 }
202};
203
204/**
205 * Private exception class (not in the header file) that makes
206 * throwing xml::LogicError instances easier. That class is public
207 * and should be caught by client code.
208 */
209class settings::ConfigFileError : public xml::LogicError
210{
211public:
212 ConfigFileError(const ConfigFileBase *file,
213 const xml::Node *pNode,
214 const char *pcszFormat, ...)
215 : xml::LogicError()
216 {
217 va_list args;
218 va_start(args, pcszFormat);
219 Utf8Str strWhat(pcszFormat, args);
220 va_end(args);
221
222 Utf8Str strLine;
223 if (pNode)
224 strLine = Utf8StrFmt(" (line %RU32)", pNode->getLineNumber());
225
226 const char *pcsz = strLine.c_str();
227 Utf8StrFmt str(N_("Error in %s%s -- %s"),
228 file->m->strFilename.c_str(),
229 (pcsz) ? pcsz : "",
230 strWhat.c_str());
231
232 setWhat(str.c_str());
233 }
234};
235
236////////////////////////////////////////////////////////////////////////////////
237//
238// ConfigFileBase
239//
240////////////////////////////////////////////////////////////////////////////////
241
242/**
243 * Constructor. Allocates the XML internals, parses the XML file if
244 * pstrFilename is != NULL and reads the settings version from it.
245 * @param pstrFilename
246 */
247ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
248 : m(new Data)
249{
250 m->fFileExists = false;
251
252 if (pstrFilename)
253 {
254 // reading existing settings file:
255 m->strFilename = *pstrFilename;
256
257 xml::XmlFileParser parser;
258 m->pDoc = new xml::Document;
259 parser.read(*pstrFilename,
260 *m->pDoc);
261
262 m->fFileExists = true;
263
264 m->pelmRoot = m->pDoc->getRootElement();
265 if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
266 throw ConfigFileError(this, m->pelmRoot, N_("Root element in VirtualBox settings files must be \"VirtualBox\""));
267
268 if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
269 throw ConfigFileError(this, m->pelmRoot, N_("Required VirtualBox/@version attribute is missing"));
270
271 LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
272
273 m->sv = parseVersion(m->strSettingsVersionFull, m->pelmRoot);
274
275 // remember the settings version we read in case it gets upgraded later,
276 // so we know when to make backups
277 m->svRead = m->sv;
278 }
279 else
280 {
281 // creating new settings file:
282 m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
283 m->sv = SettingsVersion_v1_12;
284 }
285}
286
287ConfigFileBase::ConfigFileBase(const ConfigFileBase &other)
288 : m(new Data)
289{
290 copyBaseFrom(other);
291 m->strFilename = "";
292 m->fFileExists = false;
293}
294
295/**
296 * Clean up.
297 */
298ConfigFileBase::~ConfigFileBase()
299{
300 if (m)
301 {
302 delete m;
303 m = NULL;
304 }
305}
306
307/**
308 * Helper function to convert a MediaType enum value into string from.
309 * @param t
310 */
311/*static*/
312const char *ConfigFileBase::stringifyMediaType(MediaType t)
313{
314 switch (t)
315 {
316 case HardDisk:
317 return "hard disk";
318 case DVDImage:
319 return "DVD";
320 case FloppyImage:
321 return "floppy";
322 default:
323 AssertMsgFailed(("media type %d\n", t));
324 return "UNKNOWN";
325 }
326}
327
328/**
329 * Helper function that parses a full version number.
330 *
331 * Allow future versions but fail if file is older than 1.6. Throws on errors.
332 * @returns settings version
333 * @param strVersion
334 * @param pElm
335 */
336SettingsVersion_T ConfigFileBase::parseVersion(const Utf8Str &strVersion, const xml::ElementNode *pElm)
337{
338 SettingsVersion_T sv = SettingsVersion_Null;
339 if (strVersion.length() > 3)
340 {
341 uint32_t ulMajor = 0;
342 uint32_t ulMinor = 0;
343
344 const char *pcsz = strVersion.c_str();
345 char c;
346
347 while ( (c = *pcsz)
348 && RT_C_IS_DIGIT(c)
349 )
350 {
351 ulMajor *= 10;
352 ulMajor += c - '0';
353 ++pcsz;
354 }
355
356 if (*pcsz++ == '.')
357 {
358 while ( (c = *pcsz)
359 && RT_C_IS_DIGIT(c)
360 )
361 {
362 ulMinor *= 10;
363 ulMinor += c - '0';
364 ++pcsz;
365 }
366 }
367
368 if (ulMajor == 1)
369 {
370 if (ulMinor == 3)
371 sv = SettingsVersion_v1_3;
372 else if (ulMinor == 4)
373 sv = SettingsVersion_v1_4;
374 else if (ulMinor == 5)
375 sv = SettingsVersion_v1_5;
376 else if (ulMinor == 6)
377 sv = SettingsVersion_v1_6;
378 else if (ulMinor == 7)
379 sv = SettingsVersion_v1_7;
380 else if (ulMinor == 8)
381 sv = SettingsVersion_v1_8;
382 else if (ulMinor == 9)
383 sv = SettingsVersion_v1_9;
384 else if (ulMinor == 10)
385 sv = SettingsVersion_v1_10;
386 else if (ulMinor == 11)
387 sv = SettingsVersion_v1_11;
388 else if (ulMinor == 12)
389 sv = SettingsVersion_v1_12;
390 else if (ulMinor == 13)
391 sv = SettingsVersion_v1_13;
392 else if (ulMinor == 14)
393 sv = SettingsVersion_v1_14;
394 else if (ulMinor == 15)
395 sv = SettingsVersion_v1_15;
396 else if (ulMinor == 16)
397 sv = SettingsVersion_v1_16;
398 else if (ulMinor == 17)
399 sv = SettingsVersion_v1_17;
400 else if (ulMinor > 17)
401 sv = SettingsVersion_Future;
402 }
403 else if (ulMajor > 1)
404 sv = SettingsVersion_Future;
405
406 Log(("Parsed settings version %d.%d to enum value %d\n", ulMajor, ulMinor, sv));
407 }
408
409 if (sv == SettingsVersion_Null)
410 throw ConfigFileError(this, pElm, N_("Cannot handle settings version '%s'"), strVersion.c_str());
411
412 return sv;
413}
414
415/**
416 * Helper function that parses a UUID in string form into
417 * a com::Guid item. Accepts UUIDs both with and without
418 * "{}" brackets. Throws on errors.
419 * @param guid
420 * @param strUUID
421 * @param pElm
422 */
423void ConfigFileBase::parseUUID(Guid &guid,
424 const Utf8Str &strUUID,
425 const xml::ElementNode *pElm) const
426{
427 guid = strUUID.c_str();
428 if (guid.isZero())
429 throw ConfigFileError(this, pElm, N_("UUID \"%s\" has zero format"), strUUID.c_str());
430 else if (!guid.isValid())
431 throw ConfigFileError(this, pElm, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
432}
433
434/**
435 * Parses the given string in str and attempts to treat it as an ISO
436 * date/time stamp to put into timestamp. Throws on errors.
437 * @param timestamp
438 * @param str
439 * @param pElm
440 */
441void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
442 const com::Utf8Str &str,
443 const xml::ElementNode *pElm) const
444{
445 const char *pcsz = str.c_str();
446 // yyyy-mm-ddThh:mm:ss
447 // "2009-07-10T11:54:03Z"
448 // 01234567890123456789
449 // 1
450 if (str.length() > 19)
451 {
452 // timezone must either be unspecified or 'Z' for UTC
453 if ( (pcsz[19])
454 && (pcsz[19] != 'Z')
455 )
456 throw ConfigFileError(this, pElm, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
457
458 int32_t yyyy;
459 uint32_t mm, dd, hh, min, secs;
460 if ( (pcsz[4] == '-')
461 && (pcsz[7] == '-')
462 && (pcsz[10] == 'T')
463 && (pcsz[13] == ':')
464 && (pcsz[16] == ':')
465 )
466 {
467 int rc;
468 if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
469 // could theoretically be negative but let's assume that nobody
470 // created virtual machines before the Christian era
471 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
472 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
473 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
474 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
475 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
476 )
477 {
478 RTTIME time =
479 {
480 yyyy,
481 (uint8_t)mm,
482 0,
483 0,
484 (uint8_t)dd,
485 (uint8_t)hh,
486 (uint8_t)min,
487 (uint8_t)secs,
488 0,
489 RTTIME_FLAGS_TYPE_UTC,
490 0
491 };
492 if (RTTimeNormalize(&time))
493 if (RTTimeImplode(&timestamp, &time))
494 return;
495 }
496
497 throw ConfigFileError(this, pElm, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
498 }
499
500 throw ConfigFileError(this, pElm, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
501 }
502}
503
504/**
505 * Helper function that parses a Base64 formatted string into a binary blob.
506 * @param binary
507 * @param str
508 * @param pElm
509 */
510void ConfigFileBase::parseBase64(IconBlob &binary,
511 const Utf8Str &str,
512 const xml::ElementNode *pElm) const
513{
514#define DECODE_STR_MAX _1M
515 const char* psz = str.c_str();
516 ssize_t cbOut = RTBase64DecodedSize(psz, NULL);
517 if (cbOut > DECODE_STR_MAX)
518 throw ConfigFileError(this, pElm, N_("Base64 encoded data too long (%d > %d)"), cbOut, DECODE_STR_MAX);
519 else if (cbOut < 0)
520 throw ConfigFileError(this, pElm, N_("Base64 encoded data '%s' invalid"), psz);
521 binary.resize(cbOut);
522 int vrc = VINF_SUCCESS;
523 if (cbOut)
524 vrc = RTBase64Decode(psz, &binary.front(), cbOut, NULL, NULL);
525 if (RT_FAILURE(vrc))
526 {
527 binary.resize(0);
528 throw ConfigFileError(this, pElm, N_("Base64 encoded data could not be decoded (%Rrc)"), vrc);
529 }
530}
531
532/**
533 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
534 * @param stamp
535 * @return
536 */
537com::Utf8Str ConfigFileBase::stringifyTimestamp(const RTTIMESPEC &stamp) const
538{
539 RTTIME time;
540 if (!RTTimeExplode(&time, &stamp))
541 throw ConfigFileError(this, NULL, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
542
543 return Utf8StrFmt("%04u-%02u-%02uT%02u:%02u:%02uZ",
544 time.i32Year, time.u8Month, time.u8MonthDay,
545 time.u8Hour, time.u8Minute, time.u8Second);
546}
547
548/**
549 * Helper to create a base64 encoded string out of a binary blob.
550 * @param str
551 * @param binary
552 */
553void ConfigFileBase::toBase64(com::Utf8Str &str, const IconBlob &binary) const
554{
555 ssize_t cb = binary.size();
556 if (cb > 0)
557 {
558 ssize_t cchOut = RTBase64EncodedLength(cb);
559 str.reserve(cchOut+1);
560 int vrc = RTBase64Encode(&binary.front(), cb,
561 str.mutableRaw(), str.capacity(),
562 NULL);
563 if (RT_FAILURE(vrc))
564 throw ConfigFileError(this, NULL, N_("Failed to convert binary data to base64 format (%Rrc)"), vrc);
565 str.jolt();
566 }
567}
568
569/**
570 * Helper method to read in an ExtraData subtree and stores its contents
571 * in the given map of extradata items. Used for both main and machine
572 * extradata (MainConfigFile and MachineConfigFile).
573 * @param elmExtraData
574 * @param map
575 */
576void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
577 StringsMap &map)
578{
579 xml::NodesLoop nlLevel4(elmExtraData);
580 const xml::ElementNode *pelmExtraDataItem;
581 while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
582 {
583 if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
584 {
585 // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
586 Utf8Str strName, strValue;
587 if ( pelmExtraDataItem->getAttributeValue("name", strName)
588 && pelmExtraDataItem->getAttributeValue("value", strValue) )
589 map[strName] = strValue;
590 else
591 throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing"));
592 }
593 }
594}
595
596/**
597 * Reads \<USBDeviceFilter\> entries from under the given elmDeviceFilters node and
598 * stores them in the given linklist. This is in ConfigFileBase because it's used
599 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
600 * filters).
601 * @param elmDeviceFilters
602 * @param ll
603 */
604void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
605 USBDeviceFiltersList &ll)
606{
607 xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
608 const xml::ElementNode *pelmLevel4Child;
609 while ((pelmLevel4Child = nl1.forAllNodes()))
610 {
611 USBDeviceFilter flt;
612 flt.action = USBDeviceFilterAction_Ignore;
613 Utf8Str strAction;
614 if ( pelmLevel4Child->getAttributeValue("name", flt.strName)
615 && pelmLevel4Child->getAttributeValue("active", flt.fActive))
616 {
617 if (!pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId))
618 pelmLevel4Child->getAttributeValue("vendorid", flt.strVendorId); // used before 1.3
619 if (!pelmLevel4Child->getAttributeValue("productId", flt.strProductId))
620 pelmLevel4Child->getAttributeValue("productid", flt.strProductId); // used before 1.3
621 pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
622 pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
623 pelmLevel4Child->getAttributeValue("product", flt.strProduct);
624 if (!pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber))
625 pelmLevel4Child->getAttributeValue("serialnumber", flt.strSerialNumber); // used before 1.3
626 pelmLevel4Child->getAttributeValue("port", flt.strPort);
627
628 // the next 2 are irrelevant for host USB objects
629 pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
630 pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
631
632 // action is only used with host USB objects
633 if (pelmLevel4Child->getAttributeValue("action", strAction))
634 {
635 if (strAction == "Ignore")
636 flt.action = USBDeviceFilterAction_Ignore;
637 else if (strAction == "Hold")
638 flt.action = USBDeviceFilterAction_Hold;
639 else
640 throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str());
641 }
642
643 ll.push_back(flt);
644 }
645 }
646}
647
648/**
649 * Reads a media registry entry from the main VirtualBox.xml file.
650 *
651 * Whereas the current media registry code is fairly straightforward, it was quite a mess
652 * with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements
653 * in the media registry were much more inconsistent, and different elements were used
654 * depending on the type of device and image.
655 *
656 * @param t
657 * @param elmMedium
658 * @param med
659 */
660void ConfigFileBase::readMediumOne(MediaType t,
661 const xml::ElementNode &elmMedium,
662 Medium &med)
663{
664 // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
665
666 Utf8Str strUUID;
667 if (!elmMedium.getAttributeValue("uuid", strUUID))
668 throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName());
669
670 parseUUID(med.uuid, strUUID, &elmMedium);
671
672 bool fNeedsLocation = true;
673
674 if (t == HardDisk)
675 {
676 if (m->sv < SettingsVersion_v1_4)
677 {
678 // here the system is:
679 // <HardDisk uuid="{....}" type="normal">
680 // <VirtualDiskImage filePath="/path/to/xxx.vdi"/>
681 // </HardDisk>
682
683 fNeedsLocation = false;
684 bool fNeedsFilePath = true;
685 const xml::ElementNode *pelmImage;
686 if ((pelmImage = elmMedium.findChildElement("VirtualDiskImage")))
687 med.strFormat = "VDI";
688 else if ((pelmImage = elmMedium.findChildElement("VMDKImage")))
689 med.strFormat = "VMDK";
690 else if ((pelmImage = elmMedium.findChildElement("VHDImage")))
691 med.strFormat = "VHD";
692 else if ((pelmImage = elmMedium.findChildElement("ISCSIHardDisk")))
693 {
694 med.strFormat = "iSCSI";
695
696 fNeedsFilePath = false;
697 // location is special here: current settings specify an "iscsi://user@server:port/target/lun"
698 // string for the location and also have several disk properties for these, whereas this used
699 // to be hidden in several sub-elements before 1.4, so compose a location string and set up
700 // the properties:
701 med.strLocation = "iscsi://";
702 Utf8Str strUser, strServer, strPort, strTarget, strLun;
703 if (pelmImage->getAttributeValue("userName", strUser))
704 {
705 med.strLocation.append(strUser);
706 med.strLocation.append("@");
707 }
708 Utf8Str strServerAndPort;
709 if (pelmImage->getAttributeValue("server", strServer))
710 {
711 strServerAndPort = strServer;
712 }
713 if (pelmImage->getAttributeValue("port", strPort))
714 {
715 if (strServerAndPort.length())
716 strServerAndPort.append(":");
717 strServerAndPort.append(strPort);
718 }
719 med.strLocation.append(strServerAndPort);
720 if (pelmImage->getAttributeValue("target", strTarget))
721 {
722 med.strLocation.append("/");
723 med.strLocation.append(strTarget);
724 }
725 if (pelmImage->getAttributeValue("lun", strLun))
726 {
727 med.strLocation.append("/");
728 med.strLocation.append(strLun);
729 }
730
731 if (strServer.length() && strPort.length())
732 med.properties["TargetAddress"] = strServerAndPort;
733 if (strTarget.length())
734 med.properties["TargetName"] = strTarget;
735 if (strUser.length())
736 med.properties["InitiatorUsername"] = strUser;
737 Utf8Str strPassword;
738 if (pelmImage->getAttributeValue("password", strPassword))
739 med.properties["InitiatorSecret"] = strPassword;
740 if (strLun.length())
741 med.properties["LUN"] = strLun;
742 }
743 else if ((pelmImage = elmMedium.findChildElement("CustomHardDisk")))
744 {
745 fNeedsFilePath = false;
746 fNeedsLocation = true;
747 // also requires @format attribute, which will be queried below
748 }
749 else
750 throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName());
751
752 if (fNeedsFilePath)
753 {
754 if (!(pelmImage->getAttributeValuePath("filePath", med.strLocation)))
755 throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName());
756 }
757 }
758
759 if (med.strFormat.isEmpty()) // not set with 1.4 format above, or 1.4 Custom format?
760 if (!elmMedium.getAttributeValue("format", med.strFormat))
761 throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName());
762
763 if (!elmMedium.getAttributeValue("autoReset", med.fAutoReset))
764 med.fAutoReset = false;
765
766 Utf8Str strType;
767 if (elmMedium.getAttributeValue("type", strType))
768 {
769 // pre-1.4 used lower case, so make this case-insensitive
770 strType.toUpper();
771 if (strType == "NORMAL")
772 med.hdType = MediumType_Normal;
773 else if (strType == "IMMUTABLE")
774 med.hdType = MediumType_Immutable;
775 else if (strType == "WRITETHROUGH")
776 med.hdType = MediumType_Writethrough;
777 else if (strType == "SHAREABLE")
778 med.hdType = MediumType_Shareable;
779 else if (strType == "READONLY")
780 med.hdType = MediumType_Readonly;
781 else if (strType == "MULTIATTACH")
782 med.hdType = MediumType_MultiAttach;
783 else
784 throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable, Writethrough, Shareable, Readonly or MultiAttach"));
785 }
786 }
787 else
788 {
789 if (m->sv < SettingsVersion_v1_4)
790 {
791 // DVD and floppy images before 1.4 had "src" attribute instead of "location"
792 if (!elmMedium.getAttributeValue("src", med.strLocation))
793 throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName());
794
795 fNeedsLocation = false;
796 }
797
798 if (!elmMedium.getAttributeValue("format", med.strFormat))
799 {
800 // DVD and floppy images before 1.11 had no format attribute. assign the default.
801 med.strFormat = "RAW";
802 }
803
804 if (t == DVDImage)
805 med.hdType = MediumType_Readonly;
806 else if (t == FloppyImage)
807 med.hdType = MediumType_Writethrough;
808 }
809
810 if (fNeedsLocation)
811 // current files and 1.4 CustomHardDisk elements must have a location attribute
812 if (!elmMedium.getAttributeValue("location", med.strLocation))
813 throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName());
814
815 // 3.2 builds added Description as an attribute, read it silently
816 // and write it back as an element starting with 5.1.26
817 elmMedium.getAttributeValue("Description", med.strDescription);
818
819 xml::NodesLoop nlMediumChildren(elmMedium);
820 const xml::ElementNode *pelmMediumChild;
821 while ((pelmMediumChild = nlMediumChildren.forAllNodes()))
822 {
823 if (pelmMediumChild->nameEquals("Description"))
824 med.strDescription = pelmMediumChild->getValue();
825 else if (pelmMediumChild->nameEquals("Property"))
826 {
827 // handle medium properties
828 Utf8Str strPropName, strPropValue;
829 if ( pelmMediumChild->getAttributeValue("name", strPropName)
830 && pelmMediumChild->getAttributeValue("value", strPropValue) )
831 med.properties[strPropName] = strPropValue;
832 else
833 throw ConfigFileError(this, pelmMediumChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
834 }
835 }
836}
837
838/**
839 * Reads a media registry entry from the main VirtualBox.xml file and recurses
840 * into children where applicable.
841 *
842 * @param t
843 * @param depth
844 * @param elmMedium
845 * @param med
846 */
847void ConfigFileBase::readMedium(MediaType t,
848 uint32_t depth,
849 const xml::ElementNode &elmMedium, // HardDisk node if root; if recursing,
850 // child HardDisk node or DiffHardDisk node for pre-1.4
851 Medium &med) // medium settings to fill out
852{
853 if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
854 throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
855
856 // Do not inline this method call, as the purpose of having this separate
857 // is to save on stack size. Less local variables are the key for reaching
858 // deep recursion levels with small stack (XPCOM/g++ without optimization).
859 readMediumOne(t, elmMedium, med);
860
861 if (t != HardDisk)
862 return;
863
864 // recurse to handle children
865 MediaList &llSettingsChildren = med.llChildren;
866 xml::NodesLoop nl2(elmMedium, m->sv >= SettingsVersion_v1_4 ? "HardDisk" : "DiffHardDisk");
867 const xml::ElementNode *pelmHDChild;
868 while ((pelmHDChild = nl2.forAllNodes()))
869 {
870 // recurse with this element and put the child at the end of the list.
871 // XPCOM has very small stack, avoid big local variables and use the
872 // list element.
873 llSettingsChildren.push_back(Medium::Empty);
874 readMedium(t,
875 depth + 1,
876 *pelmHDChild,
877 llSettingsChildren.back());
878 }
879}
880
881/**
882 * Reads in the entire \<MediaRegistry\> chunk and stores its media in the lists
883 * of the given MediaRegistry structure.
884 *
885 * This is used in both MainConfigFile and MachineConfigFile since starting with
886 * VirtualBox 4.0, we can have media registries in both.
887 *
888 * For pre-1.4 files, this gets called with the \<DiskRegistry\> chunk instead.
889 *
890 * @param elmMediaRegistry
891 * @param mr
892 */
893void ConfigFileBase::readMediaRegistry(const xml::ElementNode &elmMediaRegistry,
894 MediaRegistry &mr)
895{
896 xml::NodesLoop nl1(elmMediaRegistry);
897 const xml::ElementNode *pelmChild1;
898 while ((pelmChild1 = nl1.forAllNodes()))
899 {
900 MediaType t = Error;
901 if (pelmChild1->nameEquals("HardDisks"))
902 t = HardDisk;
903 else if (pelmChild1->nameEquals("DVDImages"))
904 t = DVDImage;
905 else if (pelmChild1->nameEquals("FloppyImages"))
906 t = FloppyImage;
907 else
908 continue;
909
910 xml::NodesLoop nl2(*pelmChild1);
911 const xml::ElementNode *pelmMedium;
912 while ((pelmMedium = nl2.forAllNodes()))
913 {
914 if ( t == HardDisk
915 && (pelmMedium->nameEquals("HardDisk")))
916 {
917 mr.llHardDisks.push_back(Medium::Empty);
918 readMedium(t, 1, *pelmMedium, mr.llHardDisks.back());
919 }
920 else if ( t == DVDImage
921 && (pelmMedium->nameEquals("Image")))
922 {
923 mr.llDvdImages.push_back(Medium::Empty);
924 readMedium(t, 1, *pelmMedium, mr.llDvdImages.back());
925 }
926 else if ( t == FloppyImage
927 && (pelmMedium->nameEquals("Image")))
928 {
929 mr.llFloppyImages.push_back(Medium::Empty);
930 readMedium(t, 1, *pelmMedium, mr.llFloppyImages.back());
931 }
932 }
933 }
934}
935
936/**
937 * This is common version for reading NAT port forward rule in per-_machine's_adapter_ and
938 * per-network approaches.
939 * Note: this function doesn't in fill given list from xml::ElementNodesList, because there is conflicting
940 * declaration in ovmfreader.h.
941 */
942void ConfigFileBase::readNATForwardRulesMap(const xml::ElementNode &elmParent, NATRulesMap &mapRules)
943{
944 xml::ElementNodesList plstRules;
945 elmParent.getChildElements(plstRules, "Forwarding");
946 for (xml::ElementNodesList::iterator pf = plstRules.begin(); pf != plstRules.end(); ++pf)
947 {
948 NATRule rule;
949 uint32_t port = 0;
950 (*pf)->getAttributeValue("name", rule.strName);
951 (*pf)->getAttributeValue("proto", (uint32_t&)rule.proto);
952 (*pf)->getAttributeValue("hostip", rule.strHostIP);
953 (*pf)->getAttributeValue("hostport", port);
954 rule.u16HostPort = (uint16_t)port;
955 (*pf)->getAttributeValue("guestip", rule.strGuestIP);
956 (*pf)->getAttributeValue("guestport", port);
957 rule.u16GuestPort = (uint16_t)port;
958 mapRules.insert(std::make_pair(rule.strName, rule));
959 }
960}
961
962void ConfigFileBase::readNATLoopbacks(const xml::ElementNode &elmParent, NATLoopbackOffsetList &llLoopbacks)
963{
964 xml::ElementNodesList plstLoopbacks;
965 elmParent.getChildElements(plstLoopbacks, "Loopback4");
966 for (xml::ElementNodesList::iterator lo = plstLoopbacks.begin();
967 lo != plstLoopbacks.end(); ++lo)
968 {
969 NATHostLoopbackOffset loopback;
970 (*lo)->getAttributeValue("address", loopback.strLoopbackHostAddress);
971 (*lo)->getAttributeValue("offset", (uint32_t&)loopback.u32Offset);
972 llLoopbacks.push_back(loopback);
973 }
974}
975
976
977/**
978 * Adds a "version" attribute to the given XML element with the
979 * VirtualBox settings version (e.g. "1.10-linux"). Used by
980 * the XML format for the root element and by the OVF export
981 * for the vbox:Machine element.
982 * @param elm
983 */
984void ConfigFileBase::setVersionAttribute(xml::ElementNode &elm)
985{
986 const char *pcszVersion = NULL;
987 switch (m->sv)
988 {
989 case SettingsVersion_v1_8:
990 pcszVersion = "1.8";
991 break;
992
993 case SettingsVersion_v1_9:
994 pcszVersion = "1.9";
995 break;
996
997 case SettingsVersion_v1_10:
998 pcszVersion = "1.10";
999 break;
1000
1001 case SettingsVersion_v1_11:
1002 pcszVersion = "1.11";
1003 break;
1004
1005 case SettingsVersion_v1_12:
1006 pcszVersion = "1.12";
1007 break;
1008
1009 case SettingsVersion_v1_13:
1010 pcszVersion = "1.13";
1011 break;
1012
1013 case SettingsVersion_v1_14:
1014 pcszVersion = "1.14";
1015 break;
1016
1017 case SettingsVersion_v1_15:
1018 pcszVersion = "1.15";
1019 break;
1020
1021 case SettingsVersion_v1_16:
1022 pcszVersion = "1.16";
1023 break;
1024
1025 case SettingsVersion_v1_17:
1026 pcszVersion = "1.17";
1027 break;
1028
1029 default:
1030 // catch human error: the assertion below will trigger in debug
1031 // or dbgopt builds, so hopefully this will get noticed sooner in
1032 // the future, because it's easy to forget top update something.
1033 AssertMsg(m->sv <= SettingsVersion_v1_7, ("Settings.cpp: unexpected settings version %d, unhandled future version?\n", m->sv));
1034 // silently upgrade if this is less than 1.7 because that's the oldest we can write
1035 if (m->sv <= SettingsVersion_v1_7)
1036 {
1037 pcszVersion = "1.7";
1038 m->sv = SettingsVersion_v1_7;
1039 }
1040 else
1041 {
1042 // This is reached for SettingsVersion_Future and forgotten
1043 // settings version after SettingsVersion_v1_7, which should
1044 // not happen (see assertion above). Set the version to the
1045 // latest known version, to minimize loss of information, but
1046 // as we can't predict the future we have to use some format
1047 // we know, and latest should be the best choice. Note that
1048 // for "forgotten settings" this may not be the best choice,
1049 // but as it's an omission of someone who changed this file
1050 // it's the only generic possibility.
1051 pcszVersion = "1.17";
1052 m->sv = SettingsVersion_v1_17;
1053 }
1054 break;
1055 }
1056
1057 m->strSettingsVersionFull = Utf8StrFmt("%s-%s",
1058 pcszVersion,
1059 VBOX_XML_PLATFORM); // e.g. "linux"
1060 elm.setAttribute("version", m->strSettingsVersionFull);
1061}
1062
1063
1064/**
1065 * Creates a special backup file in case there is a version
1066 * bump, so that it is possible to go back to the previous
1067 * state. This is done only once (not for every settings
1068 * version bump), when the settings version is newer than
1069 * the version read from the config file. Must be called
1070 * before ConfigFileBase::createStubDocument, because that
1071 * method may alter information which this method needs.
1072 */
1073void ConfigFileBase::specialBackupIfFirstBump()
1074{
1075 // Since this gets called before the XML document is actually written out,
1076 // this is where we must check whether we're upgrading the settings version
1077 // and need to make a backup, so the user can go back to an earlier
1078 // VirtualBox version and recover his old settings files.
1079 if ( (m->svRead != SettingsVersion_Null) // old file exists?
1080 && (m->svRead < m->sv) // we're upgrading?
1081 )
1082 {
1083 // compose new filename: strip off trailing ".xml"/".vbox"
1084 Utf8Str strFilenameNew;
1085 Utf8Str strExt = ".xml";
1086 if (m->strFilename.endsWith(".xml"))
1087 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 4);
1088 else if (m->strFilename.endsWith(".vbox"))
1089 {
1090 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 5);
1091 strExt = ".vbox";
1092 }
1093
1094 // and append something like "-1.3-linux.xml"
1095 strFilenameNew.append("-");
1096 strFilenameNew.append(m->strSettingsVersionFull); // e.g. "1.3-linux"
1097 strFilenameNew.append(strExt); // .xml for main config, .vbox for machine config
1098
1099 // Copying the file cannot be avoided, as doing tricks with renaming
1100 // causes trouble on OS X with aliases (which follow the rename), and
1101 // on all platforms there is a risk of "losing" the VM config when
1102 // running out of space, as a rename here couldn't be rolled back.
1103 // Ignoring all errors besides running out of space is intentional, as
1104 // we don't want to do anything if the file already exists.
1105 int vrc = RTFileCopy(m->strFilename.c_str(), strFilenameNew.c_str());
1106 if (RT_UNLIKELY(vrc == VERR_DISK_FULL))
1107 throw ConfigFileError(this, NULL, N_("Cannot create settings backup file when upgrading to a newer settings format"));
1108
1109 // do this only once
1110 m->svRead = SettingsVersion_Null;
1111 }
1112}
1113
1114/**
1115 * Creates a new stub xml::Document in the m->pDoc member with the
1116 * root "VirtualBox" element set up. This is used by both
1117 * MainConfigFile and MachineConfigFile at the beginning of writing
1118 * out their XML.
1119 *
1120 * Before calling this, it is the responsibility of the caller to
1121 * set the "sv" member to the required settings version that is to
1122 * be written. For newly created files, the settings version will be
1123 * recent (1.12 or later if necessary); for files read in from disk
1124 * earlier, it will be the settings version indicated in the file.
1125 * However, this method will silently make sure that the settings
1126 * version is always at least 1.7 and change it if necessary, since
1127 * there is no write support for earlier settings versions.
1128 */
1129void ConfigFileBase::createStubDocument()
1130{
1131 Assert(m->pDoc == NULL);
1132 m->pDoc = new xml::Document;
1133
1134 m->pelmRoot = m->pDoc->createRootElement("VirtualBox",
1135 "\n"
1136 "** DO NOT EDIT THIS FILE.\n"
1137 "** If you make changes to this file while any VirtualBox related application\n"
1138 "** is running, your changes will be overwritten later, without taking effect.\n"
1139 "** Use VBoxManage or the VirtualBox Manager GUI to make changes.\n"
1140);
1141 m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
1142 // Have the code for producing a proper schema reference. Not used by most
1143 // tools, so don't bother doing it. The schema is not on the server anyway.
1144#ifdef VBOX_WITH_SETTINGS_SCHEMA
1145 m->pelmRoot->setAttribute("xmlns:xsi", "http://www.w3.org/2001/XMLSchema-instance");
1146 m->pelmRoot->setAttribute("xsi:schemaLocation", VBOX_XML_NAMESPACE " " VBOX_XML_SCHEMA);
1147#endif
1148
1149 // add settings version attribute to root element, update m->strSettingsVersionFull
1150 setVersionAttribute(*m->pelmRoot);
1151
1152 LogRel(("Saving settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
1153}
1154
1155/**
1156 * Creates an \<ExtraData\> node under the given parent element with
1157 * \<ExtraDataItem\> childern according to the contents of the given
1158 * map.
1159 *
1160 * This is in ConfigFileBase because it's used in both MainConfigFile
1161 * and MachineConfigFile, which both can have extradata.
1162 *
1163 * @param elmParent
1164 * @param me
1165 */
1166void ConfigFileBase::buildExtraData(xml::ElementNode &elmParent,
1167 const StringsMap &me)
1168{
1169 if (me.size())
1170 {
1171 xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
1172 for (StringsMap::const_iterator it = me.begin();
1173 it != me.end();
1174 ++it)
1175 {
1176 const Utf8Str &strName = it->first;
1177 const Utf8Str &strValue = it->second;
1178 xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
1179 pelmThis->setAttribute("name", strName);
1180 pelmThis->setAttribute("value", strValue);
1181 }
1182 }
1183}
1184
1185/**
1186 * Creates \<DeviceFilter\> nodes under the given parent element according to
1187 * the contents of the given USBDeviceFiltersList. This is in ConfigFileBase
1188 * because it's used in both MainConfigFile (for host filters) and
1189 * MachineConfigFile (for machine filters).
1190 *
1191 * If fHostMode is true, this means that we're supposed to write filters
1192 * for the IHost interface (respect "action", omit "strRemote" and
1193 * "ulMaskedInterfaces" in struct USBDeviceFilter).
1194 *
1195 * @param elmParent
1196 * @param ll
1197 * @param fHostMode
1198 */
1199void ConfigFileBase::buildUSBDeviceFilters(xml::ElementNode &elmParent,
1200 const USBDeviceFiltersList &ll,
1201 bool fHostMode)
1202{
1203 for (USBDeviceFiltersList::const_iterator it = ll.begin();
1204 it != ll.end();
1205 ++it)
1206 {
1207 const USBDeviceFilter &flt = *it;
1208 xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
1209 pelmFilter->setAttribute("name", flt.strName);
1210 pelmFilter->setAttribute("active", flt.fActive);
1211 if (flt.strVendorId.length())
1212 pelmFilter->setAttribute("vendorId", flt.strVendorId);
1213 if (flt.strProductId.length())
1214 pelmFilter->setAttribute("productId", flt.strProductId);
1215 if (flt.strRevision.length())
1216 pelmFilter->setAttribute("revision", flt.strRevision);
1217 if (flt.strManufacturer.length())
1218 pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
1219 if (flt.strProduct.length())
1220 pelmFilter->setAttribute("product", flt.strProduct);
1221 if (flt.strSerialNumber.length())
1222 pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
1223 if (flt.strPort.length())
1224 pelmFilter->setAttribute("port", flt.strPort);
1225
1226 if (fHostMode)
1227 {
1228 const char *pcsz =
1229 (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
1230 : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
1231 pelmFilter->setAttribute("action", pcsz);
1232 }
1233 else
1234 {
1235 if (flt.strRemote.length())
1236 pelmFilter->setAttribute("remote", flt.strRemote);
1237 if (flt.ulMaskedInterfaces)
1238 pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
1239 }
1240 }
1241}
1242
1243/**
1244 * Creates a single \<HardDisk\> element for the given Medium structure
1245 * and recurses to write the child hard disks underneath. Called from
1246 * MainConfigFile::write().
1247 *
1248 * @param t
1249 * @param depth
1250 * @param elmMedium
1251 * @param mdm
1252 */
1253void ConfigFileBase::buildMedium(MediaType t,
1254 uint32_t depth,
1255 xml::ElementNode &elmMedium,
1256 const Medium &mdm)
1257{
1258 if (depth > SETTINGS_MEDIUM_DEPTH_MAX)
1259 throw ConfigFileError(this, &elmMedium, N_("Maximum medium tree depth of %u exceeded"), SETTINGS_MEDIUM_DEPTH_MAX);
1260
1261 xml::ElementNode *pelmMedium;
1262
1263 if (t == HardDisk)
1264 pelmMedium = elmMedium.createChild("HardDisk");
1265 else
1266 pelmMedium = elmMedium.createChild("Image");
1267
1268 pelmMedium->setAttribute("uuid", mdm.uuid.toStringCurly());
1269
1270 pelmMedium->setAttributePath("location", mdm.strLocation);
1271
1272 if (t == HardDisk || RTStrICmp(mdm.strFormat.c_str(), "RAW"))
1273 pelmMedium->setAttribute("format", mdm.strFormat);
1274 if ( t == HardDisk
1275 && mdm.fAutoReset)
1276 pelmMedium->setAttribute("autoReset", mdm.fAutoReset);
1277 if (mdm.strDescription.length())
1278 pelmMedium->createChild("Description")->addContent(mdm.strDescription);
1279
1280 for (StringsMap::const_iterator it = mdm.properties.begin();
1281 it != mdm.properties.end();
1282 ++it)
1283 {
1284 xml::ElementNode *pelmProp = pelmMedium->createChild("Property");
1285 pelmProp->setAttribute("name", it->first);
1286 pelmProp->setAttribute("value", it->second);
1287 }
1288
1289 // only for base hard disks, save the type
1290 if (depth == 1)
1291 {
1292 // no need to save the usual DVD/floppy medium types
1293 if ( ( t != DVDImage
1294 || ( mdm.hdType != MediumType_Writethrough // shouldn't happen
1295 && mdm.hdType != MediumType_Readonly))
1296 && ( t != FloppyImage
1297 || mdm.hdType != MediumType_Writethrough))
1298 {
1299 const char *pcszType =
1300 mdm.hdType == MediumType_Normal ? "Normal" :
1301 mdm.hdType == MediumType_Immutable ? "Immutable" :
1302 mdm.hdType == MediumType_Writethrough ? "Writethrough" :
1303 mdm.hdType == MediumType_Shareable ? "Shareable" :
1304 mdm.hdType == MediumType_Readonly ? "Readonly" :
1305 mdm.hdType == MediumType_MultiAttach ? "MultiAttach" :
1306 "INVALID";
1307 pelmMedium->setAttribute("type", pcszType);
1308 }
1309 }
1310
1311 for (MediaList::const_iterator it = mdm.llChildren.begin();
1312 it != mdm.llChildren.end();
1313 ++it)
1314 {
1315 // recurse for children
1316 buildMedium(t, // device type
1317 depth + 1, // depth
1318 *pelmMedium, // parent
1319 *it); // settings::Medium
1320 }
1321}
1322
1323/**
1324 * Creates a \<MediaRegistry\> node under the given parent and writes out all
1325 * hard disks and DVD and floppy images from the lists in the given MediaRegistry
1326 * structure under it.
1327 *
1328 * This is used in both MainConfigFile and MachineConfigFile since starting with
1329 * VirtualBox 4.0, we can have media registries in both.
1330 *
1331 * @param elmParent
1332 * @param mr
1333 */
1334void ConfigFileBase::buildMediaRegistry(xml::ElementNode &elmParent,
1335 const MediaRegistry &mr)
1336{
1337 if (mr.llHardDisks.size() == 0 && mr.llDvdImages.size() == 0 && mr.llFloppyImages.size() == 0)
1338 return;
1339
1340 xml::ElementNode *pelmMediaRegistry = elmParent.createChild("MediaRegistry");
1341
1342 if (mr.llHardDisks.size())
1343 {
1344 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
1345 for (MediaList::const_iterator it = mr.llHardDisks.begin();
1346 it != mr.llHardDisks.end();
1347 ++it)
1348 {
1349 buildMedium(HardDisk, 1, *pelmHardDisks, *it);
1350 }
1351 }
1352
1353 if (mr.llDvdImages.size())
1354 {
1355 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
1356 for (MediaList::const_iterator it = mr.llDvdImages.begin();
1357 it != mr.llDvdImages.end();
1358 ++it)
1359 {
1360 buildMedium(DVDImage, 1, *pelmDVDImages, *it);
1361 }
1362 }
1363
1364 if (mr.llFloppyImages.size())
1365 {
1366 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
1367 for (MediaList::const_iterator it = mr.llFloppyImages.begin();
1368 it != mr.llFloppyImages.end();
1369 ++it)
1370 {
1371 buildMedium(FloppyImage, 1, *pelmFloppyImages, *it);
1372 }
1373 }
1374}
1375
1376/**
1377 * Serialize NAT port-forwarding rules in parent container.
1378 * Note: it's responsibility of caller to create parent of the list tag.
1379 * because this method used for serializing per-_mahine's_adapter_ and per-network approaches.
1380 */
1381void ConfigFileBase::buildNATForwardRulesMap(xml::ElementNode &elmParent, const NATRulesMap &mapRules)
1382{
1383 for (NATRulesMap::const_iterator r = mapRules.begin();
1384 r != mapRules.end(); ++r)
1385 {
1386 xml::ElementNode *pelmPF;
1387 pelmPF = elmParent.createChild("Forwarding");
1388 const NATRule &nr = r->second;
1389 if (nr.strName.length())
1390 pelmPF->setAttribute("name", nr.strName);
1391 pelmPF->setAttribute("proto", nr.proto);
1392 if (nr.strHostIP.length())
1393 pelmPF->setAttribute("hostip", nr.strHostIP);
1394 if (nr.u16HostPort)
1395 pelmPF->setAttribute("hostport", nr.u16HostPort);
1396 if (nr.strGuestIP.length())
1397 pelmPF->setAttribute("guestip", nr.strGuestIP);
1398 if (nr.u16GuestPort)
1399 pelmPF->setAttribute("guestport", nr.u16GuestPort);
1400 }
1401}
1402
1403
1404void ConfigFileBase::buildNATLoopbacks(xml::ElementNode &elmParent, const NATLoopbackOffsetList &natLoopbackOffsetList)
1405{
1406 for (NATLoopbackOffsetList::const_iterator lo = natLoopbackOffsetList.begin();
1407 lo != natLoopbackOffsetList.end(); ++lo)
1408 {
1409 xml::ElementNode *pelmLo;
1410 pelmLo = elmParent.createChild("Loopback4");
1411 pelmLo->setAttribute("address", (*lo).strLoopbackHostAddress);
1412 pelmLo->setAttribute("offset", (*lo).u32Offset);
1413 }
1414}
1415
1416/**
1417 * Cleans up memory allocated by the internal XML parser. To be called by
1418 * descendant classes when they're done analyzing the DOM tree to discard it.
1419 */
1420void ConfigFileBase::clearDocument()
1421{
1422 m->cleanup();
1423}
1424
1425/**
1426 * Returns true only if the underlying config file exists on disk;
1427 * either because the file has been loaded from disk, or it's been written
1428 * to disk, or both.
1429 * @return
1430 */
1431bool ConfigFileBase::fileExists()
1432{
1433 return m->fFileExists;
1434}
1435
1436/**
1437 * Copies the base variables from another instance. Used by Machine::saveSettings
1438 * so that the settings version does not get lost when a copy of the Machine settings
1439 * file is made to see if settings have actually changed.
1440 * @param b
1441 */
1442void ConfigFileBase::copyBaseFrom(const ConfigFileBase &b)
1443{
1444 m->copyFrom(*b.m);
1445}
1446
1447////////////////////////////////////////////////////////////////////////////////
1448//
1449// Structures shared between Machine XML and VirtualBox.xml
1450//
1451////////////////////////////////////////////////////////////////////////////////
1452
1453
1454/**
1455 * Constructor. Needs to set sane defaults which stand the test of time.
1456 */
1457USBDeviceFilter::USBDeviceFilter() :
1458 fActive(false),
1459 action(USBDeviceFilterAction_Null),
1460 ulMaskedInterfaces(0)
1461{
1462}
1463
1464/**
1465 * Comparison operator. This gets called from MachineConfigFile::operator==,
1466 * which in turn gets called from Machine::saveSettings to figure out whether
1467 * machine settings have really changed and thus need to be written out to disk.
1468 */
1469bool USBDeviceFilter::operator==(const USBDeviceFilter &u) const
1470{
1471 return (this == &u)
1472 || ( strName == u.strName
1473 && fActive == u.fActive
1474 && strVendorId == u.strVendorId
1475 && strProductId == u.strProductId
1476 && strRevision == u.strRevision
1477 && strManufacturer == u.strManufacturer
1478 && strProduct == u.strProduct
1479 && strSerialNumber == u.strSerialNumber
1480 && strPort == u.strPort
1481 && action == u.action
1482 && strRemote == u.strRemote
1483 && ulMaskedInterfaces == u.ulMaskedInterfaces);
1484}
1485
1486/**
1487 * Constructor. Needs to set sane defaults which stand the test of time.
1488 */
1489settings::Medium::Medium() :
1490 fAutoReset(false),
1491 hdType(MediumType_Normal)
1492{
1493}
1494
1495/**
1496 * Comparison operator. This gets called from MachineConfigFile::operator==,
1497 * which in turn gets called from Machine::saveSettings to figure out whether
1498 * machine settings have really changed and thus need to be written out to disk.
1499 */
1500bool settings::Medium::operator==(const settings::Medium &m) const
1501{
1502 return (this == &m)
1503 || ( uuid == m.uuid
1504 && strLocation == m.strLocation
1505 && strDescription == m.strDescription
1506 && strFormat == m.strFormat
1507 && fAutoReset == m.fAutoReset
1508 && properties == m.properties
1509 && hdType == m.hdType
1510 && llChildren == m.llChildren); // this is deep and recurses
1511}
1512
1513const struct settings::Medium settings::Medium::Empty; /* default ctor is OK */
1514
1515/**
1516 * Comparison operator. This gets called from MachineConfigFile::operator==,
1517 * which in turn gets called from Machine::saveSettings to figure out whether
1518 * machine settings have really changed and thus need to be written out to disk.
1519 */
1520bool MediaRegistry::operator==(const MediaRegistry &m) const
1521{
1522 return (this == &m)
1523 || ( llHardDisks == m.llHardDisks
1524 && llDvdImages == m.llDvdImages
1525 && llFloppyImages == m.llFloppyImages);
1526}
1527
1528/**
1529 * Constructor. Needs to set sane defaults which stand the test of time.
1530 */
1531NATRule::NATRule() :
1532 proto(NATProtocol_TCP),
1533 u16HostPort(0),
1534 u16GuestPort(0)
1535{
1536}
1537
1538/**
1539 * Comparison operator. This gets called from MachineConfigFile::operator==,
1540 * which in turn gets called from Machine::saveSettings to figure out whether
1541 * machine settings have really changed and thus need to be written out to disk.
1542 */
1543bool NATRule::operator==(const NATRule &r) const
1544{
1545 return (this == &r)
1546 || ( strName == r.strName
1547 && proto == r.proto
1548 && u16HostPort == r.u16HostPort
1549 && strHostIP == r.strHostIP
1550 && u16GuestPort == r.u16GuestPort
1551 && strGuestIP == r.strGuestIP);
1552}
1553
1554/**
1555 * Constructor. Needs to set sane defaults which stand the test of time.
1556 */
1557NATHostLoopbackOffset::NATHostLoopbackOffset() :
1558 u32Offset(0)
1559{
1560}
1561
1562/**
1563 * Comparison operator. This gets called from MachineConfigFile::operator==,
1564 * which in turn gets called from Machine::saveSettings to figure out whether
1565 * machine settings have really changed and thus need to be written out to disk.
1566 */
1567bool NATHostLoopbackOffset::operator==(const NATHostLoopbackOffset &o) const
1568{
1569 return (this == &o)
1570 || ( strLoopbackHostAddress == o.strLoopbackHostAddress
1571 && u32Offset == o.u32Offset);
1572}
1573
1574
1575////////////////////////////////////////////////////////////////////////////////
1576//
1577// VirtualBox.xml structures
1578//
1579////////////////////////////////////////////////////////////////////////////////
1580
1581/**
1582 * Constructor. Needs to set sane defaults which stand the test of time.
1583 */
1584SystemProperties::SystemProperties()
1585 : uProxyMode(ProxyMode_System)
1586 , uLogHistoryCount(3)
1587 , fExclusiveHwVirt(true)
1588{
1589#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS)
1590 fExclusiveHwVirt = false;
1591#endif
1592}
1593
1594/**
1595 * Constructor. Needs to set sane defaults which stand the test of time.
1596 */
1597DhcpOptValue::DhcpOptValue() :
1598 text(),
1599 encoding(DhcpOptEncoding_Legacy)
1600{
1601}
1602
1603/**
1604 * Non-standard constructor.
1605 */
1606DhcpOptValue::DhcpOptValue(const com::Utf8Str &aText, DhcpOptEncoding_T aEncoding) :
1607 text(aText),
1608 encoding(aEncoding)
1609{
1610}
1611
1612/**
1613 * Non-standard constructor.
1614 */
1615VmNameSlotKey::VmNameSlotKey(const com::Utf8Str& aVmName, LONG aSlot) :
1616 VmName(aVmName),
1617 Slot(aSlot)
1618{
1619}
1620
1621/**
1622 * Non-standard comparison operator.
1623 */
1624bool VmNameSlotKey::operator< (const VmNameSlotKey& that) const
1625{
1626 if (VmName == that.VmName)
1627 return Slot < that.Slot;
1628 else
1629 return VmName < that.VmName;
1630}
1631
1632/**
1633 * Constructor. Needs to set sane defaults which stand the test of time.
1634 */
1635DHCPServer::DHCPServer() :
1636 fEnabled(false)
1637{
1638}
1639
1640/**
1641 * Constructor. Needs to set sane defaults which stand the test of time.
1642 */
1643NATNetwork::NATNetwork() :
1644 fEnabled(true),
1645 fIPv6Enabled(false),
1646 fAdvertiseDefaultIPv6Route(false),
1647 fNeedDhcpServer(true),
1648 u32HostLoopback6Offset(0)
1649{
1650}
1651
1652
1653
1654////////////////////////////////////////////////////////////////////////////////
1655//
1656// MainConfigFile
1657//
1658////////////////////////////////////////////////////////////////////////////////
1659
1660/**
1661 * Reads one \<MachineEntry\> from the main VirtualBox.xml file.
1662 * @param elmMachineRegistry
1663 */
1664void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
1665{
1666 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
1667 xml::NodesLoop nl1(elmMachineRegistry);
1668 const xml::ElementNode *pelmChild1;
1669 while ((pelmChild1 = nl1.forAllNodes()))
1670 {
1671 if (pelmChild1->nameEquals("MachineEntry"))
1672 {
1673 MachineRegistryEntry mre;
1674 Utf8Str strUUID;
1675 if ( pelmChild1->getAttributeValue("uuid", strUUID)
1676 && pelmChild1->getAttributeValue("src", mre.strSettingsFile) )
1677 {
1678 parseUUID(mre.uuid, strUUID, pelmChild1);
1679 llMachines.push_back(mre);
1680 }
1681 else
1682 throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing"));
1683 }
1684 }
1685}
1686
1687/**
1688 * Reads in the \<DHCPServers\> chunk.
1689 * @param elmDHCPServers
1690 */
1691void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
1692{
1693 xml::NodesLoop nl1(elmDHCPServers);
1694 const xml::ElementNode *pelmServer;
1695 while ((pelmServer = nl1.forAllNodes()))
1696 {
1697 if (pelmServer->nameEquals("DHCPServer"))
1698 {
1699 DHCPServer srv;
1700 if ( pelmServer->getAttributeValue("networkName", srv.strNetworkName)
1701 && pelmServer->getAttributeValue("IPAddress", srv.strIPAddress)
1702 && pelmServer->getAttributeValue("networkMask", srv.GlobalDhcpOptions[DhcpOpt_SubnetMask].text)
1703 && pelmServer->getAttributeValue("lowerIP", srv.strIPLower)
1704 && pelmServer->getAttributeValue("upperIP", srv.strIPUpper)
1705 && pelmServer->getAttributeValue("enabled", srv.fEnabled) )
1706 {
1707 xml::NodesLoop nlOptions(*pelmServer, "Options");
1708 const xml::ElementNode *options;
1709 /* XXX: Options are in 1:1 relation to DHCPServer */
1710
1711 while ((options = nlOptions.forAllNodes()))
1712 {
1713 readDhcpOptions(srv.GlobalDhcpOptions, *options);
1714 } /* end of forall("Options") */
1715 xml::NodesLoop nlConfig(*pelmServer, "Config");
1716 const xml::ElementNode *cfg;
1717 while ((cfg = nlConfig.forAllNodes()))
1718 {
1719 com::Utf8Str strVmName;
1720 uint32_t u32Slot;
1721 cfg->getAttributeValue("vm-name", strVmName);
1722 cfg->getAttributeValue("slot", u32Slot);
1723 readDhcpOptions(srv.VmSlot2OptionsM[VmNameSlotKey(strVmName, u32Slot)], *cfg);
1724 }
1725 llDhcpServers.push_back(srv);
1726 }
1727 else
1728 throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing"));
1729 }
1730 }
1731}
1732
1733void MainConfigFile::readDhcpOptions(DhcpOptionMap& map,
1734 const xml::ElementNode& options)
1735{
1736 xml::NodesLoop nl2(options, "Option");
1737 const xml::ElementNode *opt;
1738 while ((opt = nl2.forAllNodes()))
1739 {
1740 DhcpOpt_T OptName;
1741 com::Utf8Str OptText;
1742 int32_t OptEnc = DhcpOptEncoding_Legacy;
1743
1744 opt->getAttributeValue("name", (uint32_t&)OptName);
1745
1746 if (OptName == DhcpOpt_SubnetMask)
1747 continue;
1748
1749 opt->getAttributeValue("value", OptText);
1750 opt->getAttributeValue("encoding", OptEnc);
1751
1752 map[OptName] = DhcpOptValue(OptText, (DhcpOptEncoding_T)OptEnc);
1753 } /* end of forall("Option") */
1754
1755}
1756
1757/**
1758 * Reads in the \<NATNetworks\> chunk.
1759 * @param elmNATNetworks
1760 */
1761void MainConfigFile::readNATNetworks(const xml::ElementNode &elmNATNetworks)
1762{
1763 xml::NodesLoop nl1(elmNATNetworks);
1764 const xml::ElementNode *pelmNet;
1765 while ((pelmNet = nl1.forAllNodes()))
1766 {
1767 if (pelmNet->nameEquals("NATNetwork"))
1768 {
1769 NATNetwork net;
1770 if ( pelmNet->getAttributeValue("networkName", net.strNetworkName)
1771 && pelmNet->getAttributeValue("enabled", net.fEnabled)
1772 && pelmNet->getAttributeValue("network", net.strIPv4NetworkCidr)
1773 && pelmNet->getAttributeValue("ipv6", net.fIPv6Enabled)
1774 && pelmNet->getAttributeValue("ipv6prefix", net.strIPv6Prefix)
1775 && pelmNet->getAttributeValue("advertiseDefaultIPv6Route", net.fAdvertiseDefaultIPv6Route)
1776 && pelmNet->getAttributeValue("needDhcp", net.fNeedDhcpServer) )
1777 {
1778 pelmNet->getAttributeValue("loopback6", net.u32HostLoopback6Offset);
1779 const xml::ElementNode *pelmMappings;
1780 if ((pelmMappings = pelmNet->findChildElement("Mappings")))
1781 readNATLoopbacks(*pelmMappings, net.llHostLoopbackOffsetList);
1782
1783 const xml::ElementNode *pelmPortForwardRules4;
1784 if ((pelmPortForwardRules4 = pelmNet->findChildElement("PortForwarding4")))
1785 readNATForwardRulesMap(*pelmPortForwardRules4,
1786 net.mapPortForwardRules4);
1787
1788 const xml::ElementNode *pelmPortForwardRules6;
1789 if ((pelmPortForwardRules6 = pelmNet->findChildElement("PortForwarding6")))
1790 readNATForwardRulesMap(*pelmPortForwardRules6,
1791 net.mapPortForwardRules6);
1792
1793 llNATNetworks.push_back(net);
1794 }
1795 else
1796 throw ConfigFileError(this, pelmNet, N_("Required NATNetwork/@networkName, @gateway, @network,@advertiseDefaultIpv6Route , @needDhcp or @enabled attribute is missing"));
1797 }
1798 }
1799}
1800
1801/**
1802 * Creates \<USBDeviceSource\> nodes under the given parent element according to
1803 * the contents of the given USBDeviceSourcesList.
1804 *
1805 * @param elmParent
1806 * @param ll
1807 */
1808void MainConfigFile::buildUSBDeviceSources(xml::ElementNode &elmParent,
1809 const USBDeviceSourcesList &ll)
1810{
1811 for (USBDeviceSourcesList::const_iterator it = ll.begin();
1812 it != ll.end();
1813 ++it)
1814 {
1815 const USBDeviceSource &src = *it;
1816 xml::ElementNode *pelmSource = elmParent.createChild("USBDeviceSource");
1817 pelmSource->setAttribute("name", src.strName);
1818 pelmSource->setAttribute("backend", src.strBackend);
1819 pelmSource->setAttribute("address", src.strAddress);
1820
1821 /* Write the properties. */
1822 for (StringsMap::const_iterator itProp = src.properties.begin();
1823 itProp != src.properties.end();
1824 ++itProp)
1825 {
1826 xml::ElementNode *pelmProp = pelmSource->createChild("Property");
1827 pelmProp->setAttribute("name", itProp->first);
1828 pelmProp->setAttribute("value", itProp->second);
1829 }
1830 }
1831}
1832
1833/**
1834 * Reads \<USBDeviceFilter\> entries from under the given elmDeviceFilters node and
1835 * stores them in the given linklist. This is in ConfigFileBase because it's used
1836 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
1837 * filters).
1838 * @param elmDeviceSources
1839 * @param ll
1840 */
1841void MainConfigFile::readUSBDeviceSources(const xml::ElementNode &elmDeviceSources,
1842 USBDeviceSourcesList &ll)
1843{
1844 xml::NodesLoop nl1(elmDeviceSources, "USBDeviceSource");
1845 const xml::ElementNode *pelmChild;
1846 while ((pelmChild = nl1.forAllNodes()))
1847 {
1848 USBDeviceSource src;
1849
1850 if ( pelmChild->getAttributeValue("name", src.strName)
1851 && pelmChild->getAttributeValue("backend", src.strBackend)
1852 && pelmChild->getAttributeValue("address", src.strAddress))
1853 {
1854 // handle medium properties
1855 xml::NodesLoop nl2(*pelmChild, "Property");
1856 const xml::ElementNode *pelmSrcChild;
1857 while ((pelmSrcChild = nl2.forAllNodes()))
1858 {
1859 Utf8Str strPropName, strPropValue;
1860 if ( pelmSrcChild->getAttributeValue("name", strPropName)
1861 && pelmSrcChild->getAttributeValue("value", strPropValue) )
1862 src.properties[strPropName] = strPropValue;
1863 else
1864 throw ConfigFileError(this, pelmSrcChild, N_("Required USBDeviceSource/Property/@name or @value attribute is missing"));
1865 }
1866
1867 ll.push_back(src);
1868 }
1869 }
1870}
1871
1872/**
1873 * Converts old style Proxy settings from ExtraData/UI section.
1874 *
1875 * Saves proxy settings directly to systemProperties structure.
1876 *
1877 * @returns true if conversion was successfull, false if not.
1878 * @param strUIProxySettings The GUI settings string to convert.
1879 */
1880bool MainConfigFile::convertGuiProxySettings(const com::Utf8Str &strUIProxySettings)
1881{
1882 /*
1883 * Possible variants:
1884 * - "ProxyAuto,proxyserver.url,1080,authDisabled,,"
1885 * - "ProxyDisabled,proxyserver.url,1080,authDisabled,,"
1886 * - "ProxyEnabled,proxyserver.url,1080,authDisabled,,"
1887 *
1888 * Note! We only need to bother with the first three fields as the last
1889 * three was never really used or ever actually passed to the HTTP
1890 * client code.
1891 */
1892 /* First field: The proxy mode. */
1893 const char *psz = RTStrStripL(strUIProxySettings.c_str());
1894 static const struct { const char *psz; size_t cch; ProxyMode_T enmMode; } s_aModes[] =
1895 {
1896 { RT_STR_TUPLE("ProxyAuto"), ProxyMode_System },
1897 { RT_STR_TUPLE("ProxyDisabled"), ProxyMode_NoProxy },
1898 { RT_STR_TUPLE("ProxyEnabled"), ProxyMode_Manual },
1899 };
1900 for (size_t i = 0; i < RT_ELEMENTS(s_aModes); i++)
1901 if (RTStrNICmpAscii(psz, s_aModes[i].psz, s_aModes[i].cch) == 0)
1902 {
1903 systemProperties.uProxyMode = s_aModes[i].enmMode;
1904 psz = RTStrStripL(psz + s_aModes[i].cch);
1905 if (*psz == ',')
1906 {
1907 /* Second field: The proxy host, possibly fully fledged proxy URL. */
1908 psz = RTStrStripL(psz + 1);
1909 if (*psz != '\0' && *psz != ',')
1910 {
1911 const char *pszEnd = strchr(psz, ',');
1912 size_t cchHost = pszEnd ? pszEnd - psz : strlen(psz);
1913 while (cchHost > 0 && RT_C_IS_SPACE(psz[cchHost - 1]))
1914 cchHost--;
1915 systemProperties.strProxyUrl.assign(psz, cchHost);
1916 if (systemProperties.strProxyUrl.find("://") == RTCString::npos)
1917 systemProperties.strProxyUrl.replace(0, 0, "http://");
1918
1919 /* Third field: The proxy port. Defaulted to 1080 for all proxies.
1920 The new settings has type specific default ports. */
1921 uint16_t uPort = 1080;
1922 if (pszEnd)
1923 {
1924 int rc = RTStrToUInt16Ex(RTStrStripL(pszEnd + 1), NULL, 10, &uPort);
1925 if (RT_FAILURE(rc))
1926 uPort = 1080;
1927 }
1928 RTURIPARSED Parsed;
1929 int rc = RTUriParse(systemProperties.strProxyUrl.c_str(), &Parsed);
1930 if (RT_SUCCESS(rc))
1931 {
1932 if (Parsed.uAuthorityPort == UINT32_MAX)
1933 systemProperties.strProxyUrl.appendPrintf(systemProperties.strProxyUrl.endsWith(":")
1934 ? "%u" : ":%u", uPort);
1935 }
1936 else
1937 {
1938 LogRelFunc(("Dropping invalid proxy URL for %u: %s\n",
1939 systemProperties.uProxyMode, systemProperties.strProxyUrl.c_str()));
1940 systemProperties.strProxyUrl.setNull();
1941 }
1942 }
1943 /* else: don't bother with the rest if we haven't got a host. */
1944 }
1945 if ( systemProperties.strProxyUrl.isEmpty()
1946 && systemProperties.uProxyMode == ProxyMode_Manual)
1947 {
1948 systemProperties.uProxyMode = ProxyMode_System;
1949 return false;
1950 }
1951 return true;
1952 }
1953 LogRelFunc(("Unknown proxy type: %s\n", psz));
1954 return false;
1955}
1956
1957/**
1958 * Constructor.
1959 *
1960 * If pstrFilename is != NULL, this reads the given settings file into the member
1961 * variables and various substructures and lists. Otherwise, the member variables
1962 * are initialized with default values.
1963 *
1964 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1965 * the caller should catch; if this constructor does not throw, then the member
1966 * variables contain meaningful values (either from the file or defaults).
1967 *
1968 * @param pstrFilename
1969 */
1970MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
1971 : ConfigFileBase(pstrFilename)
1972{
1973 if (pstrFilename)
1974 {
1975 // the ConfigFileBase constructor has loaded the XML file, so now
1976 // we need only analyze what is in there
1977 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1978 const xml::ElementNode *pelmRootChild;
1979 bool fCopyProxySettingsFromExtraData = false;
1980 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1981 {
1982 if (pelmRootChild->nameEquals("Global"))
1983 {
1984 xml::NodesLoop nlGlobalChildren(*pelmRootChild);
1985 const xml::ElementNode *pelmGlobalChild;
1986 while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
1987 {
1988 if (pelmGlobalChild->nameEquals("SystemProperties"))
1989 {
1990 pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1991 pelmGlobalChild->getAttributeValue("LoggingLevel", systemProperties.strLoggingLevel);
1992 pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1993 if (!pelmGlobalChild->getAttributeValue("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary))
1994 // pre-1.11 used @remoteDisplayAuthLibrary instead
1995 pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strVRDEAuthLibrary);
1996 pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1997 pelmGlobalChild->getAttributeValue("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
1998 pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.uLogHistoryCount);
1999 pelmGlobalChild->getAttributeValue("autostartDatabasePath", systemProperties.strAutostartDatabasePath);
2000 pelmGlobalChild->getAttributeValue("defaultFrontend", systemProperties.strDefaultFrontend);
2001 pelmGlobalChild->getAttributeValue("exclusiveHwVirt", systemProperties.fExclusiveHwVirt);
2002 if (!pelmGlobalChild->getAttributeValue("proxyMode", systemProperties.uProxyMode))
2003 fCopyProxySettingsFromExtraData = true;
2004 pelmGlobalChild->getAttributeValue("proxyUrl", systemProperties.strProxyUrl);
2005 }
2006 else if (pelmGlobalChild->nameEquals("ExtraData"))
2007 readExtraData(*pelmGlobalChild, mapExtraDataItems);
2008 else if (pelmGlobalChild->nameEquals("MachineRegistry"))
2009 readMachineRegistry(*pelmGlobalChild);
2010 else if ( (pelmGlobalChild->nameEquals("MediaRegistry"))
2011 || ( (m->sv < SettingsVersion_v1_4)
2012 && (pelmGlobalChild->nameEquals("DiskRegistry"))
2013 )
2014 )
2015 readMediaRegistry(*pelmGlobalChild, mediaRegistry);
2016 else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
2017 {
2018 xml::NodesLoop nlLevel4(*pelmGlobalChild);
2019 const xml::ElementNode *pelmLevel4Child;
2020 while ((pelmLevel4Child = nlLevel4.forAllNodes()))
2021 {
2022 if (pelmLevel4Child->nameEquals("DHCPServers"))
2023 readDHCPServers(*pelmLevel4Child);
2024 if (pelmLevel4Child->nameEquals("NATNetworks"))
2025 readNATNetworks(*pelmLevel4Child);
2026 }
2027 }
2028 else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
2029 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
2030 else if (pelmGlobalChild->nameEquals("USBDeviceSources"))
2031 readUSBDeviceSources(*pelmGlobalChild, host.llUSBDeviceSources);
2032 }
2033 } // end if (pelmRootChild->nameEquals("Global"))
2034 }
2035
2036 if (fCopyProxySettingsFromExtraData)
2037 for (StringsMap::const_iterator it = mapExtraDataItems.begin(); it != mapExtraDataItems.end(); ++it)
2038 if (it->first.equals("GUI/ProxySettings"))
2039 {
2040 convertGuiProxySettings(it->second);
2041 break;
2042 }
2043
2044 clearDocument();
2045 }
2046
2047 // DHCP servers were introduced with settings version 1.7; if we're loading
2048 // from an older version OR this is a fresh install, then add one DHCP server
2049 // with default settings
2050 if ( (!llDhcpServers.size())
2051 && ( (!pstrFilename) // empty VirtualBox.xml file
2052 || (m->sv < SettingsVersion_v1_7) // upgrading from before 1.7
2053 )
2054 )
2055 {
2056 DHCPServer srv;
2057 srv.strNetworkName =
2058#ifdef RT_OS_WINDOWS
2059 "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
2060#else
2061 "HostInterfaceNetworking-vboxnet0";
2062#endif
2063 srv.strIPAddress = "192.168.56.100";
2064 srv.GlobalDhcpOptions[DhcpOpt_SubnetMask] = DhcpOptValue("255.255.255.0");
2065 srv.strIPLower = "192.168.56.101";
2066 srv.strIPUpper = "192.168.56.254";
2067 srv.fEnabled = true;
2068 llDhcpServers.push_back(srv);
2069 }
2070}
2071
2072void MainConfigFile::bumpSettingsVersionIfNeeded()
2073{
2074 if (m->sv < SettingsVersion_v1_16)
2075 {
2076 // VirtualBox 5.1 add support for additional USB device sources.
2077 if (!host.llUSBDeviceSources.empty())
2078 m->sv = SettingsVersion_v1_16;
2079 }
2080
2081 if (m->sv < SettingsVersion_v1_14)
2082 {
2083 // VirtualBox 4.3 adds NAT networks.
2084 if ( !llNATNetworks.empty())
2085 m->sv = SettingsVersion_v1_14;
2086 }
2087}
2088
2089
2090/**
2091 * Called from the IVirtualBox interface to write out VirtualBox.xml. This
2092 * builds an XML DOM tree and writes it out to disk.
2093 */
2094void MainConfigFile::write(const com::Utf8Str strFilename)
2095{
2096 bumpSettingsVersionIfNeeded();
2097
2098 m->strFilename = strFilename;
2099 specialBackupIfFirstBump();
2100 createStubDocument();
2101
2102 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
2103
2104 buildExtraData(*pelmGlobal, mapExtraDataItems);
2105
2106 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
2107 for (MachinesRegistry::const_iterator it = llMachines.begin();
2108 it != llMachines.end();
2109 ++it)
2110 {
2111 // <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"/>
2112 const MachineRegistryEntry &mre = *it;
2113 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
2114 pelmMachineEntry->setAttribute("uuid", mre.uuid.toStringCurly());
2115 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
2116 }
2117
2118 buildMediaRegistry(*pelmGlobal, mediaRegistry);
2119
2120 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
2121 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
2122 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
2123 it != llDhcpServers.end();
2124 ++it)
2125 {
2126 const DHCPServer &d = *it;
2127 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
2128 DhcpOptConstIterator itOpt;
2129 itOpt = d.GlobalDhcpOptions.find(DhcpOpt_SubnetMask);
2130
2131 pelmThis->setAttribute("networkName", d.strNetworkName);
2132 pelmThis->setAttribute("IPAddress", d.strIPAddress);
2133 if (itOpt != d.GlobalDhcpOptions.end())
2134 pelmThis->setAttribute("networkMask", itOpt->second.text);
2135 pelmThis->setAttribute("lowerIP", d.strIPLower);
2136 pelmThis->setAttribute("upperIP", d.strIPUpper);
2137 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
2138 /* We assume that if there're only 1 element it means that */
2139 size_t cOpt = d.GlobalDhcpOptions.size();
2140 /* We don't want duplicate validation check of networkMask here*/
2141 if ( ( itOpt == d.GlobalDhcpOptions.end()
2142 && cOpt > 0)
2143 || cOpt > 1)
2144 {
2145 xml::ElementNode *pelmOptions = pelmThis->createChild("Options");
2146 for (itOpt = d.GlobalDhcpOptions.begin();
2147 itOpt != d.GlobalDhcpOptions.end();
2148 ++itOpt)
2149 {
2150 if (itOpt->first == DhcpOpt_SubnetMask)
2151 continue;
2152
2153 xml::ElementNode *pelmOpt = pelmOptions->createChild("Option");
2154
2155 if (!pelmOpt)
2156 break;
2157
2158 pelmOpt->setAttribute("name", itOpt->first);
2159 pelmOpt->setAttribute("value", itOpt->second.text);
2160 if (itOpt->second.encoding != DhcpOptEncoding_Legacy)
2161 pelmOpt->setAttribute("encoding", (int)itOpt->second.encoding);
2162 }
2163 } /* end of if */
2164
2165 if (d.VmSlot2OptionsM.size() > 0)
2166 {
2167 VmSlot2OptionsConstIterator itVmSlot;
2168 DhcpOptConstIterator itOpt1;
2169 for(itVmSlot = d.VmSlot2OptionsM.begin();
2170 itVmSlot != d.VmSlot2OptionsM.end();
2171 ++itVmSlot)
2172 {
2173 xml::ElementNode *pelmCfg = pelmThis->createChild("Config");
2174 pelmCfg->setAttribute("vm-name", itVmSlot->first.VmName);
2175 pelmCfg->setAttribute("slot", (int32_t)itVmSlot->first.Slot);
2176
2177 for (itOpt1 = itVmSlot->second.begin();
2178 itOpt1 != itVmSlot->second.end();
2179 ++itOpt1)
2180 {
2181 xml::ElementNode *pelmOpt = pelmCfg->createChild("Option");
2182 pelmOpt->setAttribute("name", itOpt1->first);
2183 pelmOpt->setAttribute("value", itOpt1->second.text);
2184 if (itOpt1->second.encoding != DhcpOptEncoding_Legacy)
2185 pelmOpt->setAttribute("encoding", (int)itOpt1->second.encoding);
2186 }
2187 }
2188 } /* and of if */
2189
2190 }
2191
2192 xml::ElementNode *pelmNATNetworks;
2193 /* don't create entry if no NAT networks are registered. */
2194 if (!llNATNetworks.empty())
2195 {
2196 pelmNATNetworks = pelmNetserviceRegistry->createChild("NATNetworks");
2197 for (NATNetworksList::const_iterator it = llNATNetworks.begin();
2198 it != llNATNetworks.end();
2199 ++it)
2200 {
2201 const NATNetwork &n = *it;
2202 xml::ElementNode *pelmThis = pelmNATNetworks->createChild("NATNetwork");
2203 pelmThis->setAttribute("networkName", n.strNetworkName);
2204 pelmThis->setAttribute("network", n.strIPv4NetworkCidr);
2205 pelmThis->setAttribute("ipv6", n.fIPv6Enabled ? 1 : 0);
2206 pelmThis->setAttribute("ipv6prefix", n.strIPv6Prefix);
2207 pelmThis->setAttribute("advertiseDefaultIPv6Route", (n.fAdvertiseDefaultIPv6Route)? 1 : 0);
2208 pelmThis->setAttribute("needDhcp", (n.fNeedDhcpServer) ? 1 : 0);
2209 pelmThis->setAttribute("enabled", (n.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
2210 if (n.mapPortForwardRules4.size())
2211 {
2212 xml::ElementNode *pelmPf4 = pelmThis->createChild("PortForwarding4");
2213 buildNATForwardRulesMap(*pelmPf4, n.mapPortForwardRules4);
2214 }
2215 if (n.mapPortForwardRules6.size())
2216 {
2217 xml::ElementNode *pelmPf6 = pelmThis->createChild("PortForwarding6");
2218 buildNATForwardRulesMap(*pelmPf6, n.mapPortForwardRules6);
2219 }
2220
2221 if (n.llHostLoopbackOffsetList.size())
2222 {
2223 xml::ElementNode *pelmMappings = pelmThis->createChild("Mappings");
2224 buildNATLoopbacks(*pelmMappings, n.llHostLoopbackOffsetList);
2225
2226 }
2227 }
2228 }
2229
2230
2231 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
2232 if (systemProperties.strDefaultMachineFolder.length())
2233 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
2234 if (systemProperties.strLoggingLevel.length())
2235 pelmSysProps->setAttribute("LoggingLevel", systemProperties.strLoggingLevel);
2236 if (systemProperties.strDefaultHardDiskFormat.length())
2237 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
2238 if (systemProperties.strVRDEAuthLibrary.length())
2239 pelmSysProps->setAttribute("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary);
2240 if (systemProperties.strWebServiceAuthLibrary.length())
2241 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
2242 if (systemProperties.strDefaultVRDEExtPack.length())
2243 pelmSysProps->setAttribute("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
2244 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.uLogHistoryCount);
2245 if (systemProperties.strAutostartDatabasePath.length())
2246 pelmSysProps->setAttribute("autostartDatabasePath", systemProperties.strAutostartDatabasePath);
2247 if (systemProperties.strDefaultFrontend.length())
2248 pelmSysProps->setAttribute("defaultFrontend", systemProperties.strDefaultFrontend);
2249 if (systemProperties.strProxyUrl.length())
2250 pelmSysProps->setAttribute("proxyUrl", systemProperties.strProxyUrl);
2251 pelmSysProps->setAttribute("proxyMode", systemProperties.uProxyMode);
2252 pelmSysProps->setAttribute("exclusiveHwVirt", systemProperties.fExclusiveHwVirt);
2253
2254 buildUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
2255 host.llUSBDeviceFilters,
2256 true); // fHostMode
2257
2258 if (!host.llUSBDeviceSources.empty())
2259 buildUSBDeviceSources(*pelmGlobal->createChild("USBDeviceSources"),
2260 host.llUSBDeviceSources);
2261
2262 // now go write the XML
2263 xml::XmlFileWriter writer(*m->pDoc);
2264 writer.write(m->strFilename.c_str(), true /*fSafe*/);
2265
2266 m->fFileExists = true;
2267
2268 clearDocument();
2269}
2270
2271////////////////////////////////////////////////////////////////////////////////
2272//
2273// Machine XML structures
2274//
2275////////////////////////////////////////////////////////////////////////////////
2276
2277/**
2278 * Constructor. Needs to set sane defaults which stand the test of time.
2279 */
2280VRDESettings::VRDESettings() :
2281 fEnabled(true), // default for old VMs, for new ones it's false
2282 authType(AuthType_Null),
2283 ulAuthTimeout(5000),
2284 fAllowMultiConnection(false),
2285 fReuseSingleConnection(false)
2286{
2287}
2288
2289/**
2290 * Check if all settings have default values.
2291 */
2292bool VRDESettings::areDefaultSettings(SettingsVersion_T sv) const
2293{
2294 return (sv < SettingsVersion_v1_16 ? fEnabled : !fEnabled)
2295 && authType == AuthType_Null
2296 && (ulAuthTimeout == 5000 || ulAuthTimeout == 0)
2297 && strAuthLibrary.isEmpty()
2298 && !fAllowMultiConnection
2299 && !fReuseSingleConnection
2300 && strVrdeExtPack.isEmpty()
2301 && mapProperties.size() == 0;
2302}
2303
2304/**
2305 * Comparison operator. This gets called from MachineConfigFile::operator==,
2306 * which in turn gets called from Machine::saveSettings to figure out whether
2307 * machine settings have really changed and thus need to be written out to disk.
2308 */
2309bool VRDESettings::operator==(const VRDESettings& v) const
2310{
2311 return (this == &v)
2312 || ( fEnabled == v.fEnabled
2313 && authType == v.authType
2314 && ulAuthTimeout == v.ulAuthTimeout
2315 && strAuthLibrary == v.strAuthLibrary
2316 && fAllowMultiConnection == v.fAllowMultiConnection
2317 && fReuseSingleConnection == v.fReuseSingleConnection
2318 && strVrdeExtPack == v.strVrdeExtPack
2319 && mapProperties == v.mapProperties);
2320}
2321
2322/**
2323 * Constructor. Needs to set sane defaults which stand the test of time.
2324 */
2325BIOSSettings::BIOSSettings() :
2326 fACPIEnabled(true),
2327 fIOAPICEnabled(false),
2328 fLogoFadeIn(true),
2329 fLogoFadeOut(true),
2330 fPXEDebugEnabled(false),
2331 ulLogoDisplayTime(0),
2332 biosBootMenuMode(BIOSBootMenuMode_MessageAndMenu),
2333 apicMode(APICMode_APIC),
2334 llTimeOffset(0)
2335{
2336}
2337
2338/**
2339 * Check if all settings have default values.
2340 */
2341bool BIOSSettings::areDefaultSettings() const
2342{
2343 return fACPIEnabled
2344 && !fIOAPICEnabled
2345 && fLogoFadeIn
2346 && fLogoFadeOut
2347 && !fPXEDebugEnabled
2348 && ulLogoDisplayTime == 0
2349 && biosBootMenuMode == BIOSBootMenuMode_MessageAndMenu
2350 && apicMode == APICMode_APIC
2351 && llTimeOffset == 0
2352 && strLogoImagePath.isEmpty();
2353}
2354
2355/**
2356 * Comparison operator. This gets called from MachineConfigFile::operator==,
2357 * which in turn gets called from Machine::saveSettings to figure out whether
2358 * machine settings have really changed and thus need to be written out to disk.
2359 */
2360bool BIOSSettings::operator==(const BIOSSettings &d) const
2361{
2362 return (this == &d)
2363 || ( fACPIEnabled == d.fACPIEnabled
2364 && fIOAPICEnabled == d.fIOAPICEnabled
2365 && fLogoFadeIn == d.fLogoFadeIn
2366 && fLogoFadeOut == d.fLogoFadeOut
2367 && fPXEDebugEnabled == d.fPXEDebugEnabled
2368 && ulLogoDisplayTime == d.ulLogoDisplayTime
2369 && biosBootMenuMode == d.biosBootMenuMode
2370 && apicMode == d.apicMode
2371 && llTimeOffset == d.llTimeOffset
2372 && strLogoImagePath == d.strLogoImagePath);
2373}
2374
2375RecordingScreenSettings::RecordingScreenSettings(void)
2376{
2377 applyDefaults();
2378}
2379
2380RecordingScreenSettings::~RecordingScreenSettings()
2381{
2382
2383}
2384
2385/**
2386 * Applies the default settings.
2387 */
2388void RecordingScreenSettings::applyDefaults(void)
2389{
2390 /*
2391 * Set sensible defaults.
2392 */
2393
2394 fEnabled = false;
2395 enmDest = RecordingDestination_File;
2396 ulMaxTimeS = 0;
2397 strOptions = "";
2398 File.ulMaxSizeMB = 0;
2399 File.strName = "";
2400 Video.enmCodec = RecordingVideoCodec_VP8;
2401 Video.ulWidth = 1024;
2402 Video.ulHeight = 768;
2403 Video.ulRate = 512;
2404 Video.ulFPS = 25;
2405 Audio.enmAudioCodec = RecordingAudioCodec_Opus;
2406 Audio.cBits = 16;
2407 Audio.cChannels = 2;
2408 Audio.uHz = 22050;
2409
2410 featureMap[RecordingFeature_Video] = true;
2411 featureMap[RecordingFeature_Audio] = false; /** @todo Audio is not yet enabled by default. */
2412}
2413
2414/**
2415 * Check if all settings have default values.
2416 */
2417bool RecordingScreenSettings::areDefaultSettings(void) const
2418{
2419 return fEnabled == false
2420 && enmDest == RecordingDestination_File
2421 && ulMaxTimeS == 0
2422 && strOptions == ""
2423 && File.ulMaxSizeMB == 0
2424 && File.strName == ""
2425 && Video.enmCodec == RecordingVideoCodec_VP8
2426 && Video.ulWidth == 1024
2427 && Video.ulHeight == 768
2428 && Video.ulRate == 512
2429 && Video.ulFPS == 25
2430 && Audio.enmAudioCodec == RecordingAudioCodec_Opus
2431 && Audio.cBits == 16
2432 && Audio.cChannels == 2
2433 && Audio.uHz == 22050;
2434}
2435
2436/**
2437 * Returns if a certain recording feature is enabled or not.
2438 *
2439 * @returns \c true if the feature is enabled, \c false if not.
2440 * @param enmFeature Feature to check.
2441 */
2442bool RecordingScreenSettings::isFeatureEnabled(RecordingFeature_T enmFeature) const
2443{
2444 RecordingFeatureMap::const_iterator itFeature = featureMap.find(enmFeature);
2445 if (itFeature != featureMap.end())
2446 return itFeature->second;
2447
2448 return false;
2449}
2450
2451/**
2452 * Comparison operator. This gets called from MachineConfigFile::operator==,
2453 * which in turn gets called from Machine::saveSettings to figure out whether
2454 * machine settings have really changed and thus need to be written out to disk.
2455 */
2456bool RecordingScreenSettings::operator==(const RecordingScreenSettings &d) const
2457{
2458 return fEnabled == d.fEnabled
2459 && enmDest == d.enmDest
2460 && featureMap == d.featureMap
2461 && ulMaxTimeS == d.ulMaxTimeS
2462 && strOptions == d.strOptions
2463 && File.ulMaxSizeMB == d.File.ulMaxSizeMB
2464 && Video.enmCodec == d.Video.enmCodec
2465 && Video.ulWidth == d.Video.ulWidth
2466 && Video.ulHeight == d.Video.ulHeight
2467 && Video.ulRate == d.Video.ulRate
2468 && Video.ulFPS == d.Video.ulFPS
2469 && Audio.enmAudioCodec == d.Audio.enmAudioCodec
2470 && Audio.cBits == d.Audio.cBits
2471 && Audio.cChannels == d.Audio.cChannels
2472 && Audio.uHz == d.Audio.uHz;
2473}
2474
2475/**
2476 * Constructor. Needs to set sane defaults which stand the test of time.
2477 */
2478RecordingSettings::RecordingSettings()
2479{
2480 applyDefaults();
2481}
2482
2483/**
2484 * Applies the default settings.
2485 */
2486void RecordingSettings::applyDefaults(void)
2487{
2488 fEnabled = false;
2489
2490 mapScreens.clear();
2491
2492 try
2493 {
2494 /* Always add screen 0 to the default configuration. */
2495 RecordingScreenSettings screenSettings; /* Apply default settings for screen 0. */
2496 screenSettings.fEnabled = true; /* Enabled by default. */
2497 mapScreens[0] = screenSettings;
2498 }
2499 catch (std::bad_alloc &)
2500 {
2501 AssertFailed();
2502 }
2503}
2504
2505/**
2506 * Check if all settings have default values.
2507 */
2508bool RecordingSettings::areDefaultSettings() const
2509{
2510 const bool fDefault = fEnabled == false
2511 && mapScreens.size() == 1;
2512 if (!fDefault)
2513 return false;
2514
2515 RecordingScreenMap::const_iterator itScreen = mapScreens.begin();
2516 return itScreen->first == 0
2517 && itScreen->second.areDefaultSettings();
2518}
2519
2520/**
2521 * Comparison operator. This gets called from MachineConfigFile::operator==,
2522 * which in turn gets called from Machine::saveSettings to figure out whether
2523 * machine settings have really changed and thus need to be written out to disk.
2524 */
2525bool RecordingSettings::operator==(const RecordingSettings &d) const
2526{
2527 if (this == &d)
2528 return true;
2529
2530 if ( fEnabled != d.fEnabled
2531 || mapScreens.size() != d.mapScreens.size())
2532 return false;
2533
2534 RecordingScreenMap::const_iterator itScreen = mapScreens.begin();
2535 uint32_t i = 0;
2536 while (itScreen != mapScreens.end())
2537 {
2538 RecordingScreenMap::const_iterator itScreenThat = d.mapScreens.find(i);
2539 if (itScreen->second == itScreenThat->second)
2540 {
2541 /* Nothing to do in here (yet). */
2542 }
2543 else
2544 return false;
2545
2546 ++itScreen;
2547 ++i;
2548 }
2549
2550 return true;
2551}
2552
2553/**
2554 * Constructor. Needs to set sane defaults which stand the test of time.
2555 */
2556USBController::USBController() :
2557 enmType(USBControllerType_Null)
2558{
2559}
2560
2561/**
2562 * Comparison operator. This gets called from MachineConfigFile::operator==,
2563 * which in turn gets called from Machine::saveSettings to figure out whether
2564 * machine settings have really changed and thus need to be written out to disk.
2565 */
2566bool USBController::operator==(const USBController &u) const
2567{
2568 return (this == &u)
2569 || ( strName == u.strName
2570 && enmType == u.enmType);
2571}
2572
2573/**
2574 * Constructor. Needs to set sane defaults which stand the test of time.
2575 */
2576USB::USB()
2577{
2578}
2579
2580/**
2581 * Comparison operator. This gets called from MachineConfigFile::operator==,
2582 * which in turn gets called from Machine::saveSettings to figure out whether
2583 * machine settings have really changed and thus need to be written out to disk.
2584 */
2585bool USB::operator==(const USB &u) const
2586{
2587 return (this == &u)
2588 || ( llUSBControllers == u.llUSBControllers
2589 && llDeviceFilters == u.llDeviceFilters);
2590}
2591
2592/**
2593 * Constructor. Needs to set sane defaults which stand the test of time.
2594 */
2595NAT::NAT() :
2596 u32Mtu(0),
2597 u32SockRcv(0),
2598 u32SockSnd(0),
2599 u32TcpRcv(0),
2600 u32TcpSnd(0),
2601 fDNSPassDomain(true), /* historically this value is true */
2602 fDNSProxy(false),
2603 fDNSUseHostResolver(false),
2604 fAliasLog(false),
2605 fAliasProxyOnly(false),
2606 fAliasUseSamePorts(false)
2607{
2608}
2609
2610/**
2611 * Check if all DNS settings have default values.
2612 */
2613bool NAT::areDNSDefaultSettings() const
2614{
2615 return fDNSPassDomain && !fDNSProxy && !fDNSUseHostResolver;
2616}
2617
2618/**
2619 * Check if all Alias settings have default values.
2620 */
2621bool NAT::areAliasDefaultSettings() const
2622{
2623 return !fAliasLog && !fAliasProxyOnly && !fAliasUseSamePorts;
2624}
2625
2626/**
2627 * Check if all TFTP settings have default values.
2628 */
2629bool NAT::areTFTPDefaultSettings() const
2630{
2631 return strTFTPPrefix.isEmpty()
2632 && strTFTPBootFile.isEmpty()
2633 && strTFTPNextServer.isEmpty();
2634}
2635
2636/**
2637 * Check if all settings have default values.
2638 */
2639bool NAT::areDefaultSettings() const
2640{
2641 return strNetwork.isEmpty()
2642 && strBindIP.isEmpty()
2643 && u32Mtu == 0
2644 && u32SockRcv == 0
2645 && u32SockSnd == 0
2646 && u32TcpRcv == 0
2647 && u32TcpSnd == 0
2648 && areDNSDefaultSettings()
2649 && areAliasDefaultSettings()
2650 && areTFTPDefaultSettings()
2651 && mapRules.size() == 0;
2652}
2653
2654/**
2655 * Comparison operator. This gets called from MachineConfigFile::operator==,
2656 * which in turn gets called from Machine::saveSettings to figure out whether
2657 * machine settings have really changed and thus need to be written out to disk.
2658 */
2659bool NAT::operator==(const NAT &n) const
2660{
2661 return (this == &n)
2662 || ( strNetwork == n.strNetwork
2663 && strBindIP == n.strBindIP
2664 && u32Mtu == n.u32Mtu
2665 && u32SockRcv == n.u32SockRcv
2666 && u32SockSnd == n.u32SockSnd
2667 && u32TcpSnd == n.u32TcpSnd
2668 && u32TcpRcv == n.u32TcpRcv
2669 && strTFTPPrefix == n.strTFTPPrefix
2670 && strTFTPBootFile == n.strTFTPBootFile
2671 && strTFTPNextServer == n.strTFTPNextServer
2672 && fDNSPassDomain == n.fDNSPassDomain
2673 && fDNSProxy == n.fDNSProxy
2674 && fDNSUseHostResolver == n.fDNSUseHostResolver
2675 && fAliasLog == n.fAliasLog
2676 && fAliasProxyOnly == n.fAliasProxyOnly
2677 && fAliasUseSamePorts == n.fAliasUseSamePorts
2678 && mapRules == n.mapRules);
2679}
2680
2681/**
2682 * Constructor. Needs to set sane defaults which stand the test of time.
2683 */
2684NetworkAdapter::NetworkAdapter() :
2685 ulSlot(0),
2686 type(NetworkAdapterType_Am79C970A), // default for old VMs, for new ones it's Am79C973
2687 fEnabled(false),
2688 fCableConnected(false), // default for old VMs, for new ones it's true
2689 ulLineSpeed(0),
2690 enmPromiscModePolicy(NetworkAdapterPromiscModePolicy_Deny),
2691 fTraceEnabled(false),
2692 mode(NetworkAttachmentType_Null),
2693 ulBootPriority(0)
2694{
2695}
2696
2697/**
2698 * Check if all Generic Driver settings have default values.
2699 */
2700bool NetworkAdapter::areGenericDriverDefaultSettings() const
2701{
2702 return strGenericDriver.isEmpty()
2703 && genericProperties.size() == 0;
2704}
2705
2706/**
2707 * Check if all settings have default values.
2708 */
2709bool NetworkAdapter::areDefaultSettings(SettingsVersion_T sv) const
2710{
2711 // 5.0 and earlier had a default of fCableConnected=false, which doesn't
2712 // make a lot of sense (but it's a fact). Later versions don't save the
2713 // setting if it's at the default value and thus must get it right.
2714 return !fEnabled
2715 && strMACAddress.isEmpty()
2716 && ( (sv >= SettingsVersion_v1_16 && fCableConnected && type == NetworkAdapterType_Am79C973)
2717 || (sv < SettingsVersion_v1_16 && !fCableConnected && type == NetworkAdapterType_Am79C970A))
2718 && ulLineSpeed == 0
2719 && enmPromiscModePolicy == NetworkAdapterPromiscModePolicy_Deny
2720 && mode == NetworkAttachmentType_Null
2721 && nat.areDefaultSettings()
2722 && strBridgedName.isEmpty()
2723 && strInternalNetworkName.isEmpty()
2724 && strHostOnlyName.isEmpty()
2725 && areGenericDriverDefaultSettings()
2726 && strNATNetworkName.isEmpty();
2727}
2728
2729/**
2730 * Special check if settings of the non-current attachment type have default values.
2731 */
2732bool NetworkAdapter::areDisabledDefaultSettings() const
2733{
2734 return (mode != NetworkAttachmentType_NAT ? nat.areDefaultSettings() : true)
2735 && (mode != NetworkAttachmentType_Bridged ? strBridgedName.isEmpty() : true)
2736 && (mode != NetworkAttachmentType_Internal ? strInternalNetworkName.isEmpty() : true)
2737 && (mode != NetworkAttachmentType_HostOnly ? strHostOnlyName.isEmpty() : true)
2738 && (mode != NetworkAttachmentType_Generic ? areGenericDriverDefaultSettings() : true)
2739 && (mode != NetworkAttachmentType_NATNetwork ? strNATNetworkName.isEmpty() : true);
2740}
2741
2742/**
2743 * Comparison operator. This gets called from MachineConfigFile::operator==,
2744 * which in turn gets called from Machine::saveSettings to figure out whether
2745 * machine settings have really changed and thus need to be written out to disk.
2746 */
2747bool NetworkAdapter::operator==(const NetworkAdapter &n) const
2748{
2749 return (this == &n)
2750 || ( ulSlot == n.ulSlot
2751 && type == n.type
2752 && fEnabled == n.fEnabled
2753 && strMACAddress == n.strMACAddress
2754 && fCableConnected == n.fCableConnected
2755 && ulLineSpeed == n.ulLineSpeed
2756 && enmPromiscModePolicy == n.enmPromiscModePolicy
2757 && fTraceEnabled == n.fTraceEnabled
2758 && strTraceFile == n.strTraceFile
2759 && mode == n.mode
2760 && nat == n.nat
2761 && strBridgedName == n.strBridgedName
2762 && strHostOnlyName == n.strHostOnlyName
2763 && strInternalNetworkName == n.strInternalNetworkName
2764 && strGenericDriver == n.strGenericDriver
2765 && genericProperties == n.genericProperties
2766 && ulBootPriority == n.ulBootPriority
2767 && strBandwidthGroup == n.strBandwidthGroup);
2768}
2769
2770/**
2771 * Constructor. Needs to set sane defaults which stand the test of time.
2772 */
2773SerialPort::SerialPort() :
2774 ulSlot(0),
2775 fEnabled(false),
2776 ulIOBase(0x3f8),
2777 ulIRQ(4),
2778 portMode(PortMode_Disconnected),
2779 fServer(false),
2780 uartType(UartType_U16550A)
2781{
2782}
2783
2784/**
2785 * Comparison operator. This gets called from MachineConfigFile::operator==,
2786 * which in turn gets called from Machine::saveSettings to figure out whether
2787 * machine settings have really changed and thus need to be written out to disk.
2788 */
2789bool SerialPort::operator==(const SerialPort &s) const
2790{
2791 return (this == &s)
2792 || ( ulSlot == s.ulSlot
2793 && fEnabled == s.fEnabled
2794 && ulIOBase == s.ulIOBase
2795 && ulIRQ == s.ulIRQ
2796 && portMode == s.portMode
2797 && strPath == s.strPath
2798 && fServer == s.fServer
2799 && uartType == s.uartType);
2800}
2801
2802/**
2803 * Constructor. Needs to set sane defaults which stand the test of time.
2804 */
2805ParallelPort::ParallelPort() :
2806 ulSlot(0),
2807 fEnabled(false),
2808 ulIOBase(0x378),
2809 ulIRQ(7)
2810{
2811}
2812
2813/**
2814 * Comparison operator. This gets called from MachineConfigFile::operator==,
2815 * which in turn gets called from Machine::saveSettings to figure out whether
2816 * machine settings have really changed and thus need to be written out to disk.
2817 */
2818bool ParallelPort::operator==(const ParallelPort &s) const
2819{
2820 return (this == &s)
2821 || ( ulSlot == s.ulSlot
2822 && fEnabled == s.fEnabled
2823 && ulIOBase == s.ulIOBase
2824 && ulIRQ == s.ulIRQ
2825 && strPath == s.strPath);
2826}
2827
2828/**
2829 * Constructor. Needs to set sane defaults which stand the test of time.
2830 */
2831AudioAdapter::AudioAdapter() :
2832 fEnabled(true), // default for old VMs, for new ones it's false
2833 fEnabledIn(true), // default for old VMs, for new ones it's false
2834 fEnabledOut(true), // default for old VMs, for new ones it's false
2835 controllerType(AudioControllerType_AC97),
2836 codecType(AudioCodecType_STAC9700),
2837 driverType(AudioDriverType_Null)
2838{
2839}
2840
2841/**
2842 * Check if all settings have default values.
2843 */
2844bool AudioAdapter::areDefaultSettings(SettingsVersion_T sv) const
2845{
2846 return (sv < SettingsVersion_v1_16 ? false : !fEnabled)
2847 && (sv <= SettingsVersion_v1_16 ? fEnabledIn : !fEnabledIn)
2848 && (sv <= SettingsVersion_v1_16 ? fEnabledOut : !fEnabledOut)
2849 && fEnabledOut == true
2850 && controllerType == AudioControllerType_AC97
2851 && codecType == AudioCodecType_STAC9700
2852 && properties.size() == 0;
2853}
2854
2855/**
2856 * Comparison operator. This gets called from MachineConfigFile::operator==,
2857 * which in turn gets called from Machine::saveSettings to figure out whether
2858 * machine settings have really changed and thus need to be written out to disk.
2859 */
2860bool AudioAdapter::operator==(const AudioAdapter &a) const
2861{
2862 return (this == &a)
2863 || ( fEnabled == a.fEnabled
2864 && fEnabledIn == a.fEnabledIn
2865 && fEnabledOut == a.fEnabledOut
2866 && controllerType == a.controllerType
2867 && codecType == a.codecType
2868 && driverType == a.driverType
2869 && properties == a.properties);
2870}
2871
2872/**
2873 * Constructor. Needs to set sane defaults which stand the test of time.
2874 */
2875SharedFolder::SharedFolder() :
2876 fWritable(false),
2877 fAutoMount(false)
2878{
2879}
2880
2881/**
2882 * Comparison operator. This gets called from MachineConfigFile::operator==,
2883 * which in turn gets called from Machine::saveSettings to figure out whether
2884 * machine settings have really changed and thus need to be written out to disk.
2885 */
2886bool SharedFolder::operator==(const SharedFolder &g) const
2887{
2888 return (this == &g)
2889 || ( strName == g.strName
2890 && strHostPath == g.strHostPath
2891 && fWritable == g.fWritable
2892 && fAutoMount == g.fAutoMount
2893 && strAutoMountPoint == g.strAutoMountPoint);
2894}
2895
2896/**
2897 * Constructor. Needs to set sane defaults which stand the test of time.
2898 */
2899GuestProperty::GuestProperty() :
2900 timestamp(0)
2901{
2902}
2903
2904/**
2905 * Comparison operator. This gets called from MachineConfigFile::operator==,
2906 * which in turn gets called from Machine::saveSettings to figure out whether
2907 * machine settings have really changed and thus need to be written out to disk.
2908 */
2909bool GuestProperty::operator==(const GuestProperty &g) const
2910{
2911 return (this == &g)
2912 || ( strName == g.strName
2913 && strValue == g.strValue
2914 && timestamp == g.timestamp
2915 && strFlags == g.strFlags);
2916}
2917
2918/**
2919 * Constructor. Needs to set sane defaults which stand the test of time.
2920 */
2921CpuIdLeaf::CpuIdLeaf() :
2922 idx(UINT32_MAX),
2923 idxSub(0),
2924 uEax(0),
2925 uEbx(0),
2926 uEcx(0),
2927 uEdx(0)
2928{
2929}
2930
2931/**
2932 * Comparison operator. This gets called from MachineConfigFile::operator==,
2933 * which in turn gets called from Machine::saveSettings to figure out whether
2934 * machine settings have really changed and thus need to be written out to disk.
2935 */
2936bool CpuIdLeaf::operator==(const CpuIdLeaf &c) const
2937{
2938 return (this == &c)
2939 || ( idx == c.idx
2940 && idxSub == c.idxSub
2941 && uEax == c.uEax
2942 && uEbx == c.uEbx
2943 && uEcx == c.uEcx
2944 && uEdx == c.uEdx);
2945}
2946
2947/**
2948 * Constructor. Needs to set sane defaults which stand the test of time.
2949 */
2950Cpu::Cpu() :
2951 ulId(UINT32_MAX)
2952{
2953}
2954
2955/**
2956 * Comparison operator. This gets called from MachineConfigFile::operator==,
2957 * which in turn gets called from Machine::saveSettings to figure out whether
2958 * machine settings have really changed and thus need to be written out to disk.
2959 */
2960bool Cpu::operator==(const Cpu &c) const
2961{
2962 return (this == &c)
2963 || (ulId == c.ulId);
2964}
2965
2966/**
2967 * Constructor. Needs to set sane defaults which stand the test of time.
2968 */
2969BandwidthGroup::BandwidthGroup() :
2970 cMaxBytesPerSec(0),
2971 enmType(BandwidthGroupType_Null)
2972{
2973}
2974
2975/**
2976 * Comparison operator. This gets called from MachineConfigFile::operator==,
2977 * which in turn gets called from Machine::saveSettings to figure out whether
2978 * machine settings have really changed and thus need to be written out to disk.
2979 */
2980bool BandwidthGroup::operator==(const BandwidthGroup &i) const
2981{
2982 return (this == &i)
2983 || ( strName == i.strName
2984 && cMaxBytesPerSec == i.cMaxBytesPerSec
2985 && enmType == i.enmType);
2986}
2987
2988/**
2989 * IOSettings constructor.
2990 */
2991IOSettings::IOSettings() :
2992 fIOCacheEnabled(true),
2993 ulIOCacheSize(5)
2994{
2995}
2996
2997/**
2998 * Check if all IO Cache settings have default values.
2999 */
3000bool IOSettings::areIOCacheDefaultSettings() const
3001{
3002 return fIOCacheEnabled
3003 && ulIOCacheSize == 5;
3004}
3005
3006/**
3007 * Check if all settings have default values.
3008 */
3009bool IOSettings::areDefaultSettings() const
3010{
3011 return areIOCacheDefaultSettings()
3012 && llBandwidthGroups.size() == 0;
3013}
3014
3015/**
3016 * Comparison operator. This gets called from MachineConfigFile::operator==,
3017 * which in turn gets called from Machine::saveSettings to figure out whether
3018 * machine settings have really changed and thus need to be written out to disk.
3019 */
3020bool IOSettings::operator==(const IOSettings &i) const
3021{
3022 return (this == &i)
3023 || ( fIOCacheEnabled == i.fIOCacheEnabled
3024 && ulIOCacheSize == i.ulIOCacheSize
3025 && llBandwidthGroups == i.llBandwidthGroups);
3026}
3027
3028/**
3029 * Constructor. Needs to set sane defaults which stand the test of time.
3030 */
3031HostPCIDeviceAttachment::HostPCIDeviceAttachment() :
3032 uHostAddress(0),
3033 uGuestAddress(0)
3034{
3035}
3036
3037/**
3038 * Comparison operator. This gets called from MachineConfigFile::operator==,
3039 * which in turn gets called from Machine::saveSettings to figure out whether
3040 * machine settings have really changed and thus need to be written out to disk.
3041 */
3042bool HostPCIDeviceAttachment::operator==(const HostPCIDeviceAttachment &a) const
3043{
3044 return (this == &a)
3045 || ( uHostAddress == a.uHostAddress
3046 && uGuestAddress == a.uGuestAddress
3047 && strDeviceName == a.strDeviceName);
3048}
3049
3050
3051/**
3052 * Constructor. Needs to set sane defaults which stand the test of time.
3053 */
3054Hardware::Hardware() :
3055 strVersion("1"),
3056 fHardwareVirt(true),
3057 fNestedPaging(true),
3058 fVPID(true),
3059 fUnrestrictedExecution(true),
3060 fHardwareVirtForce(false),
3061 fUseNativeApi(false),
3062 fTripleFaultReset(false),
3063 fPAE(false),
3064 fAPIC(true),
3065 fX2APIC(false),
3066 fIBPBOnVMExit(false),
3067 fIBPBOnVMEntry(false),
3068 fSpecCtrl(false),
3069 fSpecCtrlByHost(false),
3070 fNestedHWVirt(false),
3071 enmLongMode(HC_ARCH_BITS == 64 ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled),
3072 cCPUs(1),
3073 fCpuHotPlug(false),
3074 fHPETEnabled(false),
3075 ulCpuExecutionCap(100),
3076 uCpuIdPortabilityLevel(0),
3077 strCpuProfile("host"),
3078 ulMemorySizeMB((uint32_t)-1),
3079 graphicsControllerType(GraphicsControllerType_VBoxVGA),
3080 ulVRAMSizeMB(8),
3081 cMonitors(1),
3082 fAccelerate3D(false),
3083 fAccelerate2DVideo(false),
3084 firmwareType(FirmwareType_BIOS),
3085 pointingHIDType(PointingHIDType_PS2Mouse),
3086 keyboardHIDType(KeyboardHIDType_PS2Keyboard),
3087 chipsetType(ChipsetType_PIIX3),
3088 paravirtProvider(ParavirtProvider_Legacy), // default for old VMs, for new ones it's ParavirtProvider_Default
3089 strParavirtDebug(""),
3090 fEmulatedUSBCardReader(false),
3091 clipboardMode(ClipboardMode_Disabled),
3092 dndMode(DnDMode_Disabled),
3093 ulMemoryBalloonSize(0),
3094 fPageFusionEnabled(false)
3095{
3096 mapBootOrder[0] = DeviceType_Floppy;
3097 mapBootOrder[1] = DeviceType_DVD;
3098 mapBootOrder[2] = DeviceType_HardDisk;
3099
3100 /* The default value for PAE depends on the host:
3101 * - 64 bits host -> always true
3102 * - 32 bits host -> true for Windows & Darwin (masked off if the host cpu doesn't support it anyway)
3103 */
3104#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
3105 fPAE = true;
3106#endif
3107
3108 /* The default value of large page supports depends on the host:
3109 * - 64 bits host -> true, unless it's Linux (pending further prediction work due to excessively expensive large page allocations)
3110 * - 32 bits host -> false
3111 */
3112#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
3113 fLargePages = true;
3114#else
3115 /* Not supported on 32 bits hosts. */
3116 fLargePages = false;
3117#endif
3118}
3119
3120/**
3121 * Check if all Paravirt settings have default values.
3122 */
3123bool Hardware::areParavirtDefaultSettings(SettingsVersion_T sv) const
3124{
3125 // 5.0 didn't save the paravirt settings if it is ParavirtProvider_Legacy,
3126 // so this default must be kept. Later versions don't save the setting if
3127 // it's at the default value.
3128 return ( (sv >= SettingsVersion_v1_16 && paravirtProvider == ParavirtProvider_Default)
3129 || (sv < SettingsVersion_v1_16 && paravirtProvider == ParavirtProvider_Legacy))
3130 && strParavirtDebug.isEmpty();
3131}
3132
3133/**
3134 * Check if all Boot Order settings have default values.
3135 */
3136bool Hardware::areBootOrderDefaultSettings() const
3137{
3138 BootOrderMap::const_iterator it0 = mapBootOrder.find(0);
3139 BootOrderMap::const_iterator it1 = mapBootOrder.find(1);
3140 BootOrderMap::const_iterator it2 = mapBootOrder.find(2);
3141 BootOrderMap::const_iterator it3 = mapBootOrder.find(3);
3142 return ( mapBootOrder.size() == 3
3143 || ( mapBootOrder.size() == 4
3144 && (it3 != mapBootOrder.end() && it3->second == DeviceType_Null)))
3145 && (it0 != mapBootOrder.end() && it0->second == DeviceType_Floppy)
3146 && (it1 != mapBootOrder.end() && it1->second == DeviceType_DVD)
3147 && (it2 != mapBootOrder.end() && it2->second == DeviceType_HardDisk);
3148}
3149
3150/**
3151 * Check if all Display settings have default values.
3152 */
3153bool Hardware::areDisplayDefaultSettings() const
3154{
3155 return graphicsControllerType == GraphicsControllerType_VBoxVGA
3156 && ulVRAMSizeMB == 8
3157 && cMonitors <= 1
3158 && !fAccelerate3D
3159 && !fAccelerate2DVideo;
3160}
3161
3162/**
3163 * Check if all Network Adapter settings have default values.
3164 */
3165bool Hardware::areAllNetworkAdaptersDefaultSettings(SettingsVersion_T sv) const
3166{
3167 for (NetworkAdaptersList::const_iterator it = llNetworkAdapters.begin();
3168 it != llNetworkAdapters.end();
3169 ++it)
3170 {
3171 if (!it->areDefaultSettings(sv))
3172 return false;
3173 }
3174 return true;
3175}
3176
3177/**
3178 * Comparison operator. This gets called from MachineConfigFile::operator==,
3179 * which in turn gets called from Machine::saveSettings to figure out whether
3180 * machine settings have really changed and thus need to be written out to disk.
3181 */
3182bool Hardware::operator==(const Hardware& h) const
3183{
3184 return (this == &h)
3185 || ( strVersion == h.strVersion
3186 && uuid == h.uuid
3187 && fHardwareVirt == h.fHardwareVirt
3188 && fNestedPaging == h.fNestedPaging
3189 && fLargePages == h.fLargePages
3190 && fVPID == h.fVPID
3191 && fUnrestrictedExecution == h.fUnrestrictedExecution
3192 && fHardwareVirtForce == h.fHardwareVirtForce
3193 && fUseNativeApi == h.fUseNativeApi
3194 && fPAE == h.fPAE
3195 && enmLongMode == h.enmLongMode
3196 && fTripleFaultReset == h.fTripleFaultReset
3197 && fAPIC == h.fAPIC
3198 && fX2APIC == h.fX2APIC
3199 && fIBPBOnVMExit == h.fIBPBOnVMExit
3200 && fIBPBOnVMEntry == h.fIBPBOnVMEntry
3201 && fSpecCtrl == h.fSpecCtrl
3202 && fSpecCtrlByHost == h.fSpecCtrlByHost
3203 && fNestedHWVirt == h.fNestedHWVirt
3204 && cCPUs == h.cCPUs
3205 && fCpuHotPlug == h.fCpuHotPlug
3206 && ulCpuExecutionCap == h.ulCpuExecutionCap
3207 && uCpuIdPortabilityLevel == h.uCpuIdPortabilityLevel
3208 && strCpuProfile == h.strCpuProfile
3209 && fHPETEnabled == h.fHPETEnabled
3210 && llCpus == h.llCpus
3211 && llCpuIdLeafs == h.llCpuIdLeafs
3212 && ulMemorySizeMB == h.ulMemorySizeMB
3213 && mapBootOrder == h.mapBootOrder
3214 && graphicsControllerType == h.graphicsControllerType
3215 && ulVRAMSizeMB == h.ulVRAMSizeMB
3216 && cMonitors == h.cMonitors
3217 && fAccelerate3D == h.fAccelerate3D
3218 && fAccelerate2DVideo == h.fAccelerate2DVideo
3219 && firmwareType == h.firmwareType
3220 && pointingHIDType == h.pointingHIDType
3221 && keyboardHIDType == h.keyboardHIDType
3222 && chipsetType == h.chipsetType
3223 && paravirtProvider == h.paravirtProvider
3224 && strParavirtDebug == h.strParavirtDebug
3225 && fEmulatedUSBCardReader == h.fEmulatedUSBCardReader
3226 && vrdeSettings == h.vrdeSettings
3227 && biosSettings == h.biosSettings
3228 && usbSettings == h.usbSettings
3229 && llNetworkAdapters == h.llNetworkAdapters
3230 && llSerialPorts == h.llSerialPorts
3231 && llParallelPorts == h.llParallelPorts
3232 && audioAdapter == h.audioAdapter
3233 && storage == h.storage
3234 && llSharedFolders == h.llSharedFolders
3235 && clipboardMode == h.clipboardMode
3236 && dndMode == h.dndMode
3237 && ulMemoryBalloonSize == h.ulMemoryBalloonSize
3238 && fPageFusionEnabled == h.fPageFusionEnabled
3239 && llGuestProperties == h.llGuestProperties
3240 && ioSettings == h.ioSettings
3241 && pciAttachments == h.pciAttachments
3242 && strDefaultFrontend == h.strDefaultFrontend);
3243}
3244
3245/**
3246 * Constructor. Needs to set sane defaults which stand the test of time.
3247 */
3248AttachedDevice::AttachedDevice() :
3249 deviceType(DeviceType_Null),
3250 fPassThrough(false),
3251 fTempEject(false),
3252 fNonRotational(false),
3253 fDiscard(false),
3254 fHotPluggable(false),
3255 lPort(0),
3256 lDevice(0)
3257{
3258}
3259
3260/**
3261 * Comparison operator. This gets called from MachineConfigFile::operator==,
3262 * which in turn gets called from Machine::saveSettings to figure out whether
3263 * machine settings have really changed and thus need to be written out to disk.
3264 */
3265bool AttachedDevice::operator==(const AttachedDevice &a) const
3266{
3267 return (this == &a)
3268 || ( deviceType == a.deviceType
3269 && fPassThrough == a.fPassThrough
3270 && fTempEject == a.fTempEject
3271 && fNonRotational == a.fNonRotational
3272 && fDiscard == a.fDiscard
3273 && fHotPluggable == a.fHotPluggable
3274 && lPort == a.lPort
3275 && lDevice == a.lDevice
3276 && uuid == a.uuid
3277 && strHostDriveSrc == a.strHostDriveSrc
3278 && strBwGroup == a.strBwGroup);
3279}
3280
3281/**
3282 * Constructor. Needs to set sane defaults which stand the test of time.
3283 */
3284StorageController::StorageController() :
3285 storageBus(StorageBus_IDE),
3286 controllerType(StorageControllerType_PIIX3),
3287 ulPortCount(2),
3288 ulInstance(0),
3289 fUseHostIOCache(true),
3290 fBootable(true)
3291{
3292}
3293
3294/**
3295 * Comparison operator. This gets called from MachineConfigFile::operator==,
3296 * which in turn gets called from Machine::saveSettings to figure out whether
3297 * machine settings have really changed and thus need to be written out to disk.
3298 */
3299bool StorageController::operator==(const StorageController &s) const
3300{
3301 return (this == &s)
3302 || ( strName == s.strName
3303 && storageBus == s.storageBus
3304 && controllerType == s.controllerType
3305 && ulPortCount == s.ulPortCount
3306 && ulInstance == s.ulInstance
3307 && fUseHostIOCache == s.fUseHostIOCache
3308 && llAttachedDevices == s.llAttachedDevices);
3309}
3310
3311/**
3312 * Comparison operator. This gets called from MachineConfigFile::operator==,
3313 * which in turn gets called from Machine::saveSettings to figure out whether
3314 * machine settings have really changed and thus need to be written out to disk.
3315 */
3316bool Storage::operator==(const Storage &s) const
3317{
3318 return (this == &s)
3319 || (llStorageControllers == s.llStorageControllers); // deep compare
3320}
3321
3322/**
3323 * Constructor. Needs to set sane defaults which stand the test of time.
3324 */
3325Debugging::Debugging() :
3326 fTracingEnabled(false),
3327 fAllowTracingToAccessVM(false),
3328 strTracingConfig()
3329{
3330}
3331
3332/**
3333 * Check if all settings have default values.
3334 */
3335bool Debugging::areDefaultSettings() const
3336{
3337 return !fTracingEnabled
3338 && !fAllowTracingToAccessVM
3339 && strTracingConfig.isEmpty();
3340}
3341
3342/**
3343 * Comparison operator. This gets called from MachineConfigFile::operator==,
3344 * which in turn gets called from Machine::saveSettings to figure out whether
3345 * machine settings have really changed and thus need to be written out to disk.
3346 */
3347bool Debugging::operator==(const Debugging &d) const
3348{
3349 return (this == &d)
3350 || ( fTracingEnabled == d.fTracingEnabled
3351 && fAllowTracingToAccessVM == d.fAllowTracingToAccessVM
3352 && strTracingConfig == d.strTracingConfig);
3353}
3354
3355/**
3356 * Constructor. Needs to set sane defaults which stand the test of time.
3357 */
3358Autostart::Autostart() :
3359 fAutostartEnabled(false),
3360 uAutostartDelay(0),
3361 enmAutostopType(AutostopType_Disabled)
3362{
3363}
3364
3365/**
3366 * Check if all settings have default values.
3367 */
3368bool Autostart::areDefaultSettings() const
3369{
3370 return !fAutostartEnabled
3371 && !uAutostartDelay
3372 && enmAutostopType == AutostopType_Disabled;
3373}
3374
3375/**
3376 * Comparison operator. This gets called from MachineConfigFile::operator==,
3377 * which in turn gets called from Machine::saveSettings to figure out whether
3378 * machine settings have really changed and thus need to be written out to disk.
3379 */
3380bool Autostart::operator==(const Autostart &a) const
3381{
3382 return (this == &a)
3383 || ( fAutostartEnabled == a.fAutostartEnabled
3384 && uAutostartDelay == a.uAutostartDelay
3385 && enmAutostopType == a.enmAutostopType);
3386}
3387
3388/**
3389 * Constructor. Needs to set sane defaults which stand the test of time.
3390 */
3391Snapshot::Snapshot()
3392{
3393 RTTimeSpecSetNano(&timestamp, 0);
3394}
3395
3396/**
3397 * Comparison operator. This gets called from MachineConfigFile::operator==,
3398 * which in turn gets called from Machine::saveSettings to figure out whether
3399 * machine settings have really changed and thus need to be written out to disk.
3400 */
3401bool Snapshot::operator==(const Snapshot &s) const
3402{
3403 return (this == &s)
3404 || ( uuid == s.uuid
3405 && strName == s.strName
3406 && strDescription == s.strDescription
3407 && RTTimeSpecIsEqual(&timestamp, &s.timestamp)
3408 && strStateFile == s.strStateFile
3409 && hardware == s.hardware // deep compare
3410 && llChildSnapshots == s.llChildSnapshots // deep compare
3411 && debugging == s.debugging
3412 && autostart == s.autostart);
3413}
3414
3415const struct Snapshot settings::Snapshot::Empty; /* default ctor is OK */
3416
3417/**
3418 * Constructor. Needs to set sane defaults which stand the test of time.
3419 */
3420MachineUserData::MachineUserData() :
3421 fDirectoryIncludesUUID(false),
3422 fNameSync(true),
3423 fTeleporterEnabled(false),
3424 uTeleporterPort(0),
3425 enmFaultToleranceState(FaultToleranceState_Inactive),
3426 uFaultTolerancePort(0),
3427 uFaultToleranceInterval(0),
3428 fRTCUseUTC(false),
3429 strVMPriority()
3430{
3431 llGroups.push_back("/");
3432}
3433
3434/**
3435 * Comparison operator. This gets called from MachineConfigFile::operator==,
3436 * which in turn gets called from Machine::saveSettings to figure out whether
3437 * machine settings have really changed and thus need to be written out to disk.
3438 */
3439bool MachineUserData::operator==(const MachineUserData &c) const
3440{
3441 return (this == &c)
3442 || ( strName == c.strName
3443 && fDirectoryIncludesUUID == c.fDirectoryIncludesUUID
3444 && fNameSync == c.fNameSync
3445 && strDescription == c.strDescription
3446 && llGroups == c.llGroups
3447 && strOsType == c.strOsType
3448 && strSnapshotFolder == c.strSnapshotFolder
3449 && fTeleporterEnabled == c.fTeleporterEnabled
3450 && uTeleporterPort == c.uTeleporterPort
3451 && strTeleporterAddress == c.strTeleporterAddress
3452 && strTeleporterPassword == c.strTeleporterPassword
3453 && enmFaultToleranceState == c.enmFaultToleranceState
3454 && uFaultTolerancePort == c.uFaultTolerancePort
3455 && uFaultToleranceInterval == c.uFaultToleranceInterval
3456 && strFaultToleranceAddress == c.strFaultToleranceAddress
3457 && strFaultTolerancePassword == c.strFaultTolerancePassword
3458 && fRTCUseUTC == c.fRTCUseUTC
3459 && ovIcon == c.ovIcon
3460 && strVMPriority == c.strVMPriority);
3461}
3462
3463
3464////////////////////////////////////////////////////////////////////////////////
3465//
3466// MachineConfigFile
3467//
3468////////////////////////////////////////////////////////////////////////////////
3469
3470/**
3471 * Constructor.
3472 *
3473 * If pstrFilename is != NULL, this reads the given settings file into the member
3474 * variables and various substructures and lists. Otherwise, the member variables
3475 * are initialized with default values.
3476 *
3477 * Throws variants of xml::Error for I/O, XML and logical content errors, which
3478 * the caller should catch; if this constructor does not throw, then the member
3479 * variables contain meaningful values (either from the file or defaults).
3480 *
3481 * @param pstrFilename
3482 */
3483MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
3484 : ConfigFileBase(pstrFilename),
3485 fCurrentStateModified(true),
3486 fAborted(false)
3487{
3488 RTTimeNow(&timeLastStateChange);
3489
3490 if (pstrFilename)
3491 {
3492 // the ConfigFileBase constructor has loaded the XML file, so now
3493 // we need only analyze what is in there
3494
3495 xml::NodesLoop nlRootChildren(*m->pelmRoot);
3496 const xml::ElementNode *pelmRootChild;
3497 while ((pelmRootChild = nlRootChildren.forAllNodes()))
3498 {
3499 if (pelmRootChild->nameEquals("Machine"))
3500 readMachine(*pelmRootChild);
3501 }
3502
3503 // clean up memory allocated by XML engine
3504 clearDocument();
3505 }
3506}
3507
3508/**
3509 * Public routine which returns true if this machine config file can have its
3510 * own media registry (which is true for settings version v1.11 and higher,
3511 * i.e. files created by VirtualBox 4.0 and higher).
3512 * @return
3513 */
3514bool MachineConfigFile::canHaveOwnMediaRegistry() const
3515{
3516 return (m->sv >= SettingsVersion_v1_11);
3517}
3518
3519/**
3520 * Public routine which allows for importing machine XML from an external DOM tree.
3521 * Use this after having called the constructor with a NULL argument.
3522 *
3523 * This is used by the OVF code if a <vbox:Machine> element has been encountered
3524 * in an OVF VirtualSystem element.
3525 *
3526 * @param elmMachine
3527 */
3528void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine)
3529{
3530 // Ideally the version should be mandatory, but since VirtualBox didn't
3531 // care about it until 5.1 came with different defaults, there are OVF
3532 // files created by magicians (not using VirtualBox, which always wrote it)
3533 // which lack this information. Let's hope that they learn to add the
3534 // version when they switch to the newer settings style/defaults of 5.1.
3535 if (!(elmMachine.getAttributeValue("version", m->strSettingsVersionFull)))
3536 m->strSettingsVersionFull = VBOX_XML_IMPORT_VERSION_FULL;
3537
3538 LogRel(("Import settings with version \"%s\"\n", m->strSettingsVersionFull.c_str()));
3539
3540 m->sv = parseVersion(m->strSettingsVersionFull, &elmMachine);
3541
3542 // remember the settings version we read in case it gets upgraded later,
3543 // so we know when to make backups
3544 m->svRead = m->sv;
3545
3546 readMachine(elmMachine);
3547}
3548
3549/**
3550 * Comparison operator. This gets called from Machine::saveSettings to figure out
3551 * whether machine settings have really changed and thus need to be written out to disk.
3552 *
3553 * Even though this is called operator==, this does NOT compare all fields; the "equals"
3554 * should be understood as "has the same machine config as". The following fields are
3555 * NOT compared:
3556 * -- settings versions and file names inherited from ConfigFileBase;
3557 * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
3558 *
3559 * The "deep" comparisons marked below will invoke the operator== functions of the
3560 * structs defined in this file, which may in turn go into comparing lists of
3561 * other structures. As a result, invoking this can be expensive, but it's
3562 * less expensive than writing out XML to disk.
3563 */
3564bool MachineConfigFile::operator==(const MachineConfigFile &c) const
3565{
3566 return (this == &c)
3567 || ( uuid == c.uuid
3568 && machineUserData == c.machineUserData
3569 && strStateFile == c.strStateFile
3570 && uuidCurrentSnapshot == c.uuidCurrentSnapshot
3571 // skip fCurrentStateModified!
3572 && RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange)
3573 && fAborted == c.fAborted
3574 && hardwareMachine == c.hardwareMachine // this one's deep
3575 && mediaRegistry == c.mediaRegistry // this one's deep
3576 // skip mapExtraDataItems! there is no old state available as it's always forced
3577 && llFirstSnapshot == c.llFirstSnapshot); // this one's deep
3578}
3579
3580/**
3581 * Called from MachineConfigFile::readHardware() to read cpu information.
3582 * @param elmCpu
3583 * @param ll
3584 */
3585void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
3586 CpuList &ll)
3587{
3588 xml::NodesLoop nl1(elmCpu, "Cpu");
3589 const xml::ElementNode *pelmCpu;
3590 while ((pelmCpu = nl1.forAllNodes()))
3591 {
3592 Cpu cpu;
3593
3594 if (!pelmCpu->getAttributeValue("id", cpu.ulId))
3595 throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
3596
3597 ll.push_back(cpu);
3598 }
3599}
3600
3601/**
3602 * Called from MachineConfigFile::readHardware() to cpuid information.
3603 * @param elmCpuid
3604 * @param ll
3605 */
3606void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid,
3607 CpuIdLeafsList &ll)
3608{
3609 xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf");
3610 const xml::ElementNode *pelmCpuIdLeaf;
3611 while ((pelmCpuIdLeaf = nl1.forAllNodes()))
3612 {
3613 CpuIdLeaf leaf;
3614
3615 if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.idx))
3616 throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing"));
3617
3618 if (!pelmCpuIdLeaf->getAttributeValue("subleaf", leaf.idxSub))
3619 leaf.idxSub = 0;
3620 pelmCpuIdLeaf->getAttributeValue("eax", leaf.uEax);
3621 pelmCpuIdLeaf->getAttributeValue("ebx", leaf.uEbx);
3622 pelmCpuIdLeaf->getAttributeValue("ecx", leaf.uEcx);
3623 pelmCpuIdLeaf->getAttributeValue("edx", leaf.uEdx);
3624
3625 ll.push_back(leaf);
3626 }
3627}
3628
3629/**
3630 * Called from MachineConfigFile::readHardware() to network information.
3631 * @param elmNetwork
3632 * @param ll
3633 */
3634void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
3635 NetworkAdaptersList &ll)
3636{
3637 xml::NodesLoop nl1(elmNetwork, "Adapter");
3638 const xml::ElementNode *pelmAdapter;
3639 while ((pelmAdapter = nl1.forAllNodes()))
3640 {
3641 NetworkAdapter nic;
3642
3643 if (m->sv >= SettingsVersion_v1_16)
3644 {
3645 /* Starting with VirtualBox 5.1 the default is cable connected and
3646 * PCnet-FAST III. Needs to match NetworkAdapter.areDefaultSettings(). */
3647 nic.fCableConnected = true;
3648 nic.type = NetworkAdapterType_Am79C973;
3649 }
3650
3651 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
3652 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
3653
3654 Utf8Str strTemp;
3655 if (pelmAdapter->getAttributeValue("type", strTemp))
3656 {
3657 if (strTemp == "Am79C970A")
3658 nic.type = NetworkAdapterType_Am79C970A;
3659 else if (strTemp == "Am79C973")
3660 nic.type = NetworkAdapterType_Am79C973;
3661 else if (strTemp == "82540EM")
3662 nic.type = NetworkAdapterType_I82540EM;
3663 else if (strTemp == "82543GC")
3664 nic.type = NetworkAdapterType_I82543GC;
3665 else if (strTemp == "82545EM")
3666 nic.type = NetworkAdapterType_I82545EM;
3667 else if (strTemp == "virtio")
3668 nic.type = NetworkAdapterType_Virtio;
3669 else
3670 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
3671 }
3672
3673 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
3674 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
3675 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
3676 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
3677
3678 if (pelmAdapter->getAttributeValue("promiscuousModePolicy", strTemp))
3679 {
3680 if (strTemp == "Deny")
3681 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
3682 else if (strTemp == "AllowNetwork")
3683 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
3684 else if (strTemp == "AllowAll")
3685 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
3686 else
3687 throw ConfigFileError(this, pelmAdapter,
3688 N_("Invalid value '%s' in Adapter/@promiscuousModePolicy attribute"), strTemp.c_str());
3689 }
3690
3691 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
3692 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
3693 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
3694 pelmAdapter->getAttributeValue("bandwidthGroup", nic.strBandwidthGroup);
3695
3696 xml::ElementNodesList llNetworkModes;
3697 pelmAdapter->getChildElements(llNetworkModes);
3698 xml::ElementNodesList::iterator it;
3699 /* We should have only active mode descriptor and disabled modes set */
3700 if (llNetworkModes.size() > 2)
3701 {
3702 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
3703 }
3704 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
3705 {
3706 const xml::ElementNode *pelmNode = *it;
3707 if (pelmNode->nameEquals("DisabledModes"))
3708 {
3709 xml::ElementNodesList llDisabledNetworkModes;
3710 xml::ElementNodesList::iterator itDisabled;
3711 pelmNode->getChildElements(llDisabledNetworkModes);
3712 /* run over disabled list and load settings */
3713 for (itDisabled = llDisabledNetworkModes.begin();
3714 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
3715 {
3716 const xml::ElementNode *pelmDisabledNode = *itDisabled;
3717 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
3718 }
3719 }
3720 else
3721 readAttachedNetworkMode(*pelmNode, true, nic);
3722 }
3723 // else: default is NetworkAttachmentType_Null
3724
3725 ll.push_back(nic);
3726 }
3727}
3728
3729void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
3730{
3731 NetworkAttachmentType_T enmAttachmentType = NetworkAttachmentType_Null;
3732
3733 if (elmMode.nameEquals("NAT"))
3734 {
3735 enmAttachmentType = NetworkAttachmentType_NAT;
3736
3737 elmMode.getAttributeValue("network", nic.nat.strNetwork);
3738 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
3739 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
3740 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
3741 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
3742 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
3743 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
3744 const xml::ElementNode *pelmDNS;
3745 if ((pelmDNS = elmMode.findChildElement("DNS")))
3746 {
3747 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDNSPassDomain);
3748 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDNSProxy);
3749 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDNSUseHostResolver);
3750 }
3751 const xml::ElementNode *pelmAlias;
3752 if ((pelmAlias = elmMode.findChildElement("Alias")))
3753 {
3754 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
3755 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
3756 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
3757 }
3758 const xml::ElementNode *pelmTFTP;
3759 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
3760 {
3761 pelmTFTP->getAttributeValue("prefix", nic.nat.strTFTPPrefix);
3762 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTFTPBootFile);
3763 pelmTFTP->getAttributeValue("next-server", nic.nat.strTFTPNextServer);
3764 }
3765
3766 readNATForwardRulesMap(elmMode, nic.nat.mapRules);
3767 }
3768 else if ( elmMode.nameEquals("HostInterface")
3769 || elmMode.nameEquals("BridgedInterface"))
3770 {
3771 enmAttachmentType = NetworkAttachmentType_Bridged;
3772
3773 // optional network name, cannot be required or we have trouble with
3774 // settings which are saved before configuring the network name
3775 elmMode.getAttributeValue("name", nic.strBridgedName);
3776 }
3777 else if (elmMode.nameEquals("InternalNetwork"))
3778 {
3779 enmAttachmentType = NetworkAttachmentType_Internal;
3780
3781 // optional network name, cannot be required or we have trouble with
3782 // settings which are saved before configuring the network name
3783 elmMode.getAttributeValue("name", nic.strInternalNetworkName);
3784 }
3785 else if (elmMode.nameEquals("HostOnlyInterface"))
3786 {
3787 enmAttachmentType = NetworkAttachmentType_HostOnly;
3788
3789 // optional network name, cannot be required or we have trouble with
3790 // settings which are saved before configuring the network name
3791 elmMode.getAttributeValue("name", nic.strHostOnlyName);
3792 }
3793 else if (elmMode.nameEquals("GenericInterface"))
3794 {
3795 enmAttachmentType = NetworkAttachmentType_Generic;
3796
3797 elmMode.getAttributeValue("driver", nic.strGenericDriver); // optional network attachment driver
3798
3799 // get all properties
3800 xml::NodesLoop nl(elmMode);
3801 const xml::ElementNode *pelmModeChild;
3802 while ((pelmModeChild = nl.forAllNodes()))
3803 {
3804 if (pelmModeChild->nameEquals("Property"))
3805 {
3806 Utf8Str strPropName, strPropValue;
3807 if ( pelmModeChild->getAttributeValue("name", strPropName)
3808 && pelmModeChild->getAttributeValue("value", strPropValue) )
3809 nic.genericProperties[strPropName] = strPropValue;
3810 else
3811 throw ConfigFileError(this, pelmModeChild, N_("Required GenericInterface/Property/@name or @value attribute is missing"));
3812 }
3813 }
3814 }
3815 else if (elmMode.nameEquals("NATNetwork"))
3816 {
3817 enmAttachmentType = NetworkAttachmentType_NATNetwork;
3818
3819 // optional network name, cannot be required or we have trouble with
3820 // settings which are saved before configuring the network name
3821 elmMode.getAttributeValue("name", nic.strNATNetworkName);
3822 }
3823 else if (elmMode.nameEquals("VDE"))
3824 {
3825 // inofficial hack (VDE networking was never part of the official
3826 // settings, so it's not mentioned in VirtualBox-settings.xsd)
3827 enmAttachmentType = NetworkAttachmentType_Generic;
3828
3829 com::Utf8Str strVDEName;
3830 elmMode.getAttributeValue("network", strVDEName); // optional network name
3831 nic.strGenericDriver = "VDE";
3832 nic.genericProperties["network"] = strVDEName;
3833 }
3834
3835 if (fEnabled && enmAttachmentType != NetworkAttachmentType_Null)
3836 nic.mode = enmAttachmentType;
3837}
3838
3839/**
3840 * Called from MachineConfigFile::readHardware() to read serial port information.
3841 * @param elmUART
3842 * @param ll
3843 */
3844void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
3845 SerialPortsList &ll)
3846{
3847 xml::NodesLoop nl1(elmUART, "Port");
3848 const xml::ElementNode *pelmPort;
3849 while ((pelmPort = nl1.forAllNodes()))
3850 {
3851 SerialPort port;
3852 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
3853 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
3854
3855 // slot must be unique
3856 for (SerialPortsList::const_iterator it = ll.begin();
3857 it != ll.end();
3858 ++it)
3859 if ((*it).ulSlot == port.ulSlot)
3860 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
3861
3862 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
3863 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
3864 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
3865 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
3866 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
3867 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
3868
3869 Utf8Str strPortMode;
3870 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
3871 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
3872 if (strPortMode == "RawFile")
3873 port.portMode = PortMode_RawFile;
3874 else if (strPortMode == "HostPipe")
3875 port.portMode = PortMode_HostPipe;
3876 else if (strPortMode == "HostDevice")
3877 port.portMode = PortMode_HostDevice;
3878 else if (strPortMode == "Disconnected")
3879 port.portMode = PortMode_Disconnected;
3880 else if (strPortMode == "TCP")
3881 port.portMode = PortMode_TCP;
3882 else
3883 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
3884
3885 pelmPort->getAttributeValue("path", port.strPath);
3886 pelmPort->getAttributeValue("server", port.fServer);
3887
3888 Utf8Str strUartType;
3889 if (pelmPort->getAttributeValue("uartType", strUartType))
3890 {
3891 if (strUartType == "16450")
3892 port.uartType = UartType_U16450;
3893 else if (strUartType == "16550A")
3894 port.uartType = UartType_U16550A;
3895 else if (strUartType == "16750")
3896 port.uartType = UartType_U16750;
3897 else
3898 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@uartType attribute"), strUartType.c_str());
3899 }
3900
3901 ll.push_back(port);
3902 }
3903}
3904
3905/**
3906 * Called from MachineConfigFile::readHardware() to read parallel port information.
3907 * @param elmLPT
3908 * @param ll
3909 */
3910void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
3911 ParallelPortsList &ll)
3912{
3913 xml::NodesLoop nl1(elmLPT, "Port");
3914 const xml::ElementNode *pelmPort;
3915 while ((pelmPort = nl1.forAllNodes()))
3916 {
3917 ParallelPort port;
3918 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
3919 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
3920
3921 // slot must be unique
3922 for (ParallelPortsList::const_iterator it = ll.begin();
3923 it != ll.end();
3924 ++it)
3925 if ((*it).ulSlot == port.ulSlot)
3926 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
3927
3928 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
3929 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
3930 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
3931 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
3932 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
3933 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
3934
3935 pelmPort->getAttributeValue("path", port.strPath);
3936
3937 ll.push_back(port);
3938 }
3939}
3940
3941/**
3942 * Called from MachineConfigFile::readHardware() to read audio adapter information
3943 * and maybe fix driver information depending on the current host hardware.
3944 *
3945 * @param elmAudioAdapter "AudioAdapter" XML element.
3946 * @param aa
3947 */
3948void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
3949 AudioAdapter &aa)
3950{
3951 if (m->sv >= SettingsVersion_v1_15)
3952 {
3953 // get all properties
3954 xml::NodesLoop nl1(elmAudioAdapter, "Property");
3955 const xml::ElementNode *pelmModeChild;
3956 while ((pelmModeChild = nl1.forAllNodes()))
3957 {
3958 Utf8Str strPropName, strPropValue;
3959 if ( pelmModeChild->getAttributeValue("name", strPropName)
3960 && pelmModeChild->getAttributeValue("value", strPropValue) )
3961 aa.properties[strPropName] = strPropValue;
3962 else
3963 throw ConfigFileError(this, pelmModeChild, N_("Required AudioAdapter/Property/@name or @value attribute "
3964 "is missing"));
3965 }
3966 }
3967
3968 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
3969 elmAudioAdapter.getAttributeValue("enabledIn", aa.fEnabledIn);
3970 elmAudioAdapter.getAttributeValue("enabledOut", aa.fEnabledOut);
3971
3972 Utf8Str strTemp;
3973 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
3974 {
3975 if (strTemp == "SB16")
3976 aa.controllerType = AudioControllerType_SB16;
3977 else if (strTemp == "AC97")
3978 aa.controllerType = AudioControllerType_AC97;
3979 else if (strTemp == "HDA")
3980 aa.controllerType = AudioControllerType_HDA;
3981 else
3982 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
3983 }
3984
3985 if (elmAudioAdapter.getAttributeValue("codec", strTemp))
3986 {
3987 if (strTemp == "SB16")
3988 aa.codecType = AudioCodecType_SB16;
3989 else if (strTemp == "STAC9700")
3990 aa.codecType = AudioCodecType_STAC9700;
3991 else if (strTemp == "AD1980")
3992 aa.codecType = AudioCodecType_AD1980;
3993 else if (strTemp == "STAC9221")
3994 aa.codecType = AudioCodecType_STAC9221;
3995 else
3996 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@codec attribute"), strTemp.c_str());
3997 }
3998 else
3999 {
4000 /* No codec attribute provided; use defaults. */
4001 switch (aa.controllerType)
4002 {
4003 case AudioControllerType_AC97:
4004 aa.codecType = AudioCodecType_STAC9700;
4005 break;
4006 case AudioControllerType_SB16:
4007 aa.codecType = AudioCodecType_SB16;
4008 break;
4009 case AudioControllerType_HDA:
4010 aa.codecType = AudioCodecType_STAC9221;
4011 break;
4012 default:
4013 Assert(false); /* We just checked the controller type above. */
4014 }
4015 }
4016
4017 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
4018 {
4019 // settings before 1.3 used lower case so make sure this is case-insensitive
4020 strTemp.toUpper();
4021 if (strTemp == "NULL")
4022 aa.driverType = AudioDriverType_Null;
4023 else if (strTemp == "WINMM")
4024 aa.driverType = AudioDriverType_WinMM;
4025 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
4026 aa.driverType = AudioDriverType_DirectSound;
4027 else if (strTemp == "SOLAUDIO") /* Deprecated -- Solaris will use OSS by default now. */
4028 aa.driverType = AudioDriverType_SolAudio;
4029 else if (strTemp == "ALSA")
4030 aa.driverType = AudioDriverType_ALSA;
4031 else if (strTemp == "PULSE")
4032 aa.driverType = AudioDriverType_Pulse;
4033 else if (strTemp == "OSS")
4034 aa.driverType = AudioDriverType_OSS;
4035 else if (strTemp == "COREAUDIO")
4036 aa.driverType = AudioDriverType_CoreAudio;
4037 else if (strTemp == "MMPM")
4038 aa.driverType = AudioDriverType_MMPM;
4039 else
4040 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
4041
4042 // now check if this is actually supported on the current host platform;
4043 // people might be opening a file created on a Windows host, and that
4044 // VM should still start on a Linux host
4045 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
4046 aa.driverType = getHostDefaultAudioDriver();
4047 }
4048}
4049
4050/**
4051 * Called from MachineConfigFile::readHardware() to read guest property information.
4052 * @param elmGuestProperties
4053 * @param hw
4054 */
4055void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
4056 Hardware &hw)
4057{
4058 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
4059 const xml::ElementNode *pelmProp;
4060 while ((pelmProp = nl1.forAllNodes()))
4061 {
4062 GuestProperty prop;
4063 pelmProp->getAttributeValue("name", prop.strName);
4064 pelmProp->getAttributeValue("value", prop.strValue);
4065
4066 pelmProp->getAttributeValue("timestamp", prop.timestamp);
4067 pelmProp->getAttributeValue("flags", prop.strFlags);
4068 hw.llGuestProperties.push_back(prop);
4069 }
4070}
4071
4072/**
4073 * Helper function to read attributes that are common to \<SATAController\> (pre-1.7)
4074 * and \<StorageController\>.
4075 * @param elmStorageController
4076 * @param sctl
4077 */
4078void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
4079 StorageController &sctl)
4080{
4081 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
4082 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
4083}
4084
4085/**
4086 * Reads in a \<Hardware\> block and stores it in the given structure. Used
4087 * both directly from readMachine and from readSnapshot, since snapshots
4088 * have their own hardware sections.
4089 *
4090 * For legacy pre-1.7 settings we also need a storage structure because
4091 * the IDE and SATA controllers used to be defined under \<Hardware\>.
4092 *
4093 * @param elmHardware
4094 * @param hw
4095 */
4096void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
4097 Hardware &hw)
4098{
4099 if (m->sv >= SettingsVersion_v1_16)
4100 {
4101 /* Starting with VirtualBox 5.1 the default is Default, before it was
4102 * Legacy. This needs to matched by areParavirtDefaultSettings(). */
4103 hw.paravirtProvider = ParavirtProvider_Default;
4104 /* The new default is disabled, before it was enabled by default. */
4105 hw.vrdeSettings.fEnabled = false;
4106 /* The new default is disabled, before it was enabled by default. */
4107 hw.audioAdapter.fEnabled = false;
4108 }
4109 else if (m->sv >= SettingsVersion_v1_17)
4110 {
4111 /* Starting with VirtualBox 5.2 the default is disabled, before it was
4112 * enabled. This needs to matched by AudioAdapter::areDefaultSettings(). */
4113 hw.audioAdapter.fEnabledIn = false;
4114 /* The new default is disabled, before it was enabled by default. */
4115 hw.audioAdapter.fEnabledOut = false;
4116 }
4117
4118 if (!elmHardware.getAttributeValue("version", hw.strVersion))
4119 {
4120 /* KLUDGE ALERT! For a while during the 3.1 development this was not
4121 written because it was thought to have a default value of "2". For
4122 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
4123 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
4124 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
4125 missing the hardware version, then it probably should be "2" instead
4126 of "1". */
4127 if (m->sv < SettingsVersion_v1_7)
4128 hw.strVersion = "1";
4129 else
4130 hw.strVersion = "2";
4131 }
4132 Utf8Str strUUID;
4133 if (elmHardware.getAttributeValue("uuid", strUUID))
4134 parseUUID(hw.uuid, strUUID, &elmHardware);
4135
4136 xml::NodesLoop nl1(elmHardware);
4137 const xml::ElementNode *pelmHwChild;
4138 while ((pelmHwChild = nl1.forAllNodes()))
4139 {
4140 if (pelmHwChild->nameEquals("CPU"))
4141 {
4142 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
4143 {
4144 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
4145 const xml::ElementNode *pelmCPUChild;
4146 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
4147 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
4148 }
4149
4150 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
4151 pelmHwChild->getAttributeValue("executionCap", hw.ulCpuExecutionCap);
4152
4153 const xml::ElementNode *pelmCPUChild;
4154 if (hw.fCpuHotPlug)
4155 {
4156 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
4157 readCpuTree(*pelmCPUChild, hw.llCpus);
4158 }
4159
4160 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
4161 {
4162 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
4163 }
4164 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
4165 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
4166 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
4167 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
4168 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
4169 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
4170 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUX")))
4171 pelmCPUChild->getAttributeValue("enabled", hw.fUnrestrictedExecution);
4172 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtForce")))
4173 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirtForce);
4174 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUseNativeApi")))
4175 pelmCPUChild->getAttributeValue("enabled", hw.fUseNativeApi);
4176
4177 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
4178 {
4179 /* The default for pre 3.1 was false, so we must respect that. */
4180 if (m->sv < SettingsVersion_v1_9)
4181 hw.fPAE = false;
4182 }
4183 else
4184 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
4185
4186 bool fLongMode;
4187 if ( (pelmCPUChild = pelmHwChild->findChildElement("LongMode"))
4188 && pelmCPUChild->getAttributeValue("enabled", fLongMode) )
4189 hw.enmLongMode = fLongMode ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled;
4190 else
4191 hw.enmLongMode = Hardware::LongMode_Legacy;
4192
4193 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
4194 {
4195 bool fSyntheticCpu = false;
4196 pelmCPUChild->getAttributeValue("enabled", fSyntheticCpu);
4197 hw.uCpuIdPortabilityLevel = fSyntheticCpu ? 1 : 0;
4198 }
4199 pelmHwChild->getAttributeValue("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
4200 pelmHwChild->getAttributeValue("CpuProfile", hw.strCpuProfile);
4201
4202 if ((pelmCPUChild = pelmHwChild->findChildElement("TripleFaultReset")))
4203 pelmCPUChild->getAttributeValue("enabled", hw.fTripleFaultReset);
4204
4205 if ((pelmCPUChild = pelmHwChild->findChildElement("APIC")))
4206 pelmCPUChild->getAttributeValue("enabled", hw.fAPIC);
4207 if ((pelmCPUChild = pelmHwChild->findChildElement("X2APIC")))
4208 pelmCPUChild->getAttributeValue("enabled", hw.fX2APIC);
4209 if (hw.fX2APIC)
4210 hw.fAPIC = true;
4211 pelmCPUChild = pelmHwChild->findChildElement("IBPBOn");
4212 if (pelmCPUChild)
4213 {
4214 pelmCPUChild->getAttributeValue("vmexit", hw.fIBPBOnVMExit);
4215 pelmCPUChild->getAttributeValue("vmentry", hw.fIBPBOnVMEntry);
4216 }
4217 pelmCPUChild = pelmHwChild->findChildElement("SpecCtrl");
4218 if (pelmCPUChild)
4219 pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrl);
4220 pelmCPUChild = pelmHwChild->findChildElement("SpecCtrlByHost");
4221 if (pelmCPUChild)
4222 pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrlByHost);
4223 pelmCPUChild = pelmHwChild->findChildElement("NestedHWVirt");
4224 if (pelmCPUChild)
4225 pelmCPUChild->getAttributeValue("enabled", hw.fNestedHWVirt);
4226
4227 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
4228 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
4229 }
4230 else if (pelmHwChild->nameEquals("Memory"))
4231 {
4232 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
4233 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
4234 }
4235 else if (pelmHwChild->nameEquals("Firmware"))
4236 {
4237 Utf8Str strFirmwareType;
4238 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
4239 {
4240 if ( (strFirmwareType == "BIOS")
4241 || (strFirmwareType == "1") // some trunk builds used the number here
4242 )
4243 hw.firmwareType = FirmwareType_BIOS;
4244 else if ( (strFirmwareType == "EFI")
4245 || (strFirmwareType == "2") // some trunk builds used the number here
4246 )
4247 hw.firmwareType = FirmwareType_EFI;
4248 else if ( strFirmwareType == "EFI32")
4249 hw.firmwareType = FirmwareType_EFI32;
4250 else if ( strFirmwareType == "EFI64")
4251 hw.firmwareType = FirmwareType_EFI64;
4252 else if ( strFirmwareType == "EFIDUAL")
4253 hw.firmwareType = FirmwareType_EFIDUAL;
4254 else
4255 throw ConfigFileError(this,
4256 pelmHwChild,
4257 N_("Invalid value '%s' in Firmware/@type"),
4258 strFirmwareType.c_str());
4259 }
4260 }
4261 else if (pelmHwChild->nameEquals("HID"))
4262 {
4263 Utf8Str strHIDType;
4264 if (pelmHwChild->getAttributeValue("Keyboard", strHIDType))
4265 {
4266 if (strHIDType == "None")
4267 hw.keyboardHIDType = KeyboardHIDType_None;
4268 else if (strHIDType == "USBKeyboard")
4269 hw.keyboardHIDType = KeyboardHIDType_USBKeyboard;
4270 else if (strHIDType == "PS2Keyboard")
4271 hw.keyboardHIDType = KeyboardHIDType_PS2Keyboard;
4272 else if (strHIDType == "ComboKeyboard")
4273 hw.keyboardHIDType = KeyboardHIDType_ComboKeyboard;
4274 else
4275 throw ConfigFileError(this,
4276 pelmHwChild,
4277 N_("Invalid value '%s' in HID/Keyboard/@type"),
4278 strHIDType.c_str());
4279 }
4280 if (pelmHwChild->getAttributeValue("Pointing", strHIDType))
4281 {
4282 if (strHIDType == "None")
4283 hw.pointingHIDType = PointingHIDType_None;
4284 else if (strHIDType == "USBMouse")
4285 hw.pointingHIDType = PointingHIDType_USBMouse;
4286 else if (strHIDType == "USBTablet")
4287 hw.pointingHIDType = PointingHIDType_USBTablet;
4288 else if (strHIDType == "PS2Mouse")
4289 hw.pointingHIDType = PointingHIDType_PS2Mouse;
4290 else if (strHIDType == "ComboMouse")
4291 hw.pointingHIDType = PointingHIDType_ComboMouse;
4292 else if (strHIDType == "USBMultiTouch")
4293 hw.pointingHIDType = PointingHIDType_USBMultiTouch;
4294 else
4295 throw ConfigFileError(this,
4296 pelmHwChild,
4297 N_("Invalid value '%s' in HID/Pointing/@type"),
4298 strHIDType.c_str());
4299 }
4300 }
4301 else if (pelmHwChild->nameEquals("Chipset"))
4302 {
4303 Utf8Str strChipsetType;
4304 if (pelmHwChild->getAttributeValue("type", strChipsetType))
4305 {
4306 if (strChipsetType == "PIIX3")
4307 hw.chipsetType = ChipsetType_PIIX3;
4308 else if (strChipsetType == "ICH9")
4309 hw.chipsetType = ChipsetType_ICH9;
4310 else
4311 throw ConfigFileError(this,
4312 pelmHwChild,
4313 N_("Invalid value '%s' in Chipset/@type"),
4314 strChipsetType.c_str());
4315 }
4316 }
4317 else if (pelmHwChild->nameEquals("Paravirt"))
4318 {
4319 Utf8Str strProvider;
4320 if (pelmHwChild->getAttributeValue("provider", strProvider))
4321 {
4322 if (strProvider == "None")
4323 hw.paravirtProvider = ParavirtProvider_None;
4324 else if (strProvider == "Default")
4325 hw.paravirtProvider = ParavirtProvider_Default;
4326 else if (strProvider == "Legacy")
4327 hw.paravirtProvider = ParavirtProvider_Legacy;
4328 else if (strProvider == "Minimal")
4329 hw.paravirtProvider = ParavirtProvider_Minimal;
4330 else if (strProvider == "HyperV")
4331 hw.paravirtProvider = ParavirtProvider_HyperV;
4332 else if (strProvider == "KVM")
4333 hw.paravirtProvider = ParavirtProvider_KVM;
4334 else
4335 throw ConfigFileError(this,
4336 pelmHwChild,
4337 N_("Invalid value '%s' in Paravirt/@provider attribute"),
4338 strProvider.c_str());
4339 }
4340
4341 pelmHwChild->getAttributeValue("debug", hw.strParavirtDebug);
4342 }
4343 else if (pelmHwChild->nameEquals("HPET"))
4344 {
4345 pelmHwChild->getAttributeValue("enabled", hw.fHPETEnabled);
4346 }
4347 else if (pelmHwChild->nameEquals("Boot"))
4348 {
4349 hw.mapBootOrder.clear();
4350
4351 xml::NodesLoop nl2(*pelmHwChild, "Order");
4352 const xml::ElementNode *pelmOrder;
4353 while ((pelmOrder = nl2.forAllNodes()))
4354 {
4355 uint32_t ulPos;
4356 Utf8Str strDevice;
4357 if (!pelmOrder->getAttributeValue("position", ulPos))
4358 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
4359
4360 if ( ulPos < 1
4361 || ulPos > SchemaDefs::MaxBootPosition
4362 )
4363 throw ConfigFileError(this,
4364 pelmOrder,
4365 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
4366 ulPos,
4367 SchemaDefs::MaxBootPosition + 1);
4368 // XML is 1-based but internal data is 0-based
4369 --ulPos;
4370
4371 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
4372 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
4373
4374 if (!pelmOrder->getAttributeValue("device", strDevice))
4375 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
4376
4377 DeviceType_T type;
4378 if (strDevice == "None")
4379 type = DeviceType_Null;
4380 else if (strDevice == "Floppy")
4381 type = DeviceType_Floppy;
4382 else if (strDevice == "DVD")
4383 type = DeviceType_DVD;
4384 else if (strDevice == "HardDisk")
4385 type = DeviceType_HardDisk;
4386 else if (strDevice == "Network")
4387 type = DeviceType_Network;
4388 else
4389 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
4390 hw.mapBootOrder[ulPos] = type;
4391 }
4392 }
4393 else if (pelmHwChild->nameEquals("Display"))
4394 {
4395 Utf8Str strGraphicsControllerType;
4396 if (!pelmHwChild->getAttributeValue("controller", strGraphicsControllerType))
4397 hw.graphicsControllerType = GraphicsControllerType_VBoxVGA;
4398 else
4399 {
4400 strGraphicsControllerType.toUpper();
4401 GraphicsControllerType_T type;
4402 if (strGraphicsControllerType == "VBOXVGA")
4403 type = GraphicsControllerType_VBoxVGA;
4404 else if (strGraphicsControllerType == "VMSVGA")
4405 type = GraphicsControllerType_VMSVGA;
4406 else if (strGraphicsControllerType == "VBOXSVGA")
4407 type = GraphicsControllerType_VBoxSVGA;
4408 else if (strGraphicsControllerType == "NONE")
4409 type = GraphicsControllerType_Null;
4410 else
4411 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Display/@controller attribute"), strGraphicsControllerType.c_str());
4412 hw.graphicsControllerType = type;
4413 }
4414 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
4415 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
4416 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
4417 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
4418 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
4419 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
4420 }
4421 else if (pelmHwChild->nameEquals("VideoCapture"))
4422 {
4423 pelmHwChild->getAttributeValue("enabled", hw.recordingSettings.fEnabled);
4424
4425 /* Right now I don't want to bump the settings version, so just convert the enabled
4426 * screens to the former uint64t_t bit array and vice versa. */
4427 uint64_t u64VideoCaptureScreens;
4428 pelmHwChild->getAttributeValue("screens", u64VideoCaptureScreens);
4429
4430 /* At the moment we only support one capturing configuration, that is, all screens
4431 * have the same configuration. So load/save to/from screen 0. */
4432 Assert(hw.recordingSettings.mapScreens.size()); /* At least screen must be present. */
4433 RecordingScreenSettings &screen0Settings = hw.recordingSettings.mapScreens[0];
4434
4435 pelmHwChild->getAttributeValue("maxTime", screen0Settings.ulMaxTimeS);
4436 pelmHwChild->getAttributeValue("options", screen0Settings.strOptions);
4437 pelmHwChild->getAttributeValuePath("file", screen0Settings.File.strName);
4438 pelmHwChild->getAttributeValue("maxSize", screen0Settings.File.ulMaxSizeMB);
4439 pelmHwChild->getAttributeValue("horzRes", screen0Settings.Video.ulWidth);
4440 pelmHwChild->getAttributeValue("vertRes", screen0Settings.Video.ulHeight);
4441 pelmHwChild->getAttributeValue("rate", screen0Settings.Video.ulRate);
4442 pelmHwChild->getAttributeValue("fps", screen0Settings.Video.ulFPS);
4443
4444 for (unsigned i = 0; i < hw.cMonitors; i++) /* Don't add more settings than we have monitors configured. */
4445 {
4446 /* Add screen i to config in any case. */
4447 hw.recordingSettings.mapScreens[i] = screen0Settings;
4448
4449 if (u64VideoCaptureScreens & RT_BIT_64(i)) /* Screen i enabled? */
4450 hw.recordingSettings.mapScreens[i].fEnabled = true;
4451 }
4452 }
4453 else if (pelmHwChild->nameEquals("RemoteDisplay"))
4454 {
4455 pelmHwChild->getAttributeValue("enabled", hw.vrdeSettings.fEnabled);
4456
4457 Utf8Str str;
4458 if (pelmHwChild->getAttributeValue("port", str))
4459 hw.vrdeSettings.mapProperties["TCP/Ports"] = str;
4460 if (pelmHwChild->getAttributeValue("netAddress", str))
4461 hw.vrdeSettings.mapProperties["TCP/Address"] = str;
4462
4463 Utf8Str strAuthType;
4464 if (pelmHwChild->getAttributeValue("authType", strAuthType))
4465 {
4466 // settings before 1.3 used lower case so make sure this is case-insensitive
4467 strAuthType.toUpper();
4468 if (strAuthType == "NULL")
4469 hw.vrdeSettings.authType = AuthType_Null;
4470 else if (strAuthType == "GUEST")
4471 hw.vrdeSettings.authType = AuthType_Guest;
4472 else if (strAuthType == "EXTERNAL")
4473 hw.vrdeSettings.authType = AuthType_External;
4474 else
4475 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
4476 }
4477
4478 pelmHwChild->getAttributeValue("authLibrary", hw.vrdeSettings.strAuthLibrary);
4479 pelmHwChild->getAttributeValue("authTimeout", hw.vrdeSettings.ulAuthTimeout);
4480 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
4481 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
4482
4483 /* 3.2 and 4.0 betas, 4.0 has this information in VRDEProperties. */
4484 const xml::ElementNode *pelmVideoChannel;
4485 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
4486 {
4487 bool fVideoChannel = false;
4488 pelmVideoChannel->getAttributeValue("enabled", fVideoChannel);
4489 hw.vrdeSettings.mapProperties["VideoChannel/Enabled"] = fVideoChannel? "true": "false";
4490
4491 uint32_t ulVideoChannelQuality = 75;
4492 pelmVideoChannel->getAttributeValue("quality", ulVideoChannelQuality);
4493 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
4494 char *pszBuffer = NULL;
4495 if (RTStrAPrintf(&pszBuffer, "%d", ulVideoChannelQuality) >= 0)
4496 {
4497 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = pszBuffer;
4498 RTStrFree(pszBuffer);
4499 }
4500 else
4501 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = "75";
4502 }
4503 pelmHwChild->getAttributeValue("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
4504
4505 const xml::ElementNode *pelmProperties = pelmHwChild->findChildElement("VRDEProperties");
4506 if (pelmProperties != NULL)
4507 {
4508 xml::NodesLoop nl(*pelmProperties);
4509 const xml::ElementNode *pelmProperty;
4510 while ((pelmProperty = nl.forAllNodes()))
4511 {
4512 if (pelmProperty->nameEquals("Property"))
4513 {
4514 /* <Property name="TCP/Ports" value="3000-3002"/> */
4515 Utf8Str strName, strValue;
4516 if ( pelmProperty->getAttributeValue("name", strName)
4517 && pelmProperty->getAttributeValue("value", strValue))
4518 hw.vrdeSettings.mapProperties[strName] = strValue;
4519 else
4520 throw ConfigFileError(this, pelmProperty, N_("Required VRDE Property/@name or @value attribute is missing"));
4521 }
4522 }
4523 }
4524 }
4525 else if (pelmHwChild->nameEquals("BIOS"))
4526 {
4527 const xml::ElementNode *pelmBIOSChild;
4528 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
4529 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
4530 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
4531 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
4532 if ((pelmBIOSChild = pelmHwChild->findChildElement("APIC")))
4533 {
4534 Utf8Str strAPIC;
4535 if (pelmBIOSChild->getAttributeValue("mode", strAPIC))
4536 {
4537 strAPIC.toUpper();
4538 if (strAPIC == "DISABLED")
4539 hw.biosSettings.apicMode = APICMode_Disabled;
4540 else if (strAPIC == "APIC")
4541 hw.biosSettings.apicMode = APICMode_APIC;
4542 else if (strAPIC == "X2APIC")
4543 hw.biosSettings.apicMode = APICMode_X2APIC;
4544 else
4545 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in APIC/@mode attribute"), strAPIC.c_str());
4546 }
4547 }
4548 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
4549 {
4550 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
4551 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
4552 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
4553 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
4554 }
4555 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
4556 {
4557 Utf8Str strBootMenuMode;
4558 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
4559 {
4560 // settings before 1.3 used lower case so make sure this is case-insensitive
4561 strBootMenuMode.toUpper();
4562 if (strBootMenuMode == "DISABLED")
4563 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
4564 else if (strBootMenuMode == "MENUONLY")
4565 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
4566 else if (strBootMenuMode == "MESSAGEANDMENU")
4567 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
4568 else
4569 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
4570 }
4571 }
4572 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
4573 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
4574 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
4575 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
4576
4577 // legacy BIOS/IDEController (pre 1.7)
4578 if ( (m->sv < SettingsVersion_v1_7)
4579 && (pelmBIOSChild = pelmHwChild->findChildElement("IDEController"))
4580 )
4581 {
4582 StorageController sctl;
4583 sctl.strName = "IDE Controller";
4584 sctl.storageBus = StorageBus_IDE;
4585
4586 Utf8Str strType;
4587 if (pelmBIOSChild->getAttributeValue("type", strType))
4588 {
4589 if (strType == "PIIX3")
4590 sctl.controllerType = StorageControllerType_PIIX3;
4591 else if (strType == "PIIX4")
4592 sctl.controllerType = StorageControllerType_PIIX4;
4593 else if (strType == "ICH6")
4594 sctl.controllerType = StorageControllerType_ICH6;
4595 else
4596 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
4597 }
4598 sctl.ulPortCount = 2;
4599 hw.storage.llStorageControllers.push_back(sctl);
4600 }
4601 }
4602 else if ( (m->sv <= SettingsVersion_v1_14)
4603 && pelmHwChild->nameEquals("USBController"))
4604 {
4605 bool fEnabled = false;
4606
4607 pelmHwChild->getAttributeValue("enabled", fEnabled);
4608 if (fEnabled)
4609 {
4610 /* Create OHCI controller with default name. */
4611 USBController ctrl;
4612
4613 ctrl.strName = "OHCI";
4614 ctrl.enmType = USBControllerType_OHCI;
4615 hw.usbSettings.llUSBControllers.push_back(ctrl);
4616 }
4617
4618 pelmHwChild->getAttributeValue("enabledEhci", fEnabled);
4619 if (fEnabled)
4620 {
4621 /* Create OHCI controller with default name. */
4622 USBController ctrl;
4623
4624 ctrl.strName = "EHCI";
4625 ctrl.enmType = USBControllerType_EHCI;
4626 hw.usbSettings.llUSBControllers.push_back(ctrl);
4627 }
4628
4629 readUSBDeviceFilters(*pelmHwChild,
4630 hw.usbSettings.llDeviceFilters);
4631 }
4632 else if (pelmHwChild->nameEquals("USB"))
4633 {
4634 const xml::ElementNode *pelmUSBChild;
4635
4636 if ((pelmUSBChild = pelmHwChild->findChildElement("Controllers")))
4637 {
4638 xml::NodesLoop nl2(*pelmUSBChild, "Controller");
4639 const xml::ElementNode *pelmCtrl;
4640
4641 while ((pelmCtrl = nl2.forAllNodes()))
4642 {
4643 USBController ctrl;
4644 com::Utf8Str strCtrlType;
4645
4646 pelmCtrl->getAttributeValue("name", ctrl.strName);
4647
4648 if (pelmCtrl->getAttributeValue("type", strCtrlType))
4649 {
4650 if (strCtrlType == "OHCI")
4651 ctrl.enmType = USBControllerType_OHCI;
4652 else if (strCtrlType == "EHCI")
4653 ctrl.enmType = USBControllerType_EHCI;
4654 else if (strCtrlType == "XHCI")
4655 ctrl.enmType = USBControllerType_XHCI;
4656 else
4657 throw ConfigFileError(this, pelmCtrl, N_("Invalid value '%s' for Controller/@type attribute"), strCtrlType.c_str());
4658 }
4659
4660 hw.usbSettings.llUSBControllers.push_back(ctrl);
4661 }
4662 }
4663
4664 if ((pelmUSBChild = pelmHwChild->findChildElement("DeviceFilters")))
4665 readUSBDeviceFilters(*pelmUSBChild, hw.usbSettings.llDeviceFilters);
4666 }
4667 else if ( m->sv < SettingsVersion_v1_7
4668 && pelmHwChild->nameEquals("SATAController"))
4669 {
4670 bool f;
4671 if ( pelmHwChild->getAttributeValue("enabled", f)
4672 && f)
4673 {
4674 StorageController sctl;
4675 sctl.strName = "SATA Controller";
4676 sctl.storageBus = StorageBus_SATA;
4677 sctl.controllerType = StorageControllerType_IntelAhci;
4678
4679 readStorageControllerAttributes(*pelmHwChild, sctl);
4680
4681 hw.storage.llStorageControllers.push_back(sctl);
4682 }
4683 }
4684 else if (pelmHwChild->nameEquals("Network"))
4685 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
4686 else if (pelmHwChild->nameEquals("RTC"))
4687 {
4688 Utf8Str strLocalOrUTC;
4689 machineUserData.fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
4690 && strLocalOrUTC == "UTC";
4691 }
4692 else if ( pelmHwChild->nameEquals("UART")
4693 || pelmHwChild->nameEquals("Uart") // used before 1.3
4694 )
4695 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
4696 else if ( pelmHwChild->nameEquals("LPT")
4697 || pelmHwChild->nameEquals("Lpt") // used before 1.3
4698 )
4699 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
4700 else if (pelmHwChild->nameEquals("AudioAdapter"))
4701 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
4702 else if (pelmHwChild->nameEquals("SharedFolders"))
4703 {
4704 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
4705 const xml::ElementNode *pelmFolder;
4706 while ((pelmFolder = nl2.forAllNodes()))
4707 {
4708 SharedFolder sf;
4709 pelmFolder->getAttributeValue("name", sf.strName);
4710 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
4711 pelmFolder->getAttributeValue("writable", sf.fWritable);
4712 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
4713 pelmFolder->getAttributeValue("autoMountPoint", sf.strAutoMountPoint);
4714 hw.llSharedFolders.push_back(sf);
4715 }
4716 }
4717 else if (pelmHwChild->nameEquals("Clipboard"))
4718 {
4719 Utf8Str strTemp;
4720 if (pelmHwChild->getAttributeValue("mode", strTemp))
4721 {
4722 if (strTemp == "Disabled")
4723 hw.clipboardMode = ClipboardMode_Disabled;
4724 else if (strTemp == "HostToGuest")
4725 hw.clipboardMode = ClipboardMode_HostToGuest;
4726 else if (strTemp == "GuestToHost")
4727 hw.clipboardMode = ClipboardMode_GuestToHost;
4728 else if (strTemp == "Bidirectional")
4729 hw.clipboardMode = ClipboardMode_Bidirectional;
4730 else
4731 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
4732 }
4733 }
4734 else if (pelmHwChild->nameEquals("DragAndDrop"))
4735 {
4736 Utf8Str strTemp;
4737 if (pelmHwChild->getAttributeValue("mode", strTemp))
4738 {
4739 if (strTemp == "Disabled")
4740 hw.dndMode = DnDMode_Disabled;
4741 else if (strTemp == "HostToGuest")
4742 hw.dndMode = DnDMode_HostToGuest;
4743 else if (strTemp == "GuestToHost")
4744 hw.dndMode = DnDMode_GuestToHost;
4745 else if (strTemp == "Bidirectional")
4746 hw.dndMode = DnDMode_Bidirectional;
4747 else
4748 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in DragAndDrop/@mode attribute"), strTemp.c_str());
4749 }
4750 }
4751 else if (pelmHwChild->nameEquals("Guest"))
4752 {
4753 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
4754 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
4755 }
4756 else if (pelmHwChild->nameEquals("GuestProperties"))
4757 readGuestProperties(*pelmHwChild, hw);
4758 else if (pelmHwChild->nameEquals("IO"))
4759 {
4760 const xml::ElementNode *pelmBwGroups;
4761 const xml::ElementNode *pelmIOChild;
4762
4763 if ((pelmIOChild = pelmHwChild->findChildElement("IoCache")))
4764 {
4765 pelmIOChild->getAttributeValue("enabled", hw.ioSettings.fIOCacheEnabled);
4766 pelmIOChild->getAttributeValue("size", hw.ioSettings.ulIOCacheSize);
4767 }
4768
4769 if ((pelmBwGroups = pelmHwChild->findChildElement("BandwidthGroups")))
4770 {
4771 xml::NodesLoop nl2(*pelmBwGroups, "BandwidthGroup");
4772 const xml::ElementNode *pelmBandwidthGroup;
4773 while ((pelmBandwidthGroup = nl2.forAllNodes()))
4774 {
4775 BandwidthGroup gr;
4776 Utf8Str strTemp;
4777
4778 pelmBandwidthGroup->getAttributeValue("name", gr.strName);
4779
4780 if (pelmBandwidthGroup->getAttributeValue("type", strTemp))
4781 {
4782 if (strTemp == "Disk")
4783 gr.enmType = BandwidthGroupType_Disk;
4784 else if (strTemp == "Network")
4785 gr.enmType = BandwidthGroupType_Network;
4786 else
4787 throw ConfigFileError(this, pelmBandwidthGroup, N_("Invalid value '%s' in BandwidthGroup/@type attribute"), strTemp.c_str());
4788 }
4789 else
4790 throw ConfigFileError(this, pelmBandwidthGroup, N_("Missing BandwidthGroup/@type attribute"));
4791
4792 if (!pelmBandwidthGroup->getAttributeValue("maxBytesPerSec", gr.cMaxBytesPerSec))
4793 {
4794 pelmBandwidthGroup->getAttributeValue("maxMbPerSec", gr.cMaxBytesPerSec);
4795 gr.cMaxBytesPerSec *= _1M;
4796 }
4797 hw.ioSettings.llBandwidthGroups.push_back(gr);
4798 }
4799 }
4800 }
4801 else if (pelmHwChild->nameEquals("HostPci"))
4802 {
4803 const xml::ElementNode *pelmDevices;
4804
4805 if ((pelmDevices = pelmHwChild->findChildElement("Devices")))
4806 {
4807 xml::NodesLoop nl2(*pelmDevices, "Device");
4808 const xml::ElementNode *pelmDevice;
4809 while ((pelmDevice = nl2.forAllNodes()))
4810 {
4811 HostPCIDeviceAttachment hpda;
4812
4813 if (!pelmDevice->getAttributeValue("host", hpda.uHostAddress))
4814 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@host attribute"));
4815
4816 if (!pelmDevice->getAttributeValue("guest", hpda.uGuestAddress))
4817 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@guest attribute"));
4818
4819 /* name is optional */
4820 pelmDevice->getAttributeValue("name", hpda.strDeviceName);
4821
4822 hw.pciAttachments.push_back(hpda);
4823 }
4824 }
4825 }
4826 else if (pelmHwChild->nameEquals("EmulatedUSB"))
4827 {
4828 const xml::ElementNode *pelmCardReader;
4829
4830 if ((pelmCardReader = pelmHwChild->findChildElement("CardReader")))
4831 {
4832 pelmCardReader->getAttributeValue("enabled", hw.fEmulatedUSBCardReader);
4833 }
4834 }
4835 else if (pelmHwChild->nameEquals("Frontend"))
4836 {
4837 const xml::ElementNode *pelmDefault;
4838
4839 if ((pelmDefault = pelmHwChild->findChildElement("Default")))
4840 {
4841 pelmDefault->getAttributeValue("type", hw.strDefaultFrontend);
4842 }
4843 }
4844 else if (pelmHwChild->nameEquals("StorageControllers"))
4845 readStorageControllers(*pelmHwChild, hw.storage);
4846 }
4847
4848 if (hw.ulMemorySizeMB == (uint32_t)-1)
4849 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
4850}
4851
4852/**
4853 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
4854 * files which have a \<HardDiskAttachments\> node and storage controller settings
4855 * hidden in the \<Hardware\> settings. We set the StorageControllers fields just the
4856 * same, just from different sources.
4857 * @param elmHardDiskAttachments \<HardDiskAttachments\> XML node.
4858 * @param strg
4859 */
4860void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
4861 Storage &strg)
4862{
4863 StorageController *pIDEController = NULL;
4864 StorageController *pSATAController = NULL;
4865
4866 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
4867 it != strg.llStorageControllers.end();
4868 ++it)
4869 {
4870 StorageController &s = *it;
4871 if (s.storageBus == StorageBus_IDE)
4872 pIDEController = &s;
4873 else if (s.storageBus == StorageBus_SATA)
4874 pSATAController = &s;
4875 }
4876
4877 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
4878 const xml::ElementNode *pelmAttachment;
4879 while ((pelmAttachment = nl1.forAllNodes()))
4880 {
4881 AttachedDevice att;
4882 Utf8Str strUUID, strBus;
4883
4884 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
4885 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
4886 parseUUID(att.uuid, strUUID, pelmAttachment);
4887
4888 if (!pelmAttachment->getAttributeValue("bus", strBus))
4889 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
4890 // pre-1.7 'channel' is now port
4891 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
4892 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
4893 // pre-1.7 'device' is still device
4894 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
4895 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
4896
4897 att.deviceType = DeviceType_HardDisk;
4898
4899 if (strBus == "IDE")
4900 {
4901 if (!pIDEController)
4902 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
4903 pIDEController->llAttachedDevices.push_back(att);
4904 }
4905 else if (strBus == "SATA")
4906 {
4907 if (!pSATAController)
4908 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
4909 pSATAController->llAttachedDevices.push_back(att);
4910 }
4911 else
4912 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
4913 }
4914}
4915
4916/**
4917 * Reads in a \<StorageControllers\> block and stores it in the given Storage structure.
4918 * Used both directly from readMachine and from readSnapshot, since snapshots
4919 * have their own storage controllers sections.
4920 *
4921 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
4922 * for earlier versions.
4923 *
4924 * @param elmStorageControllers
4925 * @param strg
4926 */
4927void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
4928 Storage &strg)
4929{
4930 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
4931 const xml::ElementNode *pelmController;
4932 while ((pelmController = nlStorageControllers.forAllNodes()))
4933 {
4934 StorageController sctl;
4935
4936 if (!pelmController->getAttributeValue("name", sctl.strName))
4937 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
4938 // canonicalize storage controller names for configs in the switchover
4939 // period.
4940 if (m->sv < SettingsVersion_v1_9)
4941 {
4942 if (sctl.strName == "IDE")
4943 sctl.strName = "IDE Controller";
4944 else if (sctl.strName == "SATA")
4945 sctl.strName = "SATA Controller";
4946 else if (sctl.strName == "SCSI")
4947 sctl.strName = "SCSI Controller";
4948 }
4949
4950 pelmController->getAttributeValue("Instance", sctl.ulInstance);
4951 // default from constructor is 0
4952
4953 pelmController->getAttributeValue("Bootable", sctl.fBootable);
4954 // default from constructor is true which is true
4955 // for settings below version 1.11 because they allowed only
4956 // one controller per type.
4957
4958 Utf8Str strType;
4959 if (!pelmController->getAttributeValue("type", strType))
4960 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
4961
4962 if (strType == "AHCI")
4963 {
4964 sctl.storageBus = StorageBus_SATA;
4965 sctl.controllerType = StorageControllerType_IntelAhci;
4966 }
4967 else if (strType == "LsiLogic")
4968 {
4969 sctl.storageBus = StorageBus_SCSI;
4970 sctl.controllerType = StorageControllerType_LsiLogic;
4971 }
4972 else if (strType == "BusLogic")
4973 {
4974 sctl.storageBus = StorageBus_SCSI;
4975 sctl.controllerType = StorageControllerType_BusLogic;
4976 }
4977 else if (strType == "PIIX3")
4978 {
4979 sctl.storageBus = StorageBus_IDE;
4980 sctl.controllerType = StorageControllerType_PIIX3;
4981 }
4982 else if (strType == "PIIX4")
4983 {
4984 sctl.storageBus = StorageBus_IDE;
4985 sctl.controllerType = StorageControllerType_PIIX4;
4986 }
4987 else if (strType == "ICH6")
4988 {
4989 sctl.storageBus = StorageBus_IDE;
4990 sctl.controllerType = StorageControllerType_ICH6;
4991 }
4992 else if ( (m->sv >= SettingsVersion_v1_9)
4993 && (strType == "I82078")
4994 )
4995 {
4996 sctl.storageBus = StorageBus_Floppy;
4997 sctl.controllerType = StorageControllerType_I82078;
4998 }
4999 else if (strType == "LsiLogicSas")
5000 {
5001 sctl.storageBus = StorageBus_SAS;
5002 sctl.controllerType = StorageControllerType_LsiLogicSas;
5003 }
5004 else if (strType == "USB")
5005 {
5006 sctl.storageBus = StorageBus_USB;
5007 sctl.controllerType = StorageControllerType_USB;
5008 }
5009 else if (strType == "NVMe")
5010 {
5011 sctl.storageBus = StorageBus_PCIe;
5012 sctl.controllerType = StorageControllerType_NVMe;
5013 }
5014 else
5015 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
5016
5017 readStorageControllerAttributes(*pelmController, sctl);
5018
5019 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
5020 const xml::ElementNode *pelmAttached;
5021 while ((pelmAttached = nlAttached.forAllNodes()))
5022 {
5023 AttachedDevice att;
5024 Utf8Str strTemp;
5025 pelmAttached->getAttributeValue("type", strTemp);
5026
5027 att.fDiscard = false;
5028 att.fNonRotational = false;
5029 att.fHotPluggable = false;
5030 att.fPassThrough = false;
5031
5032 if (strTemp == "HardDisk")
5033 {
5034 att.deviceType = DeviceType_HardDisk;
5035 pelmAttached->getAttributeValue("nonrotational", att.fNonRotational);
5036 pelmAttached->getAttributeValue("discard", att.fDiscard);
5037 }
5038 else if (m->sv >= SettingsVersion_v1_9)
5039 {
5040 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
5041 if (strTemp == "DVD")
5042 {
5043 att.deviceType = DeviceType_DVD;
5044 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
5045 pelmAttached->getAttributeValue("tempeject", att.fTempEject);
5046 }
5047 else if (strTemp == "Floppy")
5048 att.deviceType = DeviceType_Floppy;
5049 }
5050
5051 if (att.deviceType != DeviceType_Null)
5052 {
5053 const xml::ElementNode *pelmImage;
5054 // all types can have images attached, but for HardDisk it's required
5055 if (!(pelmImage = pelmAttached->findChildElement("Image")))
5056 {
5057 if (att.deviceType == DeviceType_HardDisk)
5058 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
5059 else
5060 {
5061 // DVDs and floppies can also have <HostDrive> instead of <Image>
5062 const xml::ElementNode *pelmHostDrive;
5063 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
5064 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
5065 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
5066 }
5067 }
5068 else
5069 {
5070 if (!pelmImage->getAttributeValue("uuid", strTemp))
5071 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
5072 parseUUID(att.uuid, strTemp, pelmImage);
5073 }
5074
5075 if (!pelmAttached->getAttributeValue("port", att.lPort))
5076 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
5077 if (!pelmAttached->getAttributeValue("device", att.lDevice))
5078 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
5079
5080 /* AHCI controller ports are hotpluggable by default, keep compatibility with existing settings. */
5081 if (m->sv >= SettingsVersion_v1_15)
5082 pelmAttached->getAttributeValue("hotpluggable", att.fHotPluggable);
5083 else if (sctl.controllerType == StorageControllerType_IntelAhci)
5084 att.fHotPluggable = true;
5085
5086 pelmAttached->getAttributeValue("bandwidthGroup", att.strBwGroup);
5087 sctl.llAttachedDevices.push_back(att);
5088 }
5089 }
5090
5091 strg.llStorageControllers.push_back(sctl);
5092 }
5093}
5094
5095/**
5096 * This gets called for legacy pre-1.9 settings files after having parsed the
5097 * \<Hardware\> and \<StorageControllers\> sections to parse \<Hardware\> once more
5098 * for the \<DVDDrive\> and \<FloppyDrive\> sections.
5099 *
5100 * Before settings version 1.9, DVD and floppy drives were specified separately
5101 * under \<Hardware\>; we then need this extra loop to make sure the storage
5102 * controller structs are already set up so we can add stuff to them.
5103 *
5104 * @param elmHardware
5105 * @param strg
5106 */
5107void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
5108 Storage &strg)
5109{
5110 xml::NodesLoop nl1(elmHardware);
5111 const xml::ElementNode *pelmHwChild;
5112 while ((pelmHwChild = nl1.forAllNodes()))
5113 {
5114 if (pelmHwChild->nameEquals("DVDDrive"))
5115 {
5116 // create a DVD "attached device" and attach it to the existing IDE controller
5117 AttachedDevice att;
5118 att.deviceType = DeviceType_DVD;
5119 // legacy DVD drive is always secondary master (port 1, device 0)
5120 att.lPort = 1;
5121 att.lDevice = 0;
5122 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
5123 pelmHwChild->getAttributeValue("tempeject", att.fTempEject);
5124
5125 const xml::ElementNode *pDriveChild;
5126 Utf8Str strTmp;
5127 if ( (pDriveChild = pelmHwChild->findChildElement("Image")) != NULL
5128 && pDriveChild->getAttributeValue("uuid", strTmp))
5129 parseUUID(att.uuid, strTmp, pDriveChild);
5130 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
5131 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
5132
5133 // find the IDE controller and attach the DVD drive
5134 bool fFound = false;
5135 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
5136 it != strg.llStorageControllers.end();
5137 ++it)
5138 {
5139 StorageController &sctl = *it;
5140 if (sctl.storageBus == StorageBus_IDE)
5141 {
5142 sctl.llAttachedDevices.push_back(att);
5143 fFound = true;
5144 break;
5145 }
5146 }
5147
5148 if (!fFound)
5149 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
5150 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
5151 // which should have gotten parsed in <StorageControllers> before this got called
5152 }
5153 else if (pelmHwChild->nameEquals("FloppyDrive"))
5154 {
5155 bool fEnabled;
5156 if ( pelmHwChild->getAttributeValue("enabled", fEnabled)
5157 && fEnabled)
5158 {
5159 // create a new floppy controller and attach a floppy "attached device"
5160 StorageController sctl;
5161 sctl.strName = "Floppy Controller";
5162 sctl.storageBus = StorageBus_Floppy;
5163 sctl.controllerType = StorageControllerType_I82078;
5164 sctl.ulPortCount = 1;
5165
5166 AttachedDevice att;
5167 att.deviceType = DeviceType_Floppy;
5168 att.lPort = 0;
5169 att.lDevice = 0;
5170
5171 const xml::ElementNode *pDriveChild;
5172 Utf8Str strTmp;
5173 if ( (pDriveChild = pelmHwChild->findChildElement("Image"))
5174 && pDriveChild->getAttributeValue("uuid", strTmp) )
5175 parseUUID(att.uuid, strTmp, pDriveChild);
5176 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
5177 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
5178
5179 // store attachment with controller
5180 sctl.llAttachedDevices.push_back(att);
5181 // store controller with storage
5182 strg.llStorageControllers.push_back(sctl);
5183 }
5184 }
5185 }
5186}
5187
5188/**
5189 * Called for reading the \<Teleporter\> element under \<Machine\>.
5190 */
5191void MachineConfigFile::readTeleporter(const xml::ElementNode *pElmTeleporter,
5192 MachineUserData *pUserData)
5193{
5194 pElmTeleporter->getAttributeValue("enabled", pUserData->fTeleporterEnabled);
5195 pElmTeleporter->getAttributeValue("port", pUserData->uTeleporterPort);
5196 pElmTeleporter->getAttributeValue("address", pUserData->strTeleporterAddress);
5197 pElmTeleporter->getAttributeValue("password", pUserData->strTeleporterPassword);
5198
5199 if ( pUserData->strTeleporterPassword.isNotEmpty()
5200 && !VBoxIsPasswordHashed(&pUserData->strTeleporterPassword))
5201 VBoxHashPassword(&pUserData->strTeleporterPassword);
5202}
5203
5204/**
5205 * Called for reading the \<Debugging\> element under \<Machine\> or \<Snapshot\>.
5206 */
5207void MachineConfigFile::readDebugging(const xml::ElementNode *pElmDebugging, Debugging *pDbg)
5208{
5209 if (!pElmDebugging || m->sv < SettingsVersion_v1_13)
5210 return;
5211
5212 const xml::ElementNode * const pelmTracing = pElmDebugging->findChildElement("Tracing");
5213 if (pelmTracing)
5214 {
5215 pelmTracing->getAttributeValue("enabled", pDbg->fTracingEnabled);
5216 pelmTracing->getAttributeValue("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
5217 pelmTracing->getAttributeValue("config", pDbg->strTracingConfig);
5218 }
5219}
5220
5221/**
5222 * Called for reading the \<Autostart\> element under \<Machine\> or \<Snapshot\>.
5223 */
5224void MachineConfigFile::readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart)
5225{
5226 Utf8Str strAutostop;
5227
5228 if (!pElmAutostart || m->sv < SettingsVersion_v1_13)
5229 return;
5230
5231 pElmAutostart->getAttributeValue("enabled", pAutostart->fAutostartEnabled);
5232 pElmAutostart->getAttributeValue("delay", pAutostart->uAutostartDelay);
5233 pElmAutostart->getAttributeValue("autostop", strAutostop);
5234 if (strAutostop == "Disabled")
5235 pAutostart->enmAutostopType = AutostopType_Disabled;
5236 else if (strAutostop == "SaveState")
5237 pAutostart->enmAutostopType = AutostopType_SaveState;
5238 else if (strAutostop == "PowerOff")
5239 pAutostart->enmAutostopType = AutostopType_PowerOff;
5240 else if (strAutostop == "AcpiShutdown")
5241 pAutostart->enmAutostopType = AutostopType_AcpiShutdown;
5242 else
5243 throw ConfigFileError(this, pElmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str());
5244}
5245
5246/**
5247 * Called for reading the \<Groups\> element under \<Machine\>.
5248 */
5249void MachineConfigFile::readGroups(const xml::ElementNode *pElmGroups, StringsList *pllGroups)
5250{
5251 pllGroups->clear();
5252 if (!pElmGroups || m->sv < SettingsVersion_v1_13)
5253 {
5254 pllGroups->push_back("/");
5255 return;
5256 }
5257
5258 xml::NodesLoop nlGroups(*pElmGroups);
5259 const xml::ElementNode *pelmGroup;
5260 while ((pelmGroup = nlGroups.forAllNodes()))
5261 {
5262 if (pelmGroup->nameEquals("Group"))
5263 {
5264 Utf8Str strGroup;
5265 if (!pelmGroup->getAttributeValue("name", strGroup))
5266 throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing"));
5267 pllGroups->push_back(strGroup);
5268 }
5269 }
5270}
5271
5272/**
5273 * Called initially for the \<Snapshot\> element under \<Machine\>, if present,
5274 * to store the snapshot's data into the given Snapshot structure (which is
5275 * then the one in the Machine struct). This might then recurse if
5276 * a \<Snapshots\> (plural) element is found in the snapshot, which should
5277 * contain a list of child snapshots; such lists are maintained in the
5278 * Snapshot structure.
5279 *
5280 * @param curSnapshotUuid
5281 * @param depth
5282 * @param elmSnapshot
5283 * @param snap
5284 * @returns true if curSnapshotUuid is in this snapshot subtree, otherwise false
5285 */
5286bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid,
5287 uint32_t depth,
5288 const xml::ElementNode &elmSnapshot,
5289 Snapshot &snap)
5290{
5291 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
5292 throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
5293
5294 Utf8Str strTemp;
5295
5296 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
5297 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
5298 parseUUID(snap.uuid, strTemp, &elmSnapshot);
5299 bool foundCurrentSnapshot = (snap.uuid == curSnapshotUuid);
5300
5301 if (!elmSnapshot.getAttributeValue("name", snap.strName))
5302 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
5303
5304 // 3.1 dev builds added Description as an attribute, read it silently
5305 // and write it back as an element
5306 elmSnapshot.getAttributeValue("Description", snap.strDescription);
5307
5308 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
5309 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
5310 parseTimestamp(snap.timestamp, strTemp, &elmSnapshot);
5311
5312 elmSnapshot.getAttributeValuePath("stateFile", snap.strStateFile); // online snapshots only
5313
5314 // parse Hardware before the other elements because other things depend on it
5315 const xml::ElementNode *pelmHardware;
5316 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
5317 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
5318 readHardware(*pelmHardware, snap.hardware);
5319
5320 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
5321 const xml::ElementNode *pelmSnapshotChild;
5322 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
5323 {
5324 if (pelmSnapshotChild->nameEquals("Description"))
5325 snap.strDescription = pelmSnapshotChild->getValue();
5326 else if ( m->sv < SettingsVersion_v1_7
5327 && pelmSnapshotChild->nameEquals("HardDiskAttachments"))
5328 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.hardware.storage);
5329 else if ( m->sv >= SettingsVersion_v1_7
5330 && pelmSnapshotChild->nameEquals("StorageControllers"))
5331 readStorageControllers(*pelmSnapshotChild, snap.hardware.storage);
5332 else if (pelmSnapshotChild->nameEquals("Snapshots"))
5333 {
5334 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
5335 const xml::ElementNode *pelmChildSnapshot;
5336 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
5337 {
5338 if (pelmChildSnapshot->nameEquals("Snapshot"))
5339 {
5340 // recurse with this element and put the child at the
5341 // end of the list. XPCOM has very small stack, avoid
5342 // big local variables and use the list element.
5343 snap.llChildSnapshots.push_back(Snapshot::Empty);
5344 bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, snap.llChildSnapshots.back());
5345 foundCurrentSnapshot = foundCurrentSnapshot || found;
5346 }
5347 }
5348 }
5349 }
5350
5351 if (m->sv < SettingsVersion_v1_9)
5352 // go through Hardware once more to repair the settings controller structures
5353 // with data from old DVDDrive and FloppyDrive elements
5354 readDVDAndFloppies_pre1_9(*pelmHardware, snap.hardware.storage);
5355
5356 readDebugging(elmSnapshot.findChildElement("Debugging"), &snap.debugging);
5357 readAutostart(elmSnapshot.findChildElement("Autostart"), &snap.autostart);
5358 // note: Groups exist only for Machine, not for Snapshot
5359
5360 return foundCurrentSnapshot;
5361}
5362
5363const struct {
5364 const char *pcszOld;
5365 const char *pcszNew;
5366} aConvertOSTypes[] =
5367{
5368 { "unknown", "Other" },
5369 { "dos", "DOS" },
5370 { "win31", "Windows31" },
5371 { "win95", "Windows95" },
5372 { "win98", "Windows98" },
5373 { "winme", "WindowsMe" },
5374 { "winnt4", "WindowsNT4" },
5375 { "win2k", "Windows2000" },
5376 { "winxp", "WindowsXP" },
5377 { "win2k3", "Windows2003" },
5378 { "winvista", "WindowsVista" },
5379 { "win2k8", "Windows2008" },
5380 { "os2warp3", "OS2Warp3" },
5381 { "os2warp4", "OS2Warp4" },
5382 { "os2warp45", "OS2Warp45" },
5383 { "ecs", "OS2eCS" },
5384 { "linux22", "Linux22" },
5385 { "linux24", "Linux24" },
5386 { "linux26", "Linux26" },
5387 { "archlinux", "ArchLinux" },
5388 { "debian", "Debian" },
5389 { "opensuse", "OpenSUSE" },
5390 { "fedoracore", "Fedora" },
5391 { "gentoo", "Gentoo" },
5392 { "mandriva", "Mandriva" },
5393 { "redhat", "RedHat" },
5394 { "ubuntu", "Ubuntu" },
5395 { "xandros", "Xandros" },
5396 { "freebsd", "FreeBSD" },
5397 { "openbsd", "OpenBSD" },
5398 { "netbsd", "NetBSD" },
5399 { "netware", "Netware" },
5400 { "solaris", "Solaris" },
5401 { "opensolaris", "OpenSolaris" },
5402 { "l4", "L4" }
5403};
5404
5405void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
5406{
5407 for (unsigned u = 0;
5408 u < RT_ELEMENTS(aConvertOSTypes);
5409 ++u)
5410 {
5411 if (str == aConvertOSTypes[u].pcszOld)
5412 {
5413 str = aConvertOSTypes[u].pcszNew;
5414 break;
5415 }
5416 }
5417}
5418
5419/**
5420 * Called from the constructor to actually read in the \<Machine\> element
5421 * of a machine config file.
5422 * @param elmMachine
5423 */
5424void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
5425{
5426 Utf8Str strUUID;
5427 if ( elmMachine.getAttributeValue("uuid", strUUID)
5428 && elmMachine.getAttributeValue("name", machineUserData.strName))
5429 {
5430 parseUUID(uuid, strUUID, &elmMachine);
5431
5432 elmMachine.getAttributeValue("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
5433 elmMachine.getAttributeValue("nameSync", machineUserData.fNameSync);
5434
5435 Utf8Str str;
5436 elmMachine.getAttributeValue("Description", machineUserData.strDescription);
5437 elmMachine.getAttributeValue("OSType", machineUserData.strOsType);
5438 if (m->sv < SettingsVersion_v1_5)
5439 convertOldOSType_pre1_5(machineUserData.strOsType);
5440
5441 elmMachine.getAttributeValuePath("stateFile", strStateFile);
5442
5443 if (elmMachine.getAttributeValue("currentSnapshot", str))
5444 parseUUID(uuidCurrentSnapshot, str, &elmMachine);
5445
5446 elmMachine.getAttributeValuePath("snapshotFolder", machineUserData.strSnapshotFolder);
5447
5448 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
5449 fCurrentStateModified = true;
5450 if (elmMachine.getAttributeValue("lastStateChange", str))
5451 parseTimestamp(timeLastStateChange, str, &elmMachine);
5452 // constructor has called RTTimeNow(&timeLastStateChange) before
5453 if (elmMachine.getAttributeValue("aborted", fAborted))
5454 fAborted = true;
5455
5456 elmMachine.getAttributeValue("processPriority", machineUserData.strVMPriority);
5457
5458 str.setNull();
5459 elmMachine.getAttributeValue("icon", str);
5460 parseBase64(machineUserData.ovIcon, str, &elmMachine);
5461
5462 // parse Hardware before the other elements because other things depend on it
5463 const xml::ElementNode *pelmHardware;
5464 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
5465 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
5466 readHardware(*pelmHardware, hardwareMachine);
5467
5468 xml::NodesLoop nlRootChildren(elmMachine);
5469 const xml::ElementNode *pelmMachineChild;
5470 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
5471 {
5472 if (pelmMachineChild->nameEquals("ExtraData"))
5473 readExtraData(*pelmMachineChild,
5474 mapExtraDataItems);
5475 else if ( (m->sv < SettingsVersion_v1_7)
5476 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
5477 )
5478 readHardDiskAttachments_pre1_7(*pelmMachineChild, hardwareMachine.storage);
5479 else if ( (m->sv >= SettingsVersion_v1_7)
5480 && (pelmMachineChild->nameEquals("StorageControllers"))
5481 )
5482 readStorageControllers(*pelmMachineChild, hardwareMachine.storage);
5483 else if (pelmMachineChild->nameEquals("Snapshot"))
5484 {
5485 if (uuidCurrentSnapshot.isZero())
5486 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing"));
5487 bool foundCurrentSnapshot = false;
5488 Snapshot snap;
5489 // this will recurse into child snapshots, if necessary
5490 foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, 1, *pelmMachineChild, snap);
5491 if (!foundCurrentSnapshot)
5492 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute"));
5493 llFirstSnapshot.push_back(snap);
5494 }
5495 else if (pelmMachineChild->nameEquals("Description"))
5496 machineUserData.strDescription = pelmMachineChild->getValue();
5497 else if (pelmMachineChild->nameEquals("Teleporter"))
5498 readTeleporter(pelmMachineChild, &machineUserData);
5499 else if (pelmMachineChild->nameEquals("FaultTolerance"))
5500 {
5501 Utf8Str strFaultToleranceSate;
5502 if (pelmMachineChild->getAttributeValue("state", strFaultToleranceSate))
5503 {
5504 if (strFaultToleranceSate == "master")
5505 machineUserData.enmFaultToleranceState = FaultToleranceState_Master;
5506 else
5507 if (strFaultToleranceSate == "standby")
5508 machineUserData.enmFaultToleranceState = FaultToleranceState_Standby;
5509 else
5510 machineUserData.enmFaultToleranceState = FaultToleranceState_Inactive;
5511 }
5512 pelmMachineChild->getAttributeValue("port", machineUserData.uFaultTolerancePort);
5513 pelmMachineChild->getAttributeValue("address", machineUserData.strFaultToleranceAddress);
5514 pelmMachineChild->getAttributeValue("interval", machineUserData.uFaultToleranceInterval);
5515 pelmMachineChild->getAttributeValue("password", machineUserData.strFaultTolerancePassword);
5516 }
5517 else if (pelmMachineChild->nameEquals("MediaRegistry"))
5518 readMediaRegistry(*pelmMachineChild, mediaRegistry);
5519 else if (pelmMachineChild->nameEquals("Debugging"))
5520 readDebugging(pelmMachineChild, &debugging);
5521 else if (pelmMachineChild->nameEquals("Autostart"))
5522 readAutostart(pelmMachineChild, &autostart);
5523 else if (pelmMachineChild->nameEquals("Groups"))
5524 readGroups(pelmMachineChild, &machineUserData.llGroups);
5525 }
5526
5527 if (m->sv < SettingsVersion_v1_9)
5528 // go through Hardware once more to repair the settings controller structures
5529 // with data from old DVDDrive and FloppyDrive elements
5530 readDVDAndFloppies_pre1_9(*pelmHardware, hardwareMachine.storage);
5531 }
5532 else
5533 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
5534}
5535
5536/**
5537 * Creates a \<Hardware\> node under elmParent and then writes out the XML
5538 * keys under that. Called for both the \<Machine\> node and for snapshots.
5539 * @param elmParent
5540 * @param hw
5541 * @param fl
5542 * @param pllElementsWithUuidAttributes
5543 */
5544void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
5545 const Hardware &hw,
5546 uint32_t fl,
5547 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
5548{
5549 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
5550
5551 if ( m->sv >= SettingsVersion_v1_4
5552 && (m->sv < SettingsVersion_v1_7 ? hw.strVersion != "1" : hw.strVersion != "2"))
5553 pelmHardware->setAttribute("version", hw.strVersion);
5554
5555 if ((m->sv >= SettingsVersion_v1_9)
5556 && !hw.uuid.isZero()
5557 && hw.uuid.isValid()
5558 )
5559 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
5560
5561 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
5562
5563 if (!hw.fHardwareVirt)
5564 pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
5565 if (!hw.fNestedPaging)
5566 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
5567 if (!hw.fVPID)
5568 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
5569 if (!hw.fUnrestrictedExecution)
5570 pelmCPU->createChild("HardwareVirtExUX")->setAttribute("enabled", hw.fUnrestrictedExecution);
5571 // PAE has too crazy default handling, must always save this setting.
5572 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
5573 if (m->sv >= SettingsVersion_v1_16)
5574 {
5575 if (hw.fIBPBOnVMEntry || hw.fIBPBOnVMExit)
5576 {
5577 xml::ElementNode *pelmChild = pelmCPU->createChild("IBPBOn");
5578 if (hw.fIBPBOnVMExit)
5579 pelmChild->setAttribute("vmexit", hw.fIBPBOnVMExit);
5580 if (hw.fIBPBOnVMEntry)
5581 pelmChild->setAttribute("vmentry", hw.fIBPBOnVMEntry);
5582 }
5583 }
5584 if (m->sv >= SettingsVersion_v1_16 && hw.fSpecCtrl)
5585 pelmCPU->createChild("SpecCtrl")->setAttribute("enabled", hw.fSpecCtrl);
5586 if (m->sv >= SettingsVersion_v1_16 && hw.fSpecCtrlByHost)
5587 pelmCPU->createChild("SpecCtrlByHost")->setAttribute("enabled", hw.fSpecCtrlByHost);
5588 if (m->sv >= SettingsVersion_v1_17 && hw.fNestedHWVirt)
5589 pelmCPU->createChild("NestedHWVirt")->setAttribute("enabled", hw.fNestedHWVirt);
5590
5591 if (m->sv >= SettingsVersion_v1_14 && hw.enmLongMode != Hardware::LongMode_Legacy)
5592 {
5593 // LongMode has too crazy default handling, must always save this setting.
5594 pelmCPU->createChild("LongMode")->setAttribute("enabled", hw.enmLongMode == Hardware::LongMode_Enabled);
5595 }
5596
5597 if (hw.fTripleFaultReset)
5598 pelmCPU->createChild("TripleFaultReset")->setAttribute("enabled", hw.fTripleFaultReset);
5599 if (m->sv >= SettingsVersion_v1_14)
5600 {
5601 if (hw.fX2APIC)
5602 pelmCPU->createChild("X2APIC")->setAttribute("enabled", hw.fX2APIC);
5603 else if (!hw.fAPIC)
5604 pelmCPU->createChild("APIC")->setAttribute("enabled", hw.fAPIC);
5605 }
5606 if (hw.cCPUs > 1)
5607 pelmCPU->setAttribute("count", hw.cCPUs);
5608 if (hw.ulCpuExecutionCap != 100)
5609 pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap);
5610 if (hw.uCpuIdPortabilityLevel != 0)
5611 pelmCPU->setAttribute("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
5612 if (!hw.strCpuProfile.equals("host") && hw.strCpuProfile.isNotEmpty())
5613 pelmCPU->setAttribute("CpuProfile", hw.strCpuProfile);
5614
5615 // HardwareVirtExLargePages has too crazy default handling, must always save this setting.
5616 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
5617
5618 if (m->sv >= SettingsVersion_v1_9)
5619 {
5620 if (hw.fHardwareVirtForce)
5621 pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce);
5622 }
5623
5624 if (m->sv >= SettingsVersion_v1_9 && hw.fUseNativeApi)
5625 pelmCPU->createChild("HardwareVirtExUseNativeApi")->setAttribute("enabled", hw.fUseNativeApi);
5626
5627 if (m->sv >= SettingsVersion_v1_10)
5628 {
5629 if (hw.fCpuHotPlug)
5630 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
5631
5632 xml::ElementNode *pelmCpuTree = NULL;
5633 for (CpuList::const_iterator it = hw.llCpus.begin();
5634 it != hw.llCpus.end();
5635 ++it)
5636 {
5637 const Cpu &cpu = *it;
5638
5639 if (pelmCpuTree == NULL)
5640 pelmCpuTree = pelmCPU->createChild("CpuTree");
5641
5642 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
5643 pelmCpu->setAttribute("id", cpu.ulId);
5644 }
5645 }
5646
5647 xml::ElementNode *pelmCpuIdTree = NULL;
5648 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
5649 it != hw.llCpuIdLeafs.end();
5650 ++it)
5651 {
5652 const CpuIdLeaf &leaf = *it;
5653
5654 if (pelmCpuIdTree == NULL)
5655 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
5656
5657 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
5658 pelmCpuIdLeaf->setAttribute("id", leaf.idx);
5659 if (leaf.idxSub != 0)
5660 pelmCpuIdLeaf->setAttribute("subleaf", leaf.idxSub);
5661 pelmCpuIdLeaf->setAttribute("eax", leaf.uEax);
5662 pelmCpuIdLeaf->setAttribute("ebx", leaf.uEbx);
5663 pelmCpuIdLeaf->setAttribute("ecx", leaf.uEcx);
5664 pelmCpuIdLeaf->setAttribute("edx", leaf.uEdx);
5665 }
5666
5667 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
5668 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
5669 if (m->sv >= SettingsVersion_v1_10)
5670 {
5671 if (hw.fPageFusionEnabled)
5672 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
5673 }
5674
5675 if ( (m->sv >= SettingsVersion_v1_9)
5676 && (hw.firmwareType >= FirmwareType_EFI)
5677 )
5678 {
5679 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
5680 const char *pcszFirmware;
5681
5682 switch (hw.firmwareType)
5683 {
5684 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
5685 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
5686 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
5687 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
5688 default: pcszFirmware = "None"; break;
5689 }
5690 pelmFirmware->setAttribute("type", pcszFirmware);
5691 }
5692
5693 if ( m->sv >= SettingsVersion_v1_10
5694 && ( hw.pointingHIDType != PointingHIDType_PS2Mouse
5695 || hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard))
5696 {
5697 xml::ElementNode *pelmHID = pelmHardware->createChild("HID");
5698 const char *pcszHID;
5699
5700 if (hw.pointingHIDType != PointingHIDType_PS2Mouse)
5701 {
5702 switch (hw.pointingHIDType)
5703 {
5704 case PointingHIDType_USBMouse: pcszHID = "USBMouse"; break;
5705 case PointingHIDType_USBTablet: pcszHID = "USBTablet"; break;
5706 case PointingHIDType_PS2Mouse: pcszHID = "PS2Mouse"; break;
5707 case PointingHIDType_ComboMouse: pcszHID = "ComboMouse"; break;
5708 case PointingHIDType_USBMultiTouch: pcszHID = "USBMultiTouch";break;
5709 case PointingHIDType_None: pcszHID = "None"; break;
5710 default: Assert(false); pcszHID = "PS2Mouse"; break;
5711 }
5712 pelmHID->setAttribute("Pointing", pcszHID);
5713 }
5714
5715 if (hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard)
5716 {
5717 switch (hw.keyboardHIDType)
5718 {
5719 case KeyboardHIDType_USBKeyboard: pcszHID = "USBKeyboard"; break;
5720 case KeyboardHIDType_PS2Keyboard: pcszHID = "PS2Keyboard"; break;
5721 case KeyboardHIDType_ComboKeyboard: pcszHID = "ComboKeyboard"; break;
5722 case KeyboardHIDType_None: pcszHID = "None"; break;
5723 default: Assert(false); pcszHID = "PS2Keyboard"; break;
5724 }
5725 pelmHID->setAttribute("Keyboard", pcszHID);
5726 }
5727 }
5728
5729 if ( (m->sv >= SettingsVersion_v1_10)
5730 && hw.fHPETEnabled
5731 )
5732 {
5733 xml::ElementNode *pelmHPET = pelmHardware->createChild("HPET");
5734 pelmHPET->setAttribute("enabled", hw.fHPETEnabled);
5735 }
5736
5737 if ( (m->sv >= SettingsVersion_v1_11)
5738 )
5739 {
5740 if (hw.chipsetType != ChipsetType_PIIX3)
5741 {
5742 xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset");
5743 const char *pcszChipset;
5744
5745 switch (hw.chipsetType)
5746 {
5747 case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break;
5748 case ChipsetType_ICH9: pcszChipset = "ICH9"; break;
5749 default: Assert(false); pcszChipset = "PIIX3"; break;
5750 }
5751 pelmChipset->setAttribute("type", pcszChipset);
5752 }
5753 }
5754
5755 if ( (m->sv >= SettingsVersion_v1_15)
5756 && !hw.areParavirtDefaultSettings(m->sv)
5757 )
5758 {
5759 const char *pcszParavirtProvider;
5760 switch (hw.paravirtProvider)
5761 {
5762 case ParavirtProvider_None: pcszParavirtProvider = "None"; break;
5763 case ParavirtProvider_Default: pcszParavirtProvider = "Default"; break;
5764 case ParavirtProvider_Legacy: pcszParavirtProvider = "Legacy"; break;
5765 case ParavirtProvider_Minimal: pcszParavirtProvider = "Minimal"; break;
5766 case ParavirtProvider_HyperV: pcszParavirtProvider = "HyperV"; break;
5767 case ParavirtProvider_KVM: pcszParavirtProvider = "KVM"; break;
5768 default: Assert(false); pcszParavirtProvider = "None"; break;
5769 }
5770
5771 xml::ElementNode *pelmParavirt = pelmHardware->createChild("Paravirt");
5772 pelmParavirt->setAttribute("provider", pcszParavirtProvider);
5773
5774 if ( m->sv >= SettingsVersion_v1_16
5775 && hw.strParavirtDebug.isNotEmpty())
5776 pelmParavirt->setAttribute("debug", hw.strParavirtDebug);
5777 }
5778
5779 if (!hw.areBootOrderDefaultSettings())
5780 {
5781 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
5782 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
5783 it != hw.mapBootOrder.end();
5784 ++it)
5785 {
5786 uint32_t i = it->first;
5787 DeviceType_T type = it->second;
5788 const char *pcszDevice;
5789
5790 switch (type)
5791 {
5792 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
5793 case DeviceType_DVD: pcszDevice = "DVD"; break;
5794 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
5795 case DeviceType_Network: pcszDevice = "Network"; break;
5796 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
5797 }
5798
5799 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
5800 pelmOrder->setAttribute("position",
5801 i + 1); // XML is 1-based but internal data is 0-based
5802 pelmOrder->setAttribute("device", pcszDevice);
5803 }
5804 }
5805
5806 if (!hw.areDisplayDefaultSettings())
5807 {
5808 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
5809 if (hw.graphicsControllerType != GraphicsControllerType_VBoxVGA)
5810 {
5811 const char *pcszGraphics;
5812 switch (hw.graphicsControllerType)
5813 {
5814 case GraphicsControllerType_VBoxVGA: pcszGraphics = "VBoxVGA"; break;
5815 case GraphicsControllerType_VMSVGA: pcszGraphics = "VMSVGA"; break;
5816 case GraphicsControllerType_VBoxSVGA: pcszGraphics = "VBoxSVGA"; break;
5817 default: /*case GraphicsControllerType_Null:*/ pcszGraphics = "None"; break;
5818 }
5819 pelmDisplay->setAttribute("controller", pcszGraphics);
5820 }
5821 if (hw.ulVRAMSizeMB != 8)
5822 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
5823 if (hw.cMonitors > 1)
5824 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
5825 if (hw.fAccelerate3D)
5826 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
5827
5828 if (m->sv >= SettingsVersion_v1_8)
5829 {
5830 if (hw.fAccelerate2DVideo)
5831 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
5832 }
5833 }
5834
5835 if (m->sv >= SettingsVersion_v1_14 && !hw.recordingSettings.areDefaultSettings())
5836 {
5837 xml::ElementNode *pelmVideoCapture = pelmHardware->createChild("VideoCapture");
5838
5839 if (hw.recordingSettings.fEnabled)
5840 pelmVideoCapture->setAttribute("enabled", hw.recordingSettings.fEnabled);
5841
5842 /* Right now I don't want to bump the settings version, so just convert the enabled
5843 * screens to the former uint64t_t bit array and vice versa. */
5844 uint64_t u64VideoCaptureScreens = 0;
5845 RecordingScreenMap::const_iterator itScreen = hw.recordingSettings.mapScreens.begin();
5846 while (itScreen != hw.recordingSettings.mapScreens.end())
5847 {
5848 if (itScreen->second.fEnabled)
5849 u64VideoCaptureScreens |= RT_BIT_64(itScreen->first);
5850 ++itScreen;
5851 }
5852
5853 if (u64VideoCaptureScreens)
5854 pelmVideoCapture->setAttribute("screens", u64VideoCaptureScreens);
5855
5856 /* At the moment we only support one capturing configuration, that is, all screens
5857 * have the same configuration. So load/save to/from screen 0. */
5858 Assert(hw.recordingSettings.mapScreens.size());
5859 const RecordingScreenMap::const_iterator itScreen0Settings = hw.recordingSettings.mapScreens.find(0);
5860 Assert(itScreen0Settings != hw.recordingSettings.mapScreens.end());
5861
5862 if (itScreen0Settings->second.ulMaxTimeS)
5863 pelmVideoCapture->setAttribute("maxTime", itScreen0Settings->second.ulMaxTimeS);
5864 if (itScreen0Settings->second.strOptions.isNotEmpty())
5865 pelmVideoCapture->setAttributePath("options", itScreen0Settings->second.strOptions);
5866
5867 if (!itScreen0Settings->second.File.strName.isEmpty())
5868 pelmVideoCapture->setAttributePath("file", itScreen0Settings->second.File.strName);
5869 if (itScreen0Settings->second.File.ulMaxSizeMB)
5870 pelmVideoCapture->setAttribute("maxSize", itScreen0Settings->second.File.ulMaxSizeMB);
5871
5872 if ( itScreen0Settings->second.Video.ulWidth != 1024
5873 || itScreen0Settings->second.Video.ulHeight != 768)
5874 {
5875 pelmVideoCapture->setAttribute("horzRes", itScreen0Settings->second.Video.ulWidth);
5876 pelmVideoCapture->setAttribute("vertRes", itScreen0Settings->second.Video.ulHeight);
5877 }
5878 if (itScreen0Settings->second.Video.ulRate != 512)
5879 pelmVideoCapture->setAttribute("rate", itScreen0Settings->second.Video.ulRate);
5880 if (itScreen0Settings->second.Video.ulFPS)
5881 pelmVideoCapture->setAttribute("fps", itScreen0Settings->second.Video.ulFPS);
5882 }
5883
5884 if (!hw.vrdeSettings.areDefaultSettings(m->sv))
5885 {
5886 xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay");
5887 if (m->sv < SettingsVersion_v1_16 ? !hw.vrdeSettings.fEnabled : hw.vrdeSettings.fEnabled)
5888 pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled);
5889 if (m->sv < SettingsVersion_v1_11)
5890 {
5891 /* In VBox 4.0 these attributes are replaced with "Properties". */
5892 Utf8Str strPort;
5893 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports");
5894 if (it != hw.vrdeSettings.mapProperties.end())
5895 strPort = it->second;
5896 if (!strPort.length())
5897 strPort = "3389";
5898 pelmVRDE->setAttribute("port", strPort);
5899
5900 Utf8Str strAddress;
5901 it = hw.vrdeSettings.mapProperties.find("TCP/Address");
5902 if (it != hw.vrdeSettings.mapProperties.end())
5903 strAddress = it->second;
5904 if (strAddress.length())
5905 pelmVRDE->setAttribute("netAddress", strAddress);
5906 }
5907 if (hw.vrdeSettings.authType != AuthType_Null)
5908 {
5909 const char *pcszAuthType;
5910 switch (hw.vrdeSettings.authType)
5911 {
5912 case AuthType_Guest: pcszAuthType = "Guest"; break;
5913 case AuthType_External: pcszAuthType = "External"; break;
5914 default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break;
5915 }
5916 pelmVRDE->setAttribute("authType", pcszAuthType);
5917 }
5918
5919 if (hw.vrdeSettings.ulAuthTimeout != 0 && hw.vrdeSettings.ulAuthTimeout != 5000)
5920 pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout);
5921 if (hw.vrdeSettings.fAllowMultiConnection)
5922 pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
5923 if (hw.vrdeSettings.fReuseSingleConnection)
5924 pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
5925
5926 if (m->sv == SettingsVersion_v1_10)
5927 {
5928 xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel");
5929
5930 /* In 4.0 videochannel settings were replaced with properties, so look at properties. */
5931 Utf8Str str;
5932 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
5933 if (it != hw.vrdeSettings.mapProperties.end())
5934 str = it->second;
5935 bool fVideoChannel = RTStrICmp(str.c_str(), "true") == 0
5936 || RTStrCmp(str.c_str(), "1") == 0;
5937 pelmVideoChannel->setAttribute("enabled", fVideoChannel);
5938
5939 it = hw.vrdeSettings.mapProperties.find("VideoChannel/Quality");
5940 if (it != hw.vrdeSettings.mapProperties.end())
5941 str = it->second;
5942 uint32_t ulVideoChannelQuality = RTStrToUInt32(str.c_str()); /* This returns 0 on invalid string which is ok. */
5943 if (ulVideoChannelQuality == 0)
5944 ulVideoChannelQuality = 75;
5945 else
5946 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
5947 pelmVideoChannel->setAttribute("quality", ulVideoChannelQuality);
5948 }
5949 if (m->sv >= SettingsVersion_v1_11)
5950 {
5951 if (hw.vrdeSettings.strAuthLibrary.length())
5952 pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary);
5953 if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty())
5954 pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
5955 if (hw.vrdeSettings.mapProperties.size() > 0)
5956 {
5957 xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties");
5958 for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin();
5959 it != hw.vrdeSettings.mapProperties.end();
5960 ++it)
5961 {
5962 const Utf8Str &strName = it->first;
5963 const Utf8Str &strValue = it->second;
5964 xml::ElementNode *pelm = pelmProperties->createChild("Property");
5965 pelm->setAttribute("name", strName);
5966 pelm->setAttribute("value", strValue);
5967 }
5968 }
5969 }
5970 }
5971
5972 if (!hw.biosSettings.areDefaultSettings())
5973 {
5974 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
5975 if (!hw.biosSettings.fACPIEnabled)
5976 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
5977 if (hw.biosSettings.fIOAPICEnabled)
5978 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
5979 if (hw.biosSettings.apicMode != APICMode_APIC)
5980 {
5981 const char *pcszAPIC;
5982 switch (hw.biosSettings.apicMode)
5983 {
5984 case APICMode_Disabled:
5985 pcszAPIC = "Disabled";
5986 break;
5987 case APICMode_APIC:
5988 default:
5989 pcszAPIC = "APIC";
5990 break;
5991 case APICMode_X2APIC:
5992 pcszAPIC = "X2APIC";
5993 break;
5994 }
5995 pelmBIOS->createChild("APIC")->setAttribute("mode", pcszAPIC);
5996 }
5997
5998 if ( !hw.biosSettings.fLogoFadeIn
5999 || !hw.biosSettings.fLogoFadeOut
6000 || hw.biosSettings.ulLogoDisplayTime
6001 || !hw.biosSettings.strLogoImagePath.isEmpty())
6002 {
6003 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
6004 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
6005 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
6006 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
6007 if (!hw.biosSettings.strLogoImagePath.isEmpty())
6008 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
6009 }
6010
6011 if (hw.biosSettings.biosBootMenuMode != BIOSBootMenuMode_MessageAndMenu)
6012 {
6013 const char *pcszBootMenu;
6014 switch (hw.biosSettings.biosBootMenuMode)
6015 {
6016 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
6017 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
6018 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
6019 }
6020 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
6021 }
6022 if (hw.biosSettings.llTimeOffset)
6023 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
6024 if (hw.biosSettings.fPXEDebugEnabled)
6025 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
6026 }
6027
6028 if (m->sv < SettingsVersion_v1_9)
6029 {
6030 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
6031 // run thru the storage controllers to see if we have a DVD or floppy drives
6032 size_t cDVDs = 0;
6033 size_t cFloppies = 0;
6034
6035 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
6036 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
6037
6038 for (StorageControllersList::const_iterator it = hw.storage.llStorageControllers.begin();
6039 it != hw.storage.llStorageControllers.end();
6040 ++it)
6041 {
6042 const StorageController &sctl = *it;
6043 // in old settings format, the DVD drive could only have been under the IDE controller
6044 if (sctl.storageBus == StorageBus_IDE)
6045 {
6046 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
6047 it2 != sctl.llAttachedDevices.end();
6048 ++it2)
6049 {
6050 const AttachedDevice &att = *it2;
6051 if (att.deviceType == DeviceType_DVD)
6052 {
6053 if (cDVDs > 0)
6054 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
6055
6056 ++cDVDs;
6057
6058 pelmDVD->setAttribute("passthrough", att.fPassThrough);
6059 if (att.fTempEject)
6060 pelmDVD->setAttribute("tempeject", att.fTempEject);
6061
6062 if (!att.uuid.isZero() && att.uuid.isValid())
6063 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
6064 else if (att.strHostDriveSrc.length())
6065 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6066 }
6067 }
6068 }
6069 else if (sctl.storageBus == StorageBus_Floppy)
6070 {
6071 size_t cFloppiesHere = sctl.llAttachedDevices.size();
6072 if (cFloppiesHere > 1)
6073 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
6074 if (cFloppiesHere)
6075 {
6076 const AttachedDevice &att = sctl.llAttachedDevices.front();
6077 pelmFloppy->setAttribute("enabled", true);
6078
6079 if (!att.uuid.isZero() && att.uuid.isValid())
6080 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
6081 else if (att.strHostDriveSrc.length())
6082 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6083 }
6084
6085 cFloppies += cFloppiesHere;
6086 }
6087 }
6088
6089 if (cFloppies == 0)
6090 pelmFloppy->setAttribute("enabled", false);
6091 else if (cFloppies > 1)
6092 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
6093 }
6094
6095 if (m->sv < SettingsVersion_v1_14)
6096 {
6097 bool fOhciEnabled = false;
6098 bool fEhciEnabled = false;
6099 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
6100
6101 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
6102 it != hw.usbSettings.llUSBControllers.end();
6103 ++it)
6104 {
6105 const USBController &ctrl = *it;
6106
6107 switch (ctrl.enmType)
6108 {
6109 case USBControllerType_OHCI:
6110 fOhciEnabled = true;
6111 break;
6112 case USBControllerType_EHCI:
6113 fEhciEnabled = true;
6114 break;
6115 default:
6116 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
6117 }
6118 }
6119
6120 pelmUSB->setAttribute("enabled", fOhciEnabled);
6121 pelmUSB->setAttribute("enabledEhci", fEhciEnabled);
6122
6123 buildUSBDeviceFilters(*pelmUSB, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
6124 }
6125 else
6126 {
6127 if ( hw.usbSettings.llUSBControllers.size()
6128 || hw.usbSettings.llDeviceFilters.size())
6129 {
6130 xml::ElementNode *pelmUSB = pelmHardware->createChild("USB");
6131 if (hw.usbSettings.llUSBControllers.size())
6132 {
6133 xml::ElementNode *pelmCtrls = pelmUSB->createChild("Controllers");
6134
6135 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
6136 it != hw.usbSettings.llUSBControllers.end();
6137 ++it)
6138 {
6139 const USBController &ctrl = *it;
6140 com::Utf8Str strType;
6141 xml::ElementNode *pelmCtrl = pelmCtrls->createChild("Controller");
6142
6143 switch (ctrl.enmType)
6144 {
6145 case USBControllerType_OHCI:
6146 strType = "OHCI";
6147 break;
6148 case USBControllerType_EHCI:
6149 strType = "EHCI";
6150 break;
6151 case USBControllerType_XHCI:
6152 strType = "XHCI";
6153 break;
6154 default:
6155 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
6156 }
6157
6158 pelmCtrl->setAttribute("name", ctrl.strName);
6159 pelmCtrl->setAttribute("type", strType);
6160 }
6161 }
6162
6163 if (hw.usbSettings.llDeviceFilters.size())
6164 {
6165 xml::ElementNode *pelmFilters = pelmUSB->createChild("DeviceFilters");
6166 buildUSBDeviceFilters(*pelmFilters, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
6167 }
6168 }
6169 }
6170
6171 if ( hw.llNetworkAdapters.size()
6172 && !hw.areAllNetworkAdaptersDefaultSettings(m->sv))
6173 {
6174 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
6175 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
6176 it != hw.llNetworkAdapters.end();
6177 ++it)
6178 {
6179 const NetworkAdapter &nic = *it;
6180
6181 if (!nic.areDefaultSettings(m->sv))
6182 {
6183 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
6184 pelmAdapter->setAttribute("slot", nic.ulSlot);
6185 if (nic.fEnabled)
6186 pelmAdapter->setAttribute("enabled", nic.fEnabled);
6187 if (!nic.strMACAddress.isEmpty())
6188 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
6189 if ( (m->sv >= SettingsVersion_v1_16 && !nic.fCableConnected)
6190 || (m->sv < SettingsVersion_v1_16 && nic.fCableConnected))
6191 pelmAdapter->setAttribute("cable", nic.fCableConnected);
6192 if (nic.ulLineSpeed)
6193 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
6194 if (nic.ulBootPriority != 0)
6195 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
6196 if (nic.fTraceEnabled)
6197 {
6198 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
6199 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
6200 }
6201 if (nic.strBandwidthGroup.isNotEmpty())
6202 pelmAdapter->setAttribute("bandwidthGroup", nic.strBandwidthGroup);
6203
6204 const char *pszPolicy;
6205 switch (nic.enmPromiscModePolicy)
6206 {
6207 case NetworkAdapterPromiscModePolicy_Deny: pszPolicy = NULL; break;
6208 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPolicy = "AllowNetwork"; break;
6209 case NetworkAdapterPromiscModePolicy_AllowAll: pszPolicy = "AllowAll"; break;
6210 default: pszPolicy = NULL; AssertFailed(); break;
6211 }
6212 if (pszPolicy)
6213 pelmAdapter->setAttribute("promiscuousModePolicy", pszPolicy);
6214
6215 if ( (m->sv >= SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C973)
6216 || (m->sv < SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C970A))
6217 {
6218 const char *pcszType;
6219 switch (nic.type)
6220 {
6221 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
6222 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
6223 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
6224 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
6225 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
6226 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
6227 }
6228 pelmAdapter->setAttribute("type", pcszType);
6229 }
6230
6231 xml::ElementNode *pelmNAT;
6232 if (m->sv < SettingsVersion_v1_10)
6233 {
6234 switch (nic.mode)
6235 {
6236 case NetworkAttachmentType_NAT:
6237 pelmNAT = pelmAdapter->createChild("NAT");
6238 if (nic.nat.strNetwork.length())
6239 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6240 break;
6241
6242 case NetworkAttachmentType_Bridged:
6243 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
6244 break;
6245
6246 case NetworkAttachmentType_Internal:
6247 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
6248 break;
6249
6250 case NetworkAttachmentType_HostOnly:
6251 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
6252 break;
6253
6254 default: /*case NetworkAttachmentType_Null:*/
6255 break;
6256 }
6257 }
6258 else
6259 {
6260 /* m->sv >= SettingsVersion_v1_10 */
6261 if (!nic.areDisabledDefaultSettings())
6262 {
6263 xml::ElementNode *pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
6264 if (nic.mode != NetworkAttachmentType_NAT)
6265 buildNetworkXML(NetworkAttachmentType_NAT, false, *pelmDisabledNode, nic);
6266 if (nic.mode != NetworkAttachmentType_Bridged)
6267 buildNetworkXML(NetworkAttachmentType_Bridged, false, *pelmDisabledNode, nic);
6268 if (nic.mode != NetworkAttachmentType_Internal)
6269 buildNetworkXML(NetworkAttachmentType_Internal, false, *pelmDisabledNode, nic);
6270 if (nic.mode != NetworkAttachmentType_HostOnly)
6271 buildNetworkXML(NetworkAttachmentType_HostOnly, false, *pelmDisabledNode, nic);
6272 if (nic.mode != NetworkAttachmentType_Generic)
6273 buildNetworkXML(NetworkAttachmentType_Generic, false, *pelmDisabledNode, nic);
6274 if (nic.mode != NetworkAttachmentType_NATNetwork)
6275 buildNetworkXML(NetworkAttachmentType_NATNetwork, false, *pelmDisabledNode, nic);
6276 }
6277 buildNetworkXML(nic.mode, true, *pelmAdapter, nic);
6278 }
6279 }
6280 }
6281 }
6282
6283 if (hw.llSerialPorts.size())
6284 {
6285 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
6286 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
6287 it != hw.llSerialPorts.end();
6288 ++it)
6289 {
6290 const SerialPort &port = *it;
6291 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6292 pelmPort->setAttribute("slot", port.ulSlot);
6293 pelmPort->setAttribute("enabled", port.fEnabled);
6294 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6295 pelmPort->setAttribute("IRQ", port.ulIRQ);
6296
6297 const char *pcszHostMode;
6298 switch (port.portMode)
6299 {
6300 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
6301 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
6302 case PortMode_TCP: pcszHostMode = "TCP"; break;
6303 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
6304 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
6305 }
6306 switch (port.portMode)
6307 {
6308 case PortMode_TCP:
6309 case PortMode_HostPipe:
6310 pelmPort->setAttribute("server", port.fServer);
6311 RT_FALL_THRU();
6312 case PortMode_HostDevice:
6313 case PortMode_RawFile:
6314 pelmPort->setAttribute("path", port.strPath);
6315 break;
6316
6317 default:
6318 break;
6319 }
6320 pelmPort->setAttribute("hostMode", pcszHostMode);
6321
6322 if ( m->sv >= SettingsVersion_v1_17
6323 && port.uartType != UartType_U16550A)
6324 {
6325 const char *pcszUartType;
6326
6327 switch (port.uartType)
6328 {
6329 case UartType_U16450: pcszUartType = "16450"; break;
6330 case UartType_U16550A: pcszUartType = "16550A"; break;
6331 case UartType_U16750: pcszUartType = "16750"; break;
6332 default: pcszUartType = "16550A"; break;
6333 }
6334 pelmPort->setAttribute("uartType", pcszUartType);
6335 }
6336 }
6337 }
6338
6339 if (hw.llParallelPorts.size())
6340 {
6341 xml::ElementNode *pelmPorts = pelmHardware->createChild("LPT");
6342 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
6343 it != hw.llParallelPorts.end();
6344 ++it)
6345 {
6346 const ParallelPort &port = *it;
6347 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6348 pelmPort->setAttribute("slot", port.ulSlot);
6349 pelmPort->setAttribute("enabled", port.fEnabled);
6350 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6351 pelmPort->setAttribute("IRQ", port.ulIRQ);
6352 if (port.strPath.length())
6353 pelmPort->setAttribute("path", port.strPath);
6354 }
6355 }
6356
6357 /* Always write the AudioAdapter config, intentionally not checking if
6358 * the settings are at the default, because that would be problematic
6359 * for the configured host driver type, which would automatically change
6360 * if the default host driver is detected differently. */
6361 {
6362 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
6363
6364 const char *pcszController;
6365 switch (hw.audioAdapter.controllerType)
6366 {
6367 case AudioControllerType_SB16:
6368 pcszController = "SB16";
6369 break;
6370 case AudioControllerType_HDA:
6371 if (m->sv >= SettingsVersion_v1_11)
6372 {
6373 pcszController = "HDA";
6374 break;
6375 }
6376 RT_FALL_THRU();
6377 case AudioControllerType_AC97:
6378 default:
6379 pcszController = NULL;
6380 break;
6381 }
6382 if (pcszController)
6383 pelmAudio->setAttribute("controller", pcszController);
6384
6385 const char *pcszCodec;
6386 switch (hw.audioAdapter.codecType)
6387 {
6388 /* Only write out the setting for non-default AC'97 codec
6389 * and leave the rest alone.
6390 */
6391#if 0
6392 case AudioCodecType_SB16:
6393 pcszCodec = "SB16";
6394 break;
6395 case AudioCodecType_STAC9221:
6396 pcszCodec = "STAC9221";
6397 break;
6398 case AudioCodecType_STAC9700:
6399 pcszCodec = "STAC9700";
6400 break;
6401#endif
6402 case AudioCodecType_AD1980:
6403 pcszCodec = "AD1980";
6404 break;
6405 default:
6406 /* Don't write out anything if unknown. */
6407 pcszCodec = NULL;
6408 }
6409 if (pcszCodec)
6410 pelmAudio->setAttribute("codec", pcszCodec);
6411
6412 const char *pcszDriver;
6413 switch (hw.audioAdapter.driverType)
6414 {
6415 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
6416 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
6417 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
6418 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
6419 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
6420 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
6421 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
6422 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
6423 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
6424 }
6425 /* Deliberately have the audio driver explicitly in the config file,
6426 * otherwise an unwritten default driver triggers auto-detection. */
6427 pelmAudio->setAttribute("driver", pcszDriver);
6428
6429 if (hw.audioAdapter.fEnabled || m->sv < SettingsVersion_v1_16)
6430 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
6431
6432 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledIn)
6433 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledIn))
6434 pelmAudio->setAttribute("enabledIn", hw.audioAdapter.fEnabledIn);
6435
6436 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledOut)
6437 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledOut))
6438 pelmAudio->setAttribute("enabledOut", hw.audioAdapter.fEnabledOut);
6439
6440 if (m->sv >= SettingsVersion_v1_15 && hw.audioAdapter.properties.size() > 0)
6441 {
6442 for (StringsMap::const_iterator it = hw.audioAdapter.properties.begin();
6443 it != hw.audioAdapter.properties.end();
6444 ++it)
6445 {
6446 const Utf8Str &strName = it->first;
6447 const Utf8Str &strValue = it->second;
6448 xml::ElementNode *pelm = pelmAudio->createChild("Property");
6449 pelm->setAttribute("name", strName);
6450 pelm->setAttribute("value", strValue);
6451 }
6452 }
6453 }
6454
6455 if (m->sv >= SettingsVersion_v1_10 && machineUserData.fRTCUseUTC)
6456 {
6457 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
6458 pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local");
6459 }
6460
6461 if (hw.llSharedFolders.size())
6462 {
6463 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
6464 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
6465 it != hw.llSharedFolders.end();
6466 ++it)
6467 {
6468 const SharedFolder &sf = *it;
6469 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
6470 pelmThis->setAttribute("name", sf.strName);
6471 pelmThis->setAttribute("hostPath", sf.strHostPath);
6472 pelmThis->setAttribute("writable", sf.fWritable);
6473 pelmThis->setAttribute("autoMount", sf.fAutoMount);
6474 if (sf.strAutoMountPoint.isNotEmpty())
6475 pelmThis->setAttribute("autoMountPoint", sf.strAutoMountPoint);
6476 }
6477 }
6478
6479 if (hw.clipboardMode != ClipboardMode_Disabled)
6480 {
6481 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
6482 const char *pcszClip;
6483 switch (hw.clipboardMode)
6484 {
6485 default: /*case ClipboardMode_Disabled:*/ pcszClip = "Disabled"; break;
6486 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
6487 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
6488 case ClipboardMode_Bidirectional: pcszClip = "Bidirectional"; break;
6489 }
6490 pelmClip->setAttribute("mode", pcszClip);
6491 }
6492
6493 if (hw.dndMode != DnDMode_Disabled)
6494 {
6495 xml::ElementNode *pelmDragAndDrop = pelmHardware->createChild("DragAndDrop");
6496 const char *pcszDragAndDrop;
6497 switch (hw.dndMode)
6498 {
6499 default: /*case DnDMode_Disabled:*/ pcszDragAndDrop = "Disabled"; break;
6500 case DnDMode_HostToGuest: pcszDragAndDrop = "HostToGuest"; break;
6501 case DnDMode_GuestToHost: pcszDragAndDrop = "GuestToHost"; break;
6502 case DnDMode_Bidirectional: pcszDragAndDrop = "Bidirectional"; break;
6503 }
6504 pelmDragAndDrop->setAttribute("mode", pcszDragAndDrop);
6505 }
6506
6507 if ( m->sv >= SettingsVersion_v1_10
6508 && !hw.ioSettings.areDefaultSettings())
6509 {
6510 xml::ElementNode *pelmIO = pelmHardware->createChild("IO");
6511 xml::ElementNode *pelmIOCache;
6512
6513 if (!hw.ioSettings.areDefaultSettings())
6514 {
6515 pelmIOCache = pelmIO->createChild("IoCache");
6516 if (!hw.ioSettings.fIOCacheEnabled)
6517 pelmIOCache->setAttribute("enabled", hw.ioSettings.fIOCacheEnabled);
6518 if (hw.ioSettings.ulIOCacheSize != 5)
6519 pelmIOCache->setAttribute("size", hw.ioSettings.ulIOCacheSize);
6520 }
6521
6522 if ( m->sv >= SettingsVersion_v1_11
6523 && hw.ioSettings.llBandwidthGroups.size())
6524 {
6525 xml::ElementNode *pelmBandwidthGroups = pelmIO->createChild("BandwidthGroups");
6526 for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin();
6527 it != hw.ioSettings.llBandwidthGroups.end();
6528 ++it)
6529 {
6530 const BandwidthGroup &gr = *it;
6531 const char *pcszType;
6532 xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup");
6533 pelmThis->setAttribute("name", gr.strName);
6534 switch (gr.enmType)
6535 {
6536 case BandwidthGroupType_Network: pcszType = "Network"; break;
6537 default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break;
6538 }
6539 pelmThis->setAttribute("type", pcszType);
6540 if (m->sv >= SettingsVersion_v1_13)
6541 pelmThis->setAttribute("maxBytesPerSec", gr.cMaxBytesPerSec);
6542 else
6543 pelmThis->setAttribute("maxMbPerSec", gr.cMaxBytesPerSec / _1M);
6544 }
6545 }
6546 }
6547
6548 if ( m->sv >= SettingsVersion_v1_12
6549 && hw.pciAttachments.size())
6550 {
6551 xml::ElementNode *pelmPCI = pelmHardware->createChild("HostPci");
6552 xml::ElementNode *pelmPCIDevices = pelmPCI->createChild("Devices");
6553
6554 for (HostPCIDeviceAttachmentList::const_iterator it = hw.pciAttachments.begin();
6555 it != hw.pciAttachments.end();
6556 ++it)
6557 {
6558 const HostPCIDeviceAttachment &hpda = *it;
6559
6560 xml::ElementNode *pelmThis = pelmPCIDevices->createChild("Device");
6561
6562 pelmThis->setAttribute("host", hpda.uHostAddress);
6563 pelmThis->setAttribute("guest", hpda.uGuestAddress);
6564 pelmThis->setAttribute("name", hpda.strDeviceName);
6565 }
6566 }
6567
6568 if ( m->sv >= SettingsVersion_v1_12
6569 && hw.fEmulatedUSBCardReader)
6570 {
6571 xml::ElementNode *pelmEmulatedUSB = pelmHardware->createChild("EmulatedUSB");
6572
6573 xml::ElementNode *pelmCardReader = pelmEmulatedUSB->createChild("CardReader");
6574 pelmCardReader->setAttribute("enabled", hw.fEmulatedUSBCardReader);
6575 }
6576
6577 if ( m->sv >= SettingsVersion_v1_14
6578 && !hw.strDefaultFrontend.isEmpty())
6579 {
6580 xml::ElementNode *pelmFrontend = pelmHardware->createChild("Frontend");
6581 xml::ElementNode *pelmDefault = pelmFrontend->createChild("Default");
6582 pelmDefault->setAttribute("type", hw.strDefaultFrontend);
6583 }
6584
6585 if (hw.ulMemoryBalloonSize)
6586 {
6587 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
6588 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
6589 }
6590
6591 if (hw.llGuestProperties.size())
6592 {
6593 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
6594 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
6595 it != hw.llGuestProperties.end();
6596 ++it)
6597 {
6598 const GuestProperty &prop = *it;
6599 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
6600 pelmProp->setAttribute("name", prop.strName);
6601 pelmProp->setAttribute("value", prop.strValue);
6602 pelmProp->setAttribute("timestamp", prop.timestamp);
6603 pelmProp->setAttribute("flags", prop.strFlags);
6604 }
6605 }
6606
6607 /** @todo In the future (6.0?) place the storage controllers under \<Hardware\>, because
6608 * this is where it always should've been. What else than hardware are they? */
6609 xml::ElementNode &elmStorageParent = (m->sv > SettingsVersion_Future) ? *pelmHardware : elmParent;
6610 buildStorageControllersXML(elmStorageParent,
6611 hw.storage,
6612 !!(fl & BuildMachineXML_SkipRemovableMedia),
6613 pllElementsWithUuidAttributes);
6614}
6615
6616/**
6617 * Fill a \<Network\> node. Only relevant for XML version >= v1_10.
6618 * @param mode
6619 * @param fEnabled
6620 * @param elmParent
6621 * @param nic
6622 */
6623void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
6624 bool fEnabled,
6625 xml::ElementNode &elmParent,
6626 const NetworkAdapter &nic)
6627{
6628 switch (mode)
6629 {
6630 case NetworkAttachmentType_NAT:
6631 // For the currently active network attachment type we have to
6632 // generate the tag, otherwise the attachment type is lost.
6633 if (fEnabled || !nic.nat.areDefaultSettings())
6634 {
6635 xml::ElementNode *pelmNAT = elmParent.createChild("NAT");
6636
6637 if (!nic.nat.areDefaultSettings())
6638 {
6639 if (nic.nat.strNetwork.length())
6640 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6641 if (nic.nat.strBindIP.length())
6642 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
6643 if (nic.nat.u32Mtu)
6644 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
6645 if (nic.nat.u32SockRcv)
6646 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
6647 if (nic.nat.u32SockSnd)
6648 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
6649 if (nic.nat.u32TcpRcv)
6650 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
6651 if (nic.nat.u32TcpSnd)
6652 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
6653 if (!nic.nat.areDNSDefaultSettings())
6654 {
6655 xml::ElementNode *pelmDNS = pelmNAT->createChild("DNS");
6656 if (!nic.nat.fDNSPassDomain)
6657 pelmDNS->setAttribute("pass-domain", nic.nat.fDNSPassDomain);
6658 if (nic.nat.fDNSProxy)
6659 pelmDNS->setAttribute("use-proxy", nic.nat.fDNSProxy);
6660 if (nic.nat.fDNSUseHostResolver)
6661 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDNSUseHostResolver);
6662 }
6663
6664 if (!nic.nat.areAliasDefaultSettings())
6665 {
6666 xml::ElementNode *pelmAlias = pelmNAT->createChild("Alias");
6667 if (nic.nat.fAliasLog)
6668 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
6669 if (nic.nat.fAliasProxyOnly)
6670 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
6671 if (nic.nat.fAliasUseSamePorts)
6672 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
6673 }
6674
6675 if (!nic.nat.areTFTPDefaultSettings())
6676 {
6677 xml::ElementNode *pelmTFTP;
6678 pelmTFTP = pelmNAT->createChild("TFTP");
6679 if (nic.nat.strTFTPPrefix.length())
6680 pelmTFTP->setAttribute("prefix", nic.nat.strTFTPPrefix);
6681 if (nic.nat.strTFTPBootFile.length())
6682 pelmTFTP->setAttribute("boot-file", nic.nat.strTFTPBootFile);
6683 if (nic.nat.strTFTPNextServer.length())
6684 pelmTFTP->setAttribute("next-server", nic.nat.strTFTPNextServer);
6685 }
6686 buildNATForwardRulesMap(*pelmNAT, nic.nat.mapRules);
6687 }
6688 }
6689 break;
6690
6691 case NetworkAttachmentType_Bridged:
6692 // For the currently active network attachment type we have to
6693 // generate the tag, otherwise the attachment type is lost.
6694 if (fEnabled || !nic.strBridgedName.isEmpty())
6695 {
6696 xml::ElementNode *pelmMode = elmParent.createChild("BridgedInterface");
6697 if (!nic.strBridgedName.isEmpty())
6698 pelmMode->setAttribute("name", nic.strBridgedName);
6699 }
6700 break;
6701
6702 case NetworkAttachmentType_Internal:
6703 // For the currently active network attachment type we have to
6704 // generate the tag, otherwise the attachment type is lost.
6705 if (fEnabled || !nic.strInternalNetworkName.isEmpty())
6706 {
6707 xml::ElementNode *pelmMode = elmParent.createChild("InternalNetwork");
6708 if (!nic.strInternalNetworkName.isEmpty())
6709 pelmMode->setAttribute("name", nic.strInternalNetworkName);
6710 }
6711 break;
6712
6713 case NetworkAttachmentType_HostOnly:
6714 // For the currently active network attachment type we have to
6715 // generate the tag, otherwise the attachment type is lost.
6716 if (fEnabled || !nic.strHostOnlyName.isEmpty())
6717 {
6718 xml::ElementNode *pelmMode = elmParent.createChild("HostOnlyInterface");
6719 if (!nic.strHostOnlyName.isEmpty())
6720 pelmMode->setAttribute("name", nic.strHostOnlyName);
6721 }
6722 break;
6723
6724 case NetworkAttachmentType_Generic:
6725 // For the currently active network attachment type we have to
6726 // generate the tag, otherwise the attachment type is lost.
6727 if (fEnabled || !nic.areGenericDriverDefaultSettings())
6728 {
6729 xml::ElementNode *pelmMode = elmParent.createChild("GenericInterface");
6730 if (!nic.areGenericDriverDefaultSettings())
6731 {
6732 pelmMode->setAttribute("driver", nic.strGenericDriver);
6733 for (StringsMap::const_iterator it = nic.genericProperties.begin();
6734 it != nic.genericProperties.end();
6735 ++it)
6736 {
6737 xml::ElementNode *pelmProp = pelmMode->createChild("Property");
6738 pelmProp->setAttribute("name", it->first);
6739 pelmProp->setAttribute("value", it->second);
6740 }
6741 }
6742 }
6743 break;
6744
6745 case NetworkAttachmentType_NATNetwork:
6746 // For the currently active network attachment type we have to
6747 // generate the tag, otherwise the attachment type is lost.
6748 if (fEnabled || !nic.strNATNetworkName.isEmpty())
6749 {
6750 xml::ElementNode *pelmMode = elmParent.createChild("NATNetwork");
6751 if (!nic.strNATNetworkName.isEmpty())
6752 pelmMode->setAttribute("name", nic.strNATNetworkName);
6753 }
6754 break;
6755
6756 default: /*case NetworkAttachmentType_Null:*/
6757 break;
6758 }
6759}
6760
6761/**
6762 * Creates a \<StorageControllers\> node under elmParent and then writes out the XML
6763 * keys under that. Called for both the \<Machine\> node and for snapshots.
6764 * @param elmParent
6765 * @param st
6766 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
6767 * an empty drive is always written instead. This is for the OVF export case.
6768 * This parameter is ignored unless the settings version is at least v1.9, which
6769 * is always the case when this gets called for OVF export.
6770 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
6771 * pointers to which we will append all elements that we created here that contain
6772 * UUID attributes. This allows the OVF export code to quickly replace the internal
6773 * media UUIDs with the UUIDs of the media that were exported.
6774 */
6775void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
6776 const Storage &st,
6777 bool fSkipRemovableMedia,
6778 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
6779{
6780 if (!st.llStorageControllers.size())
6781 return;
6782 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
6783
6784 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
6785 it != st.llStorageControllers.end();
6786 ++it)
6787 {
6788 const StorageController &sc = *it;
6789
6790 if ( (m->sv < SettingsVersion_v1_9)
6791 && (sc.controllerType == StorageControllerType_I82078)
6792 )
6793 // floppy controller already got written into <Hardware>/<FloppyController> in buildHardwareXML()
6794 // for pre-1.9 settings
6795 continue;
6796
6797 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
6798 com::Utf8Str name = sc.strName;
6799 if (m->sv < SettingsVersion_v1_8)
6800 {
6801 // pre-1.8 settings use shorter controller names, they are
6802 // expanded when reading the settings
6803 if (name == "IDE Controller")
6804 name = "IDE";
6805 else if (name == "SATA Controller")
6806 name = "SATA";
6807 else if (name == "SCSI Controller")
6808 name = "SCSI";
6809 }
6810 pelmController->setAttribute("name", sc.strName);
6811
6812 const char *pcszType;
6813 switch (sc.controllerType)
6814 {
6815 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
6816 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
6817 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
6818 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
6819 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
6820 case StorageControllerType_I82078: pcszType = "I82078"; break;
6821 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
6822 case StorageControllerType_USB: pcszType = "USB"; break;
6823 case StorageControllerType_NVMe: pcszType = "NVMe"; break;
6824 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
6825 }
6826 pelmController->setAttribute("type", pcszType);
6827
6828 pelmController->setAttribute("PortCount", sc.ulPortCount);
6829
6830 if (m->sv >= SettingsVersion_v1_9)
6831 if (sc.ulInstance)
6832 pelmController->setAttribute("Instance", sc.ulInstance);
6833
6834 if (m->sv >= SettingsVersion_v1_10)
6835 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
6836
6837 if (m->sv >= SettingsVersion_v1_11)
6838 pelmController->setAttribute("Bootable", sc.fBootable);
6839
6840 if (sc.controllerType == StorageControllerType_IntelAhci)
6841 {
6842 pelmController->setAttribute("IDE0MasterEmulationPort", 0);
6843 pelmController->setAttribute("IDE0SlaveEmulationPort", 1);
6844 pelmController->setAttribute("IDE1MasterEmulationPort", 2);
6845 pelmController->setAttribute("IDE1SlaveEmulationPort", 3);
6846 }
6847
6848 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
6849 it2 != sc.llAttachedDevices.end();
6850 ++it2)
6851 {
6852 const AttachedDevice &att = *it2;
6853
6854 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
6855 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
6856 // the floppy controller at the top of the loop
6857 if ( att.deviceType == DeviceType_DVD
6858 && m->sv < SettingsVersion_v1_9
6859 )
6860 continue;
6861
6862 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
6863
6864 pcszType = NULL;
6865
6866 switch (att.deviceType)
6867 {
6868 case DeviceType_HardDisk:
6869 pcszType = "HardDisk";
6870 if (att.fNonRotational)
6871 pelmDevice->setAttribute("nonrotational", att.fNonRotational);
6872 if (att.fDiscard)
6873 pelmDevice->setAttribute("discard", att.fDiscard);
6874 break;
6875
6876 case DeviceType_DVD:
6877 pcszType = "DVD";
6878 pelmDevice->setAttribute("passthrough", att.fPassThrough);
6879 if (att.fTempEject)
6880 pelmDevice->setAttribute("tempeject", att.fTempEject);
6881 break;
6882
6883 case DeviceType_Floppy:
6884 pcszType = "Floppy";
6885 break;
6886
6887 default: break; /* Shut up MSC. */
6888 }
6889
6890 pelmDevice->setAttribute("type", pcszType);
6891
6892 if (m->sv >= SettingsVersion_v1_15)
6893 pelmDevice->setAttribute("hotpluggable", att.fHotPluggable);
6894
6895 pelmDevice->setAttribute("port", att.lPort);
6896 pelmDevice->setAttribute("device", att.lDevice);
6897
6898 if (att.strBwGroup.length())
6899 pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup);
6900
6901 // attached image, if any
6902 if (!att.uuid.isZero()
6903 && att.uuid.isValid()
6904 && (att.deviceType == DeviceType_HardDisk
6905 || !fSkipRemovableMedia
6906 )
6907 )
6908 {
6909 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
6910 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
6911
6912 // if caller wants a list of UUID elements, give it to them
6913 if (pllElementsWithUuidAttributes)
6914 pllElementsWithUuidAttributes->push_back(pelmImage);
6915 }
6916 else if ( (m->sv >= SettingsVersion_v1_9)
6917 && (att.strHostDriveSrc.length())
6918 )
6919 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6920 }
6921 }
6922}
6923
6924/**
6925 * Creates a \<Debugging\> node under elmParent and then writes out the XML
6926 * keys under that. Called for both the \<Machine\> node and for snapshots.
6927 *
6928 * @param pElmParent Pointer to the parent element.
6929 * @param pDbg Pointer to the debugging settings.
6930 */
6931void MachineConfigFile::buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg)
6932{
6933 if (m->sv < SettingsVersion_v1_13 || pDbg->areDefaultSettings())
6934 return;
6935
6936 xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");
6937 xml::ElementNode *pElmTracing = pElmDebugging->createChild("Tracing");
6938 pElmTracing->setAttribute("enabled", pDbg->fTracingEnabled);
6939 pElmTracing->setAttribute("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
6940 pElmTracing->setAttribute("config", pDbg->strTracingConfig);
6941}
6942
6943/**
6944 * Creates a \<Autostart\> node under elmParent and then writes out the XML
6945 * keys under that. Called for both the \<Machine\> node and for snapshots.
6946 *
6947 * @param pElmParent Pointer to the parent element.
6948 * @param pAutostart Pointer to the autostart settings.
6949 */
6950void MachineConfigFile::buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart)
6951{
6952 const char *pcszAutostop = NULL;
6953
6954 if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())
6955 return;
6956
6957 xml::ElementNode *pElmAutostart = pElmParent->createChild("Autostart");
6958 pElmAutostart->setAttribute("enabled", pAutostart->fAutostartEnabled);
6959 pElmAutostart->setAttribute("delay", pAutostart->uAutostartDelay);
6960
6961 switch (pAutostart->enmAutostopType)
6962 {
6963 case AutostopType_Disabled: pcszAutostop = "Disabled"; break;
6964 case AutostopType_SaveState: pcszAutostop = "SaveState"; break;
6965 case AutostopType_PowerOff: pcszAutostop = "PowerOff"; break;
6966 case AutostopType_AcpiShutdown: pcszAutostop = "AcpiShutdown"; break;
6967 default: Assert(false); pcszAutostop = "Disabled"; break;
6968 }
6969 pElmAutostart->setAttribute("autostop", pcszAutostop);
6970}
6971
6972/**
6973 * Creates a \<Groups\> node under elmParent and then writes out the XML
6974 * keys under that. Called for the \<Machine\> node only.
6975 *
6976 * @param pElmParent Pointer to the parent element.
6977 * @param pllGroups Pointer to the groups list.
6978 */
6979void MachineConfigFile::buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups)
6980{
6981 if ( m->sv < SettingsVersion_v1_13 || pllGroups->size() == 0
6982 || (pllGroups->size() == 1 && pllGroups->front() == "/"))
6983 return;
6984
6985 xml::ElementNode *pElmGroups = pElmParent->createChild("Groups");
6986 for (StringsList::const_iterator it = pllGroups->begin();
6987 it != pllGroups->end();
6988 ++it)
6989 {
6990 const Utf8Str &group = *it;
6991 xml::ElementNode *pElmGroup = pElmGroups->createChild("Group");
6992 pElmGroup->setAttribute("name", group);
6993 }
6994}
6995
6996/**
6997 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
6998 * for the root snapshot of a machine, if present; elmParent then points to the \<Snapshots\> node under the
6999 * \<Machine\> node to which \<Snapshot\> must be added. This may then recurse for child snapshots.
7000 *
7001 * @param depth
7002 * @param elmParent
7003 * @param snap
7004 */
7005void MachineConfigFile::buildSnapshotXML(uint32_t depth,
7006 xml::ElementNode &elmParent,
7007 const Snapshot &snap)
7008{
7009 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
7010 throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
7011
7012 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
7013
7014 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
7015 pelmSnapshot->setAttribute("name", snap.strName);
7016 pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(snap.timestamp));
7017
7018 if (snap.strStateFile.length())
7019 pelmSnapshot->setAttributePath("stateFile", snap.strStateFile);
7020
7021 if (snap.strDescription.length())
7022 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
7023
7024 // We only skip removable media for OVF, but OVF never includes snapshots.
7025 buildHardwareXML(*pelmSnapshot, snap.hardware, 0 /* fl */, NULL /* pllElementsWithUuidAttributes */);
7026 buildDebuggingXML(pelmSnapshot, &snap.debugging);
7027 buildAutostartXML(pelmSnapshot, &snap.autostart);
7028 // note: Groups exist only for Machine, not for Snapshot
7029
7030 if (snap.llChildSnapshots.size())
7031 {
7032 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
7033 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
7034 it != snap.llChildSnapshots.end();
7035 ++it)
7036 {
7037 const Snapshot &child = *it;
7038 buildSnapshotXML(depth + 1, *pelmChildren, child);
7039 }
7040 }
7041}
7042
7043/**
7044 * Builds the XML DOM tree for the machine config under the given XML element.
7045 *
7046 * This has been separated out from write() so it can be called from elsewhere,
7047 * such as the OVF code, to build machine XML in an existing XML tree.
7048 *
7049 * As a result, this gets called from two locations:
7050 *
7051 * -- MachineConfigFile::write();
7052 *
7053 * -- Appliance::buildXMLForOneVirtualSystem()
7054 *
7055 * In fl, the following flag bits are recognized:
7056 *
7057 * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will
7058 * be written, if present. This is not set when called from OVF because OVF
7059 * has its own variant of a media registry. This flag is ignored unless the
7060 * settings version is at least v1.11 (VirtualBox 4.0).
7061 *
7062 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
7063 * of the machine and write out \<Snapshot\> and possibly more snapshots under
7064 * that, if snapshots are present. Otherwise all snapshots are suppressed
7065 * (when called from OVF).
7066 *
7067 * -- BuildMachineXML_WriteVBoxVersionAttribute: If set, add a settingsVersion
7068 * attribute to the machine tag with the vbox settings version. This is for
7069 * the OVF export case in which we don't have the settings version set in
7070 * the root element.
7071 *
7072 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
7073 * (DVDs, floppies) are silently skipped. This is for the OVF export case
7074 * until we support copying ISO and RAW media as well. This flag is ignored
7075 * unless the settings version is at least v1.9, which is always the case
7076 * when this gets called for OVF export.
7077 *
7078 * -- BuildMachineXML_SuppressSavedState: If set, the Machine/stateFile
7079 * attribute is never set. This is also for the OVF export case because we
7080 * cannot save states with OVF.
7081 *
7082 * @param elmMachine XML \<Machine\> element to add attributes and elements to.
7083 * @param fl Flags.
7084 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
7085 * see buildStorageControllersXML() for details.
7086 */
7087void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
7088 uint32_t fl,
7089 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
7090{
7091 if (fl & BuildMachineXML_WriteVBoxVersionAttribute)
7092 {
7093 // add settings version attribute to machine element
7094 setVersionAttribute(elmMachine);
7095 LogRel(("Exporting settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
7096 }
7097
7098 elmMachine.setAttribute("uuid", uuid.toStringCurly());
7099 elmMachine.setAttribute("name", machineUserData.strName);
7100 if (machineUserData.fDirectoryIncludesUUID)
7101 elmMachine.setAttribute("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
7102 if (!machineUserData.fNameSync)
7103 elmMachine.setAttribute("nameSync", machineUserData.fNameSync);
7104 if (machineUserData.strDescription.length())
7105 elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
7106 elmMachine.setAttribute("OSType", machineUserData.strOsType);
7107 if ( strStateFile.length()
7108 && !(fl & BuildMachineXML_SuppressSavedState)
7109 )
7110 elmMachine.setAttributePath("stateFile", strStateFile);
7111
7112 if ((fl & BuildMachineXML_IncludeSnapshots)
7113 && !uuidCurrentSnapshot.isZero()
7114 && uuidCurrentSnapshot.isValid())
7115 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
7116
7117 if (machineUserData.strSnapshotFolder.length())
7118 elmMachine.setAttributePath("snapshotFolder", machineUserData.strSnapshotFolder);
7119 if (!fCurrentStateModified)
7120 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
7121 elmMachine.setAttribute("lastStateChange", stringifyTimestamp(timeLastStateChange));
7122 if (fAborted)
7123 elmMachine.setAttribute("aborted", fAborted);
7124 if (machineUserData.strVMPriority.length())
7125 elmMachine.setAttribute("processPriority", machineUserData.strVMPriority);
7126 // Please keep the icon last so that one doesn't have to check if there
7127 // is anything in the line after this very long attribute in the XML.
7128 if (machineUserData.ovIcon.size())
7129 {
7130 Utf8Str strIcon;
7131 toBase64(strIcon, machineUserData.ovIcon);
7132 elmMachine.setAttribute("icon", strIcon);
7133 }
7134 if ( m->sv >= SettingsVersion_v1_9
7135 && ( machineUserData.fTeleporterEnabled
7136 || machineUserData.uTeleporterPort
7137 || !machineUserData.strTeleporterAddress.isEmpty()
7138 || !machineUserData.strTeleporterPassword.isEmpty()
7139 )
7140 )
7141 {
7142 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
7143 pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled);
7144 pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort);
7145 pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress);
7146 pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword);
7147 }
7148
7149 if ( m->sv >= SettingsVersion_v1_11
7150 && ( machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
7151 || machineUserData.uFaultTolerancePort
7152 || machineUserData.uFaultToleranceInterval
7153 || !machineUserData.strFaultToleranceAddress.isEmpty()
7154 )
7155 )
7156 {
7157 xml::ElementNode *pelmFaultTolerance = elmMachine.createChild("FaultTolerance");
7158 switch (machineUserData.enmFaultToleranceState)
7159 {
7160 case FaultToleranceState_Inactive:
7161 pelmFaultTolerance->setAttribute("state", "inactive");
7162 break;
7163 case FaultToleranceState_Master:
7164 pelmFaultTolerance->setAttribute("state", "master");
7165 break;
7166 case FaultToleranceState_Standby:
7167 pelmFaultTolerance->setAttribute("state", "standby");
7168 break;
7169#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
7170 case FaultToleranceState_32BitHack: /* (compiler warnings) */
7171 AssertFailedBreak();
7172#endif
7173 }
7174
7175 pelmFaultTolerance->setAttribute("port", machineUserData.uFaultTolerancePort);
7176 pelmFaultTolerance->setAttribute("address", machineUserData.strFaultToleranceAddress);
7177 pelmFaultTolerance->setAttribute("interval", machineUserData.uFaultToleranceInterval);
7178 pelmFaultTolerance->setAttribute("password", machineUserData.strFaultTolerancePassword);
7179 }
7180
7181 if ( (fl & BuildMachineXML_MediaRegistry)
7182 && (m->sv >= SettingsVersion_v1_11)
7183 )
7184 buildMediaRegistry(elmMachine, mediaRegistry);
7185
7186 buildExtraData(elmMachine, mapExtraDataItems);
7187
7188 if ( (fl & BuildMachineXML_IncludeSnapshots)
7189 && llFirstSnapshot.size())
7190 buildSnapshotXML(1, elmMachine, llFirstSnapshot.front());
7191
7192 buildHardwareXML(elmMachine, hardwareMachine, fl, pllElementsWithUuidAttributes);
7193 buildDebuggingXML(&elmMachine, &debugging);
7194 buildAutostartXML(&elmMachine, &autostart);
7195 buildGroupsXML(&elmMachine, &machineUserData.llGroups);
7196}
7197
7198/**
7199 * Returns true only if the given AudioDriverType is supported on
7200 * the current host platform. For example, this would return false
7201 * for AudioDriverType_DirectSound when compiled on a Linux host.
7202 * @param drv AudioDriverType_* enum to test.
7203 * @return true only if the current host supports that driver.
7204 */
7205/*static*/
7206bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
7207{
7208 switch (drv)
7209 {
7210 case AudioDriverType_Null:
7211#ifdef RT_OS_WINDOWS
7212 case AudioDriverType_DirectSound:
7213#endif
7214#ifdef VBOX_WITH_AUDIO_OSS
7215 case AudioDriverType_OSS:
7216#endif
7217#ifdef VBOX_WITH_AUDIO_ALSA
7218 case AudioDriverType_ALSA:
7219#endif
7220#ifdef VBOX_WITH_AUDIO_PULSE
7221 case AudioDriverType_Pulse:
7222#endif
7223#ifdef RT_OS_DARWIN
7224 case AudioDriverType_CoreAudio:
7225#endif
7226#ifdef RT_OS_OS2
7227 case AudioDriverType_MMPM:
7228#endif
7229 return true;
7230 default: break; /* Shut up MSC. */
7231 }
7232
7233 return false;
7234}
7235
7236/**
7237 * Returns the AudioDriverType_* which should be used by default on this
7238 * host platform. On Linux, this will check at runtime whether PulseAudio
7239 * or ALSA are actually supported on the first call.
7240 *
7241 * @return Default audio driver type for this host platform.
7242 */
7243/*static*/
7244AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
7245{
7246#if defined(RT_OS_WINDOWS)
7247 return AudioDriverType_DirectSound;
7248
7249#elif defined(RT_OS_LINUX)
7250 /* On Linux, we need to check at runtime what's actually supported. */
7251 static RTCLockMtx s_mtx;
7252 static AudioDriverType_T s_enmLinuxDriver = AudioDriverType_Null;
7253 RTCLock lock(s_mtx);
7254 if (s_enmLinuxDriver == AudioDriverType_Null)
7255 {
7256# ifdef VBOX_WITH_AUDIO_PULSE
7257 /* Check for the pulse library & that the pulse audio daemon is running. */
7258 if (RTProcIsRunningByName("pulseaudio") &&
7259 RTLdrIsLoadable("libpulse.so.0"))
7260 s_enmLinuxDriver = AudioDriverType_Pulse;
7261 else
7262# endif /* VBOX_WITH_AUDIO_PULSE */
7263# ifdef VBOX_WITH_AUDIO_ALSA
7264 /* Check if we can load the ALSA library */
7265 if (RTLdrIsLoadable("libasound.so.2"))
7266 s_enmLinuxDriver = AudioDriverType_ALSA;
7267 else
7268# endif /* VBOX_WITH_AUDIO_ALSA */
7269 s_enmLinuxDriver = AudioDriverType_OSS;
7270 }
7271 return s_enmLinuxDriver;
7272
7273#elif defined(RT_OS_DARWIN)
7274 return AudioDriverType_CoreAudio;
7275
7276#elif defined(RT_OS_OS2)
7277 return AudioDriverType_MMPM;
7278
7279#else /* All other platforms. */
7280# ifdef VBOX_WITH_AUDIO_OSS
7281 return AudioDriverType_OSS;
7282# else
7283 /* Return NULL driver as a fallback if nothing of the above is available. */
7284 return AudioDriverType_Null;
7285# endif
7286#endif
7287}
7288
7289/**
7290 * Called from write() before calling ConfigFileBase::createStubDocument().
7291 * This adjusts the settings version in m->sv if incompatible settings require
7292 * a settings bump, whereas otherwise we try to preserve the settings version
7293 * to avoid breaking compatibility with older versions.
7294 *
7295 * We do the checks in here in reverse order: newest first, oldest last, so
7296 * that we avoid unnecessary checks since some of these are expensive.
7297 */
7298void MachineConfigFile::bumpSettingsVersionIfNeeded()
7299{
7300 if (m->sv < SettingsVersion_v1_17)
7301 {
7302 // VirtualBox 6.0 adds nested hardware virtualization, using native API (NEM).
7303 if ( hardwareMachine.fNestedHWVirt
7304 || hardwareMachine.fUseNativeApi)
7305 {
7306 m->sv = SettingsVersion_v1_17;
7307 return;
7308 }
7309 if (hardwareMachine.llSharedFolders.size())
7310 for (SharedFoldersList::const_iterator it = hardwareMachine.llSharedFolders.begin();
7311 it != hardwareMachine.llSharedFolders.end();
7312 ++it)
7313 if (it->strAutoMountPoint.isNotEmpty())
7314 {
7315 m->sv = SettingsVersion_v1_17;
7316 return;
7317 }
7318
7319 /*
7320 * Check if any serial port uses a non 16550A serial port.
7321 */
7322 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
7323 it != hardwareMachine.llSerialPorts.end();
7324 ++it)
7325 {
7326 const SerialPort &port = *it;
7327 if (port.uartType != UartType_U16550A)
7328 {
7329 m->sv = SettingsVersion_v1_17;
7330 return;
7331 }
7332 }
7333 }
7334
7335 if (m->sv < SettingsVersion_v1_16)
7336 {
7337 // VirtualBox 5.1 adds a NVMe storage controller, paravirt debug
7338 // options, cpu profile, APIC settings (CPU capability and BIOS).
7339
7340 if ( hardwareMachine.strParavirtDebug.isNotEmpty()
7341 || (!hardwareMachine.strCpuProfile.equals("host") && hardwareMachine.strCpuProfile.isNotEmpty())
7342 || hardwareMachine.biosSettings.apicMode != APICMode_APIC
7343 || !hardwareMachine.fAPIC
7344 || hardwareMachine.fX2APIC
7345 || hardwareMachine.fIBPBOnVMExit
7346 || hardwareMachine.fIBPBOnVMEntry
7347 || hardwareMachine.fSpecCtrl
7348 || hardwareMachine.fSpecCtrlByHost)
7349 {
7350 m->sv = SettingsVersion_v1_16;
7351 return;
7352 }
7353
7354 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7355 it != hardwareMachine.storage.llStorageControllers.end();
7356 ++it)
7357 {
7358 const StorageController &sctl = *it;
7359
7360 if (sctl.controllerType == StorageControllerType_NVMe)
7361 {
7362 m->sv = SettingsVersion_v1_16;
7363 return;
7364 }
7365 }
7366
7367 for (CpuIdLeafsList::const_iterator it = hardwareMachine.llCpuIdLeafs.begin();
7368 it != hardwareMachine.llCpuIdLeafs.end();
7369 ++it)
7370 if (it->idxSub != 0)
7371 {
7372 m->sv = SettingsVersion_v1_16;
7373 return;
7374 }
7375 }
7376
7377 if (m->sv < SettingsVersion_v1_15)
7378 {
7379 // VirtualBox 5.0 adds paravirt providers, explicit AHCI port hotplug
7380 // setting, USB storage controller, xHCI, serial port TCP backend
7381 // and VM process priority.
7382
7383 /*
7384 * Check simple configuration bits first, loopy stuff afterwards.
7385 */
7386 if ( hardwareMachine.paravirtProvider != ParavirtProvider_Legacy
7387 || hardwareMachine.uCpuIdPortabilityLevel != 0
7388 || machineUserData.strVMPriority.length())
7389 {
7390 m->sv = SettingsVersion_v1_15;
7391 return;
7392 }
7393
7394 /*
7395 * Check whether the hotpluggable flag of all storage devices differs
7396 * from the default for old settings.
7397 * AHCI ports are hotpluggable by default every other device is not.
7398 * Also check if there are USB storage controllers.
7399 */
7400 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7401 it != hardwareMachine.storage.llStorageControllers.end();
7402 ++it)
7403 {
7404 const StorageController &sctl = *it;
7405
7406 if (sctl.controllerType == StorageControllerType_USB)
7407 {
7408 m->sv = SettingsVersion_v1_15;
7409 return;
7410 }
7411
7412 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
7413 it2 != sctl.llAttachedDevices.end();
7414 ++it2)
7415 {
7416 const AttachedDevice &att = *it2;
7417
7418 if ( ( att.fHotPluggable
7419 && sctl.controllerType != StorageControllerType_IntelAhci)
7420 || ( !att.fHotPluggable
7421 && sctl.controllerType == StorageControllerType_IntelAhci))
7422 {
7423 m->sv = SettingsVersion_v1_15;
7424 return;
7425 }
7426 }
7427 }
7428
7429 /*
7430 * Check if there is an xHCI (USB3) USB controller.
7431 */
7432 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
7433 it != hardwareMachine.usbSettings.llUSBControllers.end();
7434 ++it)
7435 {
7436 const USBController &ctrl = *it;
7437 if (ctrl.enmType == USBControllerType_XHCI)
7438 {
7439 m->sv = SettingsVersion_v1_15;
7440 return;
7441 }
7442 }
7443
7444 /*
7445 * Check if any serial port uses the TCP backend.
7446 */
7447 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
7448 it != hardwareMachine.llSerialPorts.end();
7449 ++it)
7450 {
7451 const SerialPort &port = *it;
7452 if (port.portMode == PortMode_TCP)
7453 {
7454 m->sv = SettingsVersion_v1_15;
7455 return;
7456 }
7457 }
7458 }
7459
7460 if (m->sv < SettingsVersion_v1_14)
7461 {
7462 // VirtualBox 4.3 adds default frontend setting, graphics controller
7463 // setting, explicit long mode setting, (video) capturing and NAT networking.
7464 if ( !hardwareMachine.strDefaultFrontend.isEmpty()
7465 || hardwareMachine.graphicsControllerType != GraphicsControllerType_VBoxVGA
7466 || hardwareMachine.enmLongMode != Hardware::LongMode_Legacy
7467 || machineUserData.ovIcon.size() > 0
7468 || hardwareMachine.recordingSettings.fEnabled)
7469 {
7470 m->sv = SettingsVersion_v1_14;
7471 return;
7472 }
7473 NetworkAdaptersList::const_iterator netit;
7474 for (netit = hardwareMachine.llNetworkAdapters.begin();
7475 netit != hardwareMachine.llNetworkAdapters.end();
7476 ++netit)
7477 {
7478 if (netit->mode == NetworkAttachmentType_NATNetwork)
7479 {
7480 m->sv = SettingsVersion_v1_14;
7481 break;
7482 }
7483 }
7484 }
7485
7486 if (m->sv < SettingsVersion_v1_14)
7487 {
7488 unsigned cOhciCtrls = 0;
7489 unsigned cEhciCtrls = 0;
7490 bool fNonStdName = false;
7491
7492 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
7493 it != hardwareMachine.usbSettings.llUSBControllers.end();
7494 ++it)
7495 {
7496 const USBController &ctrl = *it;
7497
7498 switch (ctrl.enmType)
7499 {
7500 case USBControllerType_OHCI:
7501 cOhciCtrls++;
7502 if (ctrl.strName != "OHCI")
7503 fNonStdName = true;
7504 break;
7505 case USBControllerType_EHCI:
7506 cEhciCtrls++;
7507 if (ctrl.strName != "EHCI")
7508 fNonStdName = true;
7509 break;
7510 default:
7511 /* Anything unknown forces a bump. */
7512 fNonStdName = true;
7513 }
7514
7515 /* Skip checking other controllers if the settings bump is necessary. */
7516 if (cOhciCtrls > 1 || cEhciCtrls > 1 || fNonStdName)
7517 {
7518 m->sv = SettingsVersion_v1_14;
7519 break;
7520 }
7521 }
7522 }
7523
7524 if (m->sv < SettingsVersion_v1_13)
7525 {
7526 // VirtualBox 4.2 adds tracing, autostart, UUID in directory and groups.
7527 if ( !debugging.areDefaultSettings()
7528 || !autostart.areDefaultSettings()
7529 || machineUserData.fDirectoryIncludesUUID
7530 || machineUserData.llGroups.size() > 1
7531 || machineUserData.llGroups.front() != "/")
7532 m->sv = SettingsVersion_v1_13;
7533 }
7534
7535 if (m->sv < SettingsVersion_v1_13)
7536 {
7537 // VirtualBox 4.2 changes the units for bandwidth group limits.
7538 for (BandwidthGroupList::const_iterator it = hardwareMachine.ioSettings.llBandwidthGroups.begin();
7539 it != hardwareMachine.ioSettings.llBandwidthGroups.end();
7540 ++it)
7541 {
7542 const BandwidthGroup &gr = *it;
7543 if (gr.cMaxBytesPerSec % _1M)
7544 {
7545 // Bump version if a limit cannot be expressed in megabytes
7546 m->sv = SettingsVersion_v1_13;
7547 break;
7548 }
7549 }
7550 }
7551
7552 if (m->sv < SettingsVersion_v1_12)
7553 {
7554 // VirtualBox 4.1 adds PCI passthrough and emulated USB Smart Card reader
7555 if ( hardwareMachine.pciAttachments.size()
7556 || hardwareMachine.fEmulatedUSBCardReader)
7557 m->sv = SettingsVersion_v1_12;
7558 }
7559
7560 if (m->sv < SettingsVersion_v1_12)
7561 {
7562 // VirtualBox 4.1 adds a promiscuous mode policy to the network
7563 // adapters and a generic network driver transport.
7564 NetworkAdaptersList::const_iterator netit;
7565 for (netit = hardwareMachine.llNetworkAdapters.begin();
7566 netit != hardwareMachine.llNetworkAdapters.end();
7567 ++netit)
7568 {
7569 if ( netit->enmPromiscModePolicy != NetworkAdapterPromiscModePolicy_Deny
7570 || netit->mode == NetworkAttachmentType_Generic
7571 || !netit->areGenericDriverDefaultSettings()
7572 )
7573 {
7574 m->sv = SettingsVersion_v1_12;
7575 break;
7576 }
7577 }
7578 }
7579
7580 if (m->sv < SettingsVersion_v1_11)
7581 {
7582 // VirtualBox 4.0 adds HD audio, CPU priorities, fault tolerance,
7583 // per-machine media registries, VRDE, JRockitVE, bandwidth groups,
7584 // ICH9 chipset
7585 if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
7586 || hardwareMachine.ulCpuExecutionCap != 100
7587 || machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
7588 || machineUserData.uFaultTolerancePort
7589 || machineUserData.uFaultToleranceInterval
7590 || !machineUserData.strFaultToleranceAddress.isEmpty()
7591 || mediaRegistry.llHardDisks.size()
7592 || mediaRegistry.llDvdImages.size()
7593 || mediaRegistry.llFloppyImages.size()
7594 || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty()
7595 || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty()
7596 || machineUserData.strOsType == "JRockitVE"
7597 || hardwareMachine.ioSettings.llBandwidthGroups.size()
7598 || hardwareMachine.chipsetType == ChipsetType_ICH9
7599 )
7600 m->sv = SettingsVersion_v1_11;
7601 }
7602
7603 if (m->sv < SettingsVersion_v1_10)
7604 {
7605 /* If the properties contain elements other than "TCP/Ports" and "TCP/Address",
7606 * then increase the version to at least VBox 3.2, which can have video channel properties.
7607 */
7608 unsigned cOldProperties = 0;
7609
7610 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
7611 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7612 cOldProperties++;
7613 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
7614 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7615 cOldProperties++;
7616
7617 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
7618 m->sv = SettingsVersion_v1_10;
7619 }
7620
7621 if (m->sv < SettingsVersion_v1_11)
7622 {
7623 /* If the properties contain elements other than "TCP/Ports", "TCP/Address",
7624 * "VideoChannel/Enabled" and "VideoChannel/Quality" then increase the version to VBox 4.0.
7625 */
7626 unsigned cOldProperties = 0;
7627
7628 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
7629 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7630 cOldProperties++;
7631 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
7632 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7633 cOldProperties++;
7634 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
7635 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7636 cOldProperties++;
7637 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Quality");
7638 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7639 cOldProperties++;
7640
7641 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
7642 m->sv = SettingsVersion_v1_11;
7643 }
7644
7645 // settings version 1.9 is required if there is not exactly one DVD
7646 // or more than one floppy drive present or the DVD is not at the secondary
7647 // master; this check is a bit more complicated
7648 //
7649 // settings version 1.10 is required if the host cache should be disabled
7650 //
7651 // settings version 1.11 is required for bandwidth limits and if more than
7652 // one controller of each type is present.
7653 if (m->sv < SettingsVersion_v1_11)
7654 {
7655 // count attached DVDs and floppies (only if < v1.9)
7656 size_t cDVDs = 0;
7657 size_t cFloppies = 0;
7658
7659 // count storage controllers (if < v1.11)
7660 size_t cSata = 0;
7661 size_t cScsiLsi = 0;
7662 size_t cScsiBuslogic = 0;
7663 size_t cSas = 0;
7664 size_t cIde = 0;
7665 size_t cFloppy = 0;
7666
7667 // need to run thru all the storage controllers and attached devices to figure this out
7668 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7669 it != hardwareMachine.storage.llStorageControllers.end();
7670 ++it)
7671 {
7672 const StorageController &sctl = *it;
7673
7674 // count storage controllers of each type; 1.11 is required if more than one
7675 // controller of one type is present
7676 switch (sctl.storageBus)
7677 {
7678 case StorageBus_IDE:
7679 cIde++;
7680 break;
7681 case StorageBus_SATA:
7682 cSata++;
7683 break;
7684 case StorageBus_SAS:
7685 cSas++;
7686 break;
7687 case StorageBus_SCSI:
7688 if (sctl.controllerType == StorageControllerType_LsiLogic)
7689 cScsiLsi++;
7690 else
7691 cScsiBuslogic++;
7692 break;
7693 case StorageBus_Floppy:
7694 cFloppy++;
7695 break;
7696 default:
7697 // Do nothing
7698 break;
7699 }
7700
7701 if ( cSata > 1
7702 || cScsiLsi > 1
7703 || cScsiBuslogic > 1
7704 || cSas > 1
7705 || cIde > 1
7706 || cFloppy > 1)
7707 {
7708 m->sv = SettingsVersion_v1_11;
7709 break; // abort the loop -- we will not raise the version further
7710 }
7711
7712 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
7713 it2 != sctl.llAttachedDevices.end();
7714 ++it2)
7715 {
7716 const AttachedDevice &att = *it2;
7717
7718 // Bandwidth limitations are new in VirtualBox 4.0 (1.11)
7719 if (m->sv < SettingsVersion_v1_11)
7720 {
7721 if (att.strBwGroup.length() != 0)
7722 {
7723 m->sv = SettingsVersion_v1_11;
7724 break; // abort the loop -- we will not raise the version further
7725 }
7726 }
7727
7728 // disabling the host IO cache requires settings version 1.10
7729 if ( (m->sv < SettingsVersion_v1_10)
7730 && (!sctl.fUseHostIOCache)
7731 )
7732 m->sv = SettingsVersion_v1_10;
7733
7734 // we can only write the StorageController/@Instance attribute with v1.9
7735 if ( (m->sv < SettingsVersion_v1_9)
7736 && (sctl.ulInstance != 0)
7737 )
7738 m->sv = SettingsVersion_v1_9;
7739
7740 if (m->sv < SettingsVersion_v1_9)
7741 {
7742 if (att.deviceType == DeviceType_DVD)
7743 {
7744 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
7745 || (att.lPort != 1) // DVDs not at secondary master?
7746 || (att.lDevice != 0)
7747 )
7748 m->sv = SettingsVersion_v1_9;
7749
7750 ++cDVDs;
7751 }
7752 else if (att.deviceType == DeviceType_Floppy)
7753 ++cFloppies;
7754 }
7755 }
7756
7757 if (m->sv >= SettingsVersion_v1_11)
7758 break; // abort the loop -- we will not raise the version further
7759 }
7760
7761 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
7762 // so any deviation from that will require settings version 1.9
7763 if ( (m->sv < SettingsVersion_v1_9)
7764 && ( (cDVDs != 1)
7765 || (cFloppies > 1)
7766 )
7767 )
7768 m->sv = SettingsVersion_v1_9;
7769 }
7770
7771 // VirtualBox 3.2: Check for non default I/O settings
7772 if (m->sv < SettingsVersion_v1_10)
7773 {
7774 if ( (hardwareMachine.ioSettings.fIOCacheEnabled != true)
7775 || (hardwareMachine.ioSettings.ulIOCacheSize != 5)
7776 // and page fusion
7777 || (hardwareMachine.fPageFusionEnabled)
7778 // and CPU hotplug, RTC timezone control, HID type and HPET
7779 || machineUserData.fRTCUseUTC
7780 || hardwareMachine.fCpuHotPlug
7781 || hardwareMachine.pointingHIDType != PointingHIDType_PS2Mouse
7782 || hardwareMachine.keyboardHIDType != KeyboardHIDType_PS2Keyboard
7783 || hardwareMachine.fHPETEnabled
7784 )
7785 m->sv = SettingsVersion_v1_10;
7786 }
7787
7788 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main
7789 // VirtualBox 4.0 adds network bandwitdth
7790 if (m->sv < SettingsVersion_v1_11)
7791 {
7792 NetworkAdaptersList::const_iterator netit;
7793 for (netit = hardwareMachine.llNetworkAdapters.begin();
7794 netit != hardwareMachine.llNetworkAdapters.end();
7795 ++netit)
7796 {
7797 if ( (m->sv < SettingsVersion_v1_12)
7798 && (netit->strBandwidthGroup.isNotEmpty())
7799 )
7800 {
7801 /* New in VirtualBox 4.1 */
7802 m->sv = SettingsVersion_v1_12;
7803 break;
7804 }
7805 else if ( (m->sv < SettingsVersion_v1_10)
7806 && (netit->fEnabled)
7807 && (netit->mode == NetworkAttachmentType_NAT)
7808 && ( netit->nat.u32Mtu != 0
7809 || netit->nat.u32SockRcv != 0
7810 || netit->nat.u32SockSnd != 0
7811 || netit->nat.u32TcpRcv != 0
7812 || netit->nat.u32TcpSnd != 0
7813 || !netit->nat.fDNSPassDomain
7814 || netit->nat.fDNSProxy
7815 || netit->nat.fDNSUseHostResolver
7816 || netit->nat.fAliasLog
7817 || netit->nat.fAliasProxyOnly
7818 || netit->nat.fAliasUseSamePorts
7819 || netit->nat.strTFTPPrefix.length()
7820 || netit->nat.strTFTPBootFile.length()
7821 || netit->nat.strTFTPNextServer.length()
7822 || netit->nat.mapRules.size()
7823 )
7824 )
7825 {
7826 m->sv = SettingsVersion_v1_10;
7827 // no break because we still might need v1.11 above
7828 }
7829 else if ( (m->sv < SettingsVersion_v1_10)
7830 && (netit->fEnabled)
7831 && (netit->ulBootPriority != 0)
7832 )
7833 {
7834 m->sv = SettingsVersion_v1_10;
7835 // no break because we still might need v1.11 above
7836 }
7837 }
7838 }
7839
7840 // all the following require settings version 1.9
7841 if ( (m->sv < SettingsVersion_v1_9)
7842 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
7843 || machineUserData.fTeleporterEnabled
7844 || machineUserData.uTeleporterPort
7845 || !machineUserData.strTeleporterAddress.isEmpty()
7846 || !machineUserData.strTeleporterPassword.isEmpty()
7847 || (!hardwareMachine.uuid.isZero() && hardwareMachine.uuid.isValid())
7848 )
7849 )
7850 m->sv = SettingsVersion_v1_9;
7851
7852 // "accelerate 2d video" requires settings version 1.8
7853 if ( (m->sv < SettingsVersion_v1_8)
7854 && (hardwareMachine.fAccelerate2DVideo)
7855 )
7856 m->sv = SettingsVersion_v1_8;
7857
7858 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
7859 if ( m->sv < SettingsVersion_v1_4
7860 && hardwareMachine.strVersion != "1"
7861 )
7862 m->sv = SettingsVersion_v1_4;
7863}
7864
7865/**
7866 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
7867 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
7868 * in particular if the file cannot be written.
7869 */
7870void MachineConfigFile::write(const com::Utf8Str &strFilename)
7871{
7872 try
7873 {
7874 // createStubDocument() sets the settings version to at least 1.7; however,
7875 // we might need to enfore a later settings version if incompatible settings
7876 // are present:
7877 bumpSettingsVersionIfNeeded();
7878
7879 m->strFilename = strFilename;
7880 specialBackupIfFirstBump();
7881 createStubDocument();
7882
7883 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
7884 buildMachineXML(*pelmMachine,
7885 MachineConfigFile::BuildMachineXML_IncludeSnapshots
7886 | MachineConfigFile::BuildMachineXML_MediaRegistry,
7887 // but not BuildMachineXML_WriteVBoxVersionAttribute
7888 NULL); /* pllElementsWithUuidAttributes */
7889
7890 // now go write the XML
7891 xml::XmlFileWriter writer(*m->pDoc);
7892 writer.write(m->strFilename.c_str(), true /*fSafe*/);
7893
7894 m->fFileExists = true;
7895 clearDocument();
7896 }
7897 catch (...)
7898 {
7899 clearDocument();
7900 throw;
7901 }
7902}
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