VirtualBox

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

Last change on this file since 79732 was 79732, checked in by vboxsync, 5 years ago

Main: Redoing the IDHCPServer interface, part I. bugref:9288

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