VirtualBox

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

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

Main/Settings.cpp: Catch any error in the constructor of ConfigFileBase and free m there as the desructor is not called when the constructor throws an exception (mumbling something about exceptions being a pain...), fixes a memory leak if loading the xml settings fail

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