VirtualBox

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

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

Main: bugref:7929: Added ability to change the priority of the VM process

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 316.6 KB
Line 
1/* $Id: Settings.cpp 77910 2019-03-27 11:33:01Z 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 enmVMPriority(VMProcPriority_Default)
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 && enmVMPriority == c.enmVMPriority);
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 {
5480 Utf8Str strVMPriority;
5481 if (elmMachine.getAttributeValue("processPriority", strVMPriority))
5482 {
5483 if (strVMPriority == "Flat")
5484 machineUserData.enmVMPriority = VMProcPriority_Flat;
5485 else if (strVMPriority == "Low")
5486 machineUserData.enmVMPriority = VMProcPriority_Low;
5487 else if (strVMPriority == "Normal")
5488 machineUserData.enmVMPriority = VMProcPriority_Normal;
5489 else if (strVMPriority == "High")
5490 machineUserData.enmVMPriority = VMProcPriority_High;
5491 else
5492 machineUserData.enmVMPriority = VMProcPriority_Default;
5493 }
5494 }
5495
5496 str.setNull();
5497 elmMachine.getAttributeValue("icon", str);
5498 parseBase64(machineUserData.ovIcon, str, &elmMachine);
5499
5500 // parse Hardware before the other elements because other things depend on it
5501 const xml::ElementNode *pelmHardware;
5502 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
5503 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
5504 readHardware(*pelmHardware, hardwareMachine);
5505
5506 xml::NodesLoop nlRootChildren(elmMachine);
5507 const xml::ElementNode *pelmMachineChild;
5508 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
5509 {
5510 if (pelmMachineChild->nameEquals("ExtraData"))
5511 readExtraData(*pelmMachineChild,
5512 mapExtraDataItems);
5513 else if ( (m->sv < SettingsVersion_v1_7)
5514 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
5515 )
5516 readHardDiskAttachments_pre1_7(*pelmMachineChild, hardwareMachine.storage);
5517 else if ( (m->sv >= SettingsVersion_v1_7)
5518 && (pelmMachineChild->nameEquals("StorageControllers"))
5519 )
5520 readStorageControllers(*pelmMachineChild, hardwareMachine.storage);
5521 else if (pelmMachineChild->nameEquals("Snapshot"))
5522 {
5523 if (uuidCurrentSnapshot.isZero())
5524 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing"));
5525 bool foundCurrentSnapshot = false;
5526 Snapshot snap;
5527 // this will recurse into child snapshots, if necessary
5528 foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, 1, *pelmMachineChild, snap);
5529 if (!foundCurrentSnapshot)
5530 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute"));
5531 llFirstSnapshot.push_back(snap);
5532 }
5533 else if (pelmMachineChild->nameEquals("Description"))
5534 machineUserData.strDescription = pelmMachineChild->getValue();
5535 else if (pelmMachineChild->nameEquals("Teleporter"))
5536 readTeleporter(pelmMachineChild, &machineUserData);
5537 else if (pelmMachineChild->nameEquals("FaultTolerance"))
5538 {
5539 Utf8Str strFaultToleranceSate;
5540 if (pelmMachineChild->getAttributeValue("state", strFaultToleranceSate))
5541 {
5542 if (strFaultToleranceSate == "master")
5543 machineUserData.enmFaultToleranceState = FaultToleranceState_Master;
5544 else
5545 if (strFaultToleranceSate == "standby")
5546 machineUserData.enmFaultToleranceState = FaultToleranceState_Standby;
5547 else
5548 machineUserData.enmFaultToleranceState = FaultToleranceState_Inactive;
5549 }
5550 pelmMachineChild->getAttributeValue("port", machineUserData.uFaultTolerancePort);
5551 pelmMachineChild->getAttributeValue("address", machineUserData.strFaultToleranceAddress);
5552 pelmMachineChild->getAttributeValue("interval", machineUserData.uFaultToleranceInterval);
5553 pelmMachineChild->getAttributeValue("password", machineUserData.strFaultTolerancePassword);
5554 }
5555 else if (pelmMachineChild->nameEquals("MediaRegistry"))
5556 readMediaRegistry(*pelmMachineChild, mediaRegistry);
5557 else if (pelmMachineChild->nameEquals("Debugging"))
5558 readDebugging(pelmMachineChild, &debugging);
5559 else if (pelmMachineChild->nameEquals("Autostart"))
5560 readAutostart(pelmMachineChild, &autostart);
5561 else if (pelmMachineChild->nameEquals("Groups"))
5562 readGroups(pelmMachineChild, &machineUserData.llGroups);
5563 }
5564
5565 if (m->sv < SettingsVersion_v1_9)
5566 // go through Hardware once more to repair the settings controller structures
5567 // with data from old DVDDrive and FloppyDrive elements
5568 readDVDAndFloppies_pre1_9(*pelmHardware, hardwareMachine.storage);
5569 }
5570 else
5571 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
5572}
5573
5574/**
5575 * Creates a \<Hardware\> node under elmParent and then writes out the XML
5576 * keys under that. Called for both the \<Machine\> node and for snapshots.
5577 * @param elmParent
5578 * @param hw
5579 * @param fl
5580 * @param pllElementsWithUuidAttributes
5581 */
5582void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
5583 const Hardware &hw,
5584 uint32_t fl,
5585 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
5586{
5587 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
5588
5589 if ( m->sv >= SettingsVersion_v1_4
5590 && (m->sv < SettingsVersion_v1_7 ? hw.strVersion != "1" : hw.strVersion != "2"))
5591 pelmHardware->setAttribute("version", hw.strVersion);
5592
5593 if ((m->sv >= SettingsVersion_v1_9)
5594 && !hw.uuid.isZero()
5595 && hw.uuid.isValid()
5596 )
5597 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
5598
5599 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
5600
5601 if (!hw.fHardwareVirt)
5602 pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
5603 if (!hw.fNestedPaging)
5604 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
5605 if (!hw.fVPID)
5606 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
5607 if (!hw.fUnrestrictedExecution)
5608 pelmCPU->createChild("HardwareVirtExUX")->setAttribute("enabled", hw.fUnrestrictedExecution);
5609 // PAE has too crazy default handling, must always save this setting.
5610 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
5611 if (m->sv >= SettingsVersion_v1_16)
5612 {
5613 if (hw.fIBPBOnVMEntry || hw.fIBPBOnVMExit)
5614 {
5615 xml::ElementNode *pelmChild = pelmCPU->createChild("IBPBOn");
5616 if (hw.fIBPBOnVMExit)
5617 pelmChild->setAttribute("vmexit", hw.fIBPBOnVMExit);
5618 if (hw.fIBPBOnVMEntry)
5619 pelmChild->setAttribute("vmentry", hw.fIBPBOnVMEntry);
5620 }
5621 if (hw.fSpecCtrl)
5622 pelmCPU->createChild("SpecCtrl")->setAttribute("enabled", hw.fSpecCtrl);
5623 if (hw.fSpecCtrlByHost)
5624 pelmCPU->createChild("SpecCtrlByHost")->setAttribute("enabled", hw.fSpecCtrlByHost);
5625 if (!hw.fL1DFlushOnSched || hw.fL1DFlushOnVMEntry)
5626 {
5627 xml::ElementNode *pelmChild = pelmCPU->createChild("L1DFlushOn");
5628 if (!hw.fL1DFlushOnSched)
5629 pelmChild->setAttribute("scheduling", hw.fL1DFlushOnSched);
5630 if (hw.fL1DFlushOnVMEntry)
5631 pelmChild->setAttribute("vmentry", hw.fL1DFlushOnVMEntry);
5632 }
5633 }
5634 if (m->sv >= SettingsVersion_v1_17 && hw.fNestedHWVirt)
5635 pelmCPU->createChild("NestedHWVirt")->setAttribute("enabled", hw.fNestedHWVirt);
5636
5637 if (m->sv >= SettingsVersion_v1_14 && hw.enmLongMode != Hardware::LongMode_Legacy)
5638 {
5639 // LongMode has too crazy default handling, must always save this setting.
5640 pelmCPU->createChild("LongMode")->setAttribute("enabled", hw.enmLongMode == Hardware::LongMode_Enabled);
5641 }
5642
5643 if (hw.fTripleFaultReset)
5644 pelmCPU->createChild("TripleFaultReset")->setAttribute("enabled", hw.fTripleFaultReset);
5645 if (m->sv >= SettingsVersion_v1_14)
5646 {
5647 if (hw.fX2APIC)
5648 pelmCPU->createChild("X2APIC")->setAttribute("enabled", hw.fX2APIC);
5649 else if (!hw.fAPIC)
5650 pelmCPU->createChild("APIC")->setAttribute("enabled", hw.fAPIC);
5651 }
5652 if (hw.cCPUs > 1)
5653 pelmCPU->setAttribute("count", hw.cCPUs);
5654 if (hw.ulCpuExecutionCap != 100)
5655 pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap);
5656 if (hw.uCpuIdPortabilityLevel != 0)
5657 pelmCPU->setAttribute("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
5658 if (!hw.strCpuProfile.equals("host") && hw.strCpuProfile.isNotEmpty())
5659 pelmCPU->setAttribute("CpuProfile", hw.strCpuProfile);
5660
5661 // HardwareVirtExLargePages has too crazy default handling, must always save this setting.
5662 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
5663
5664 if (m->sv >= SettingsVersion_v1_9)
5665 {
5666 if (hw.fHardwareVirtForce)
5667 pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce);
5668 }
5669
5670 if (m->sv >= SettingsVersion_v1_9 && hw.fUseNativeApi)
5671 pelmCPU->createChild("HardwareVirtExUseNativeApi")->setAttribute("enabled", hw.fUseNativeApi);
5672
5673 if (m->sv >= SettingsVersion_v1_10)
5674 {
5675 if (hw.fCpuHotPlug)
5676 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
5677
5678 xml::ElementNode *pelmCpuTree = NULL;
5679 for (CpuList::const_iterator it = hw.llCpus.begin();
5680 it != hw.llCpus.end();
5681 ++it)
5682 {
5683 const Cpu &cpu = *it;
5684
5685 if (pelmCpuTree == NULL)
5686 pelmCpuTree = pelmCPU->createChild("CpuTree");
5687
5688 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
5689 pelmCpu->setAttribute("id", cpu.ulId);
5690 }
5691 }
5692
5693 xml::ElementNode *pelmCpuIdTree = NULL;
5694 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
5695 it != hw.llCpuIdLeafs.end();
5696 ++it)
5697 {
5698 const CpuIdLeaf &leaf = *it;
5699
5700 if (pelmCpuIdTree == NULL)
5701 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
5702
5703 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
5704 pelmCpuIdLeaf->setAttribute("id", leaf.idx);
5705 if (leaf.idxSub != 0)
5706 pelmCpuIdLeaf->setAttribute("subleaf", leaf.idxSub);
5707 pelmCpuIdLeaf->setAttribute("eax", leaf.uEax);
5708 pelmCpuIdLeaf->setAttribute("ebx", leaf.uEbx);
5709 pelmCpuIdLeaf->setAttribute("ecx", leaf.uEcx);
5710 pelmCpuIdLeaf->setAttribute("edx", leaf.uEdx);
5711 }
5712
5713 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
5714 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
5715 if (m->sv >= SettingsVersion_v1_10)
5716 {
5717 if (hw.fPageFusionEnabled)
5718 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
5719 }
5720
5721 if ( (m->sv >= SettingsVersion_v1_9)
5722 && (hw.firmwareType >= FirmwareType_EFI)
5723 )
5724 {
5725 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
5726 const char *pcszFirmware;
5727
5728 switch (hw.firmwareType)
5729 {
5730 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
5731 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
5732 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
5733 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
5734 default: pcszFirmware = "None"; break;
5735 }
5736 pelmFirmware->setAttribute("type", pcszFirmware);
5737 }
5738
5739 if ( m->sv >= SettingsVersion_v1_10
5740 && ( hw.pointingHIDType != PointingHIDType_PS2Mouse
5741 || hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard))
5742 {
5743 xml::ElementNode *pelmHID = pelmHardware->createChild("HID");
5744 const char *pcszHID;
5745
5746 if (hw.pointingHIDType != PointingHIDType_PS2Mouse)
5747 {
5748 switch (hw.pointingHIDType)
5749 {
5750 case PointingHIDType_USBMouse: pcszHID = "USBMouse"; break;
5751 case PointingHIDType_USBTablet: pcszHID = "USBTablet"; break;
5752 case PointingHIDType_PS2Mouse: pcszHID = "PS2Mouse"; break;
5753 case PointingHIDType_ComboMouse: pcszHID = "ComboMouse"; break;
5754 case PointingHIDType_USBMultiTouch: pcszHID = "USBMultiTouch";break;
5755 case PointingHIDType_None: pcszHID = "None"; break;
5756 default: Assert(false); pcszHID = "PS2Mouse"; break;
5757 }
5758 pelmHID->setAttribute("Pointing", pcszHID);
5759 }
5760
5761 if (hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard)
5762 {
5763 switch (hw.keyboardHIDType)
5764 {
5765 case KeyboardHIDType_USBKeyboard: pcszHID = "USBKeyboard"; break;
5766 case KeyboardHIDType_PS2Keyboard: pcszHID = "PS2Keyboard"; break;
5767 case KeyboardHIDType_ComboKeyboard: pcszHID = "ComboKeyboard"; break;
5768 case KeyboardHIDType_None: pcszHID = "None"; break;
5769 default: Assert(false); pcszHID = "PS2Keyboard"; break;
5770 }
5771 pelmHID->setAttribute("Keyboard", pcszHID);
5772 }
5773 }
5774
5775 if ( (m->sv >= SettingsVersion_v1_10)
5776 && hw.fHPETEnabled
5777 )
5778 {
5779 xml::ElementNode *pelmHPET = pelmHardware->createChild("HPET");
5780 pelmHPET->setAttribute("enabled", hw.fHPETEnabled);
5781 }
5782
5783 if ( (m->sv >= SettingsVersion_v1_11)
5784 )
5785 {
5786 if (hw.chipsetType != ChipsetType_PIIX3)
5787 {
5788 xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset");
5789 const char *pcszChipset;
5790
5791 switch (hw.chipsetType)
5792 {
5793 case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break;
5794 case ChipsetType_ICH9: pcszChipset = "ICH9"; break;
5795 default: Assert(false); pcszChipset = "PIIX3"; break;
5796 }
5797 pelmChipset->setAttribute("type", pcszChipset);
5798 }
5799 }
5800
5801 if ( (m->sv >= SettingsVersion_v1_15)
5802 && !hw.areParavirtDefaultSettings(m->sv)
5803 )
5804 {
5805 const char *pcszParavirtProvider;
5806 switch (hw.paravirtProvider)
5807 {
5808 case ParavirtProvider_None: pcszParavirtProvider = "None"; break;
5809 case ParavirtProvider_Default: pcszParavirtProvider = "Default"; break;
5810 case ParavirtProvider_Legacy: pcszParavirtProvider = "Legacy"; break;
5811 case ParavirtProvider_Minimal: pcszParavirtProvider = "Minimal"; break;
5812 case ParavirtProvider_HyperV: pcszParavirtProvider = "HyperV"; break;
5813 case ParavirtProvider_KVM: pcszParavirtProvider = "KVM"; break;
5814 default: Assert(false); pcszParavirtProvider = "None"; break;
5815 }
5816
5817 xml::ElementNode *pelmParavirt = pelmHardware->createChild("Paravirt");
5818 pelmParavirt->setAttribute("provider", pcszParavirtProvider);
5819
5820 if ( m->sv >= SettingsVersion_v1_16
5821 && hw.strParavirtDebug.isNotEmpty())
5822 pelmParavirt->setAttribute("debug", hw.strParavirtDebug);
5823 }
5824
5825 if (!hw.areBootOrderDefaultSettings())
5826 {
5827 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
5828 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
5829 it != hw.mapBootOrder.end();
5830 ++it)
5831 {
5832 uint32_t i = it->first;
5833 DeviceType_T type = it->second;
5834 const char *pcszDevice;
5835
5836 switch (type)
5837 {
5838 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
5839 case DeviceType_DVD: pcszDevice = "DVD"; break;
5840 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
5841 case DeviceType_Network: pcszDevice = "Network"; break;
5842 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
5843 }
5844
5845 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
5846 pelmOrder->setAttribute("position",
5847 i + 1); // XML is 1-based but internal data is 0-based
5848 pelmOrder->setAttribute("device", pcszDevice);
5849 }
5850 }
5851
5852 if (!hw.areDisplayDefaultSettings())
5853 {
5854 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
5855 if (hw.graphicsControllerType != GraphicsControllerType_VBoxVGA)
5856 {
5857 const char *pcszGraphics;
5858 switch (hw.graphicsControllerType)
5859 {
5860 case GraphicsControllerType_VBoxVGA: pcszGraphics = "VBoxVGA"; break;
5861 case GraphicsControllerType_VMSVGA: pcszGraphics = "VMSVGA"; break;
5862 case GraphicsControllerType_VBoxSVGA: pcszGraphics = "VBoxSVGA"; break;
5863 default: /*case GraphicsControllerType_Null:*/ pcszGraphics = "None"; break;
5864 }
5865 pelmDisplay->setAttribute("controller", pcszGraphics);
5866 }
5867 if (hw.ulVRAMSizeMB != 8)
5868 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
5869 if (hw.cMonitors > 1)
5870 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
5871 if (hw.fAccelerate3D)
5872 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
5873
5874 if (m->sv >= SettingsVersion_v1_8)
5875 {
5876 if (hw.fAccelerate2DVideo)
5877 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
5878 }
5879 }
5880
5881 if (m->sv >= SettingsVersion_v1_14 && !hw.recordingSettings.areDefaultSettings())
5882 {
5883 xml::ElementNode *pelmVideoCapture = pelmHardware->createChild("VideoCapture");
5884
5885 if (hw.recordingSettings.fEnabled)
5886 pelmVideoCapture->setAttribute("enabled", hw.recordingSettings.fEnabled);
5887
5888 /* Right now I don't want to bump the settings version, so just convert the enabled
5889 * screens to the former uint64t_t bit array and vice versa. */
5890 uint64_t u64VideoCaptureScreens = 0;
5891 RecordingScreenMap::const_iterator itScreen = hw.recordingSettings.mapScreens.begin();
5892 while (itScreen != hw.recordingSettings.mapScreens.end())
5893 {
5894 if (itScreen->second.fEnabled)
5895 u64VideoCaptureScreens |= RT_BIT_64(itScreen->first);
5896 ++itScreen;
5897 }
5898
5899 if (u64VideoCaptureScreens)
5900 pelmVideoCapture->setAttribute("screens", u64VideoCaptureScreens);
5901
5902 /* At the moment we only support one capturing configuration, that is, all screens
5903 * have the same configuration. So load/save to/from screen 0. */
5904 Assert(hw.recordingSettings.mapScreens.size());
5905 const RecordingScreenMap::const_iterator itScreen0Settings = hw.recordingSettings.mapScreens.find(0);
5906 Assert(itScreen0Settings != hw.recordingSettings.mapScreens.end());
5907
5908 if (itScreen0Settings->second.ulMaxTimeS)
5909 pelmVideoCapture->setAttribute("maxTime", itScreen0Settings->second.ulMaxTimeS);
5910 if (itScreen0Settings->second.strOptions.isNotEmpty())
5911 pelmVideoCapture->setAttributePath("options", itScreen0Settings->second.strOptions);
5912
5913 if (!itScreen0Settings->second.File.strName.isEmpty())
5914 pelmVideoCapture->setAttributePath("file", itScreen0Settings->second.File.strName);
5915 if (itScreen0Settings->second.File.ulMaxSizeMB)
5916 pelmVideoCapture->setAttribute("maxSize", itScreen0Settings->second.File.ulMaxSizeMB);
5917
5918 if ( itScreen0Settings->second.Video.ulWidth != 1024
5919 || itScreen0Settings->second.Video.ulHeight != 768)
5920 {
5921 pelmVideoCapture->setAttribute("horzRes", itScreen0Settings->second.Video.ulWidth);
5922 pelmVideoCapture->setAttribute("vertRes", itScreen0Settings->second.Video.ulHeight);
5923 }
5924 if (itScreen0Settings->second.Video.ulRate != 512)
5925 pelmVideoCapture->setAttribute("rate", itScreen0Settings->second.Video.ulRate);
5926 if (itScreen0Settings->second.Video.ulFPS)
5927 pelmVideoCapture->setAttribute("fps", itScreen0Settings->second.Video.ulFPS);
5928 }
5929
5930 if (!hw.vrdeSettings.areDefaultSettings(m->sv))
5931 {
5932 xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay");
5933 if (m->sv < SettingsVersion_v1_16 ? !hw.vrdeSettings.fEnabled : hw.vrdeSettings.fEnabled)
5934 pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled);
5935 if (m->sv < SettingsVersion_v1_11)
5936 {
5937 /* In VBox 4.0 these attributes are replaced with "Properties". */
5938 Utf8Str strPort;
5939 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports");
5940 if (it != hw.vrdeSettings.mapProperties.end())
5941 strPort = it->second;
5942 if (!strPort.length())
5943 strPort = "3389";
5944 pelmVRDE->setAttribute("port", strPort);
5945
5946 Utf8Str strAddress;
5947 it = hw.vrdeSettings.mapProperties.find("TCP/Address");
5948 if (it != hw.vrdeSettings.mapProperties.end())
5949 strAddress = it->second;
5950 if (strAddress.length())
5951 pelmVRDE->setAttribute("netAddress", strAddress);
5952 }
5953 if (hw.vrdeSettings.authType != AuthType_Null)
5954 {
5955 const char *pcszAuthType;
5956 switch (hw.vrdeSettings.authType)
5957 {
5958 case AuthType_Guest: pcszAuthType = "Guest"; break;
5959 case AuthType_External: pcszAuthType = "External"; break;
5960 default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break;
5961 }
5962 pelmVRDE->setAttribute("authType", pcszAuthType);
5963 }
5964
5965 if (hw.vrdeSettings.ulAuthTimeout != 0 && hw.vrdeSettings.ulAuthTimeout != 5000)
5966 pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout);
5967 if (hw.vrdeSettings.fAllowMultiConnection)
5968 pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
5969 if (hw.vrdeSettings.fReuseSingleConnection)
5970 pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
5971
5972 if (m->sv == SettingsVersion_v1_10)
5973 {
5974 xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel");
5975
5976 /* In 4.0 videochannel settings were replaced with properties, so look at properties. */
5977 Utf8Str str;
5978 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
5979 if (it != hw.vrdeSettings.mapProperties.end())
5980 str = it->second;
5981 bool fVideoChannel = RTStrICmp(str.c_str(), "true") == 0
5982 || RTStrCmp(str.c_str(), "1") == 0;
5983 pelmVideoChannel->setAttribute("enabled", fVideoChannel);
5984
5985 it = hw.vrdeSettings.mapProperties.find("VideoChannel/Quality");
5986 if (it != hw.vrdeSettings.mapProperties.end())
5987 str = it->second;
5988 uint32_t ulVideoChannelQuality = RTStrToUInt32(str.c_str()); /* This returns 0 on invalid string which is ok. */
5989 if (ulVideoChannelQuality == 0)
5990 ulVideoChannelQuality = 75;
5991 else
5992 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
5993 pelmVideoChannel->setAttribute("quality", ulVideoChannelQuality);
5994 }
5995 if (m->sv >= SettingsVersion_v1_11)
5996 {
5997 if (hw.vrdeSettings.strAuthLibrary.length())
5998 pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary);
5999 if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty())
6000 pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
6001 if (hw.vrdeSettings.mapProperties.size() > 0)
6002 {
6003 xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties");
6004 for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin();
6005 it != hw.vrdeSettings.mapProperties.end();
6006 ++it)
6007 {
6008 const Utf8Str &strName = it->first;
6009 const Utf8Str &strValue = it->second;
6010 xml::ElementNode *pelm = pelmProperties->createChild("Property");
6011 pelm->setAttribute("name", strName);
6012 pelm->setAttribute("value", strValue);
6013 }
6014 }
6015 }
6016 }
6017
6018 if (!hw.biosSettings.areDefaultSettings())
6019 {
6020 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
6021 if (!hw.biosSettings.fACPIEnabled)
6022 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
6023 if (hw.biosSettings.fIOAPICEnabled)
6024 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
6025 if (hw.biosSettings.apicMode != APICMode_APIC)
6026 {
6027 const char *pcszAPIC;
6028 switch (hw.biosSettings.apicMode)
6029 {
6030 case APICMode_Disabled:
6031 pcszAPIC = "Disabled";
6032 break;
6033 case APICMode_APIC:
6034 default:
6035 pcszAPIC = "APIC";
6036 break;
6037 case APICMode_X2APIC:
6038 pcszAPIC = "X2APIC";
6039 break;
6040 }
6041 pelmBIOS->createChild("APIC")->setAttribute("mode", pcszAPIC);
6042 }
6043
6044 if ( !hw.biosSettings.fLogoFadeIn
6045 || !hw.biosSettings.fLogoFadeOut
6046 || hw.biosSettings.ulLogoDisplayTime
6047 || !hw.biosSettings.strLogoImagePath.isEmpty())
6048 {
6049 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
6050 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
6051 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
6052 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
6053 if (!hw.biosSettings.strLogoImagePath.isEmpty())
6054 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
6055 }
6056
6057 if (hw.biosSettings.biosBootMenuMode != BIOSBootMenuMode_MessageAndMenu)
6058 {
6059 const char *pcszBootMenu;
6060 switch (hw.biosSettings.biosBootMenuMode)
6061 {
6062 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
6063 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
6064 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
6065 }
6066 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
6067 }
6068 if (hw.biosSettings.llTimeOffset)
6069 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
6070 if (hw.biosSettings.fPXEDebugEnabled)
6071 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
6072 }
6073
6074 if (m->sv < SettingsVersion_v1_9)
6075 {
6076 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
6077 // run thru the storage controllers to see if we have a DVD or floppy drives
6078 size_t cDVDs = 0;
6079 size_t cFloppies = 0;
6080
6081 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
6082 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
6083
6084 for (StorageControllersList::const_iterator it = hw.storage.llStorageControllers.begin();
6085 it != hw.storage.llStorageControllers.end();
6086 ++it)
6087 {
6088 const StorageController &sctl = *it;
6089 // in old settings format, the DVD drive could only have been under the IDE controller
6090 if (sctl.storageBus == StorageBus_IDE)
6091 {
6092 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
6093 it2 != sctl.llAttachedDevices.end();
6094 ++it2)
6095 {
6096 const AttachedDevice &att = *it2;
6097 if (att.deviceType == DeviceType_DVD)
6098 {
6099 if (cDVDs > 0)
6100 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
6101
6102 ++cDVDs;
6103
6104 pelmDVD->setAttribute("passthrough", att.fPassThrough);
6105 if (att.fTempEject)
6106 pelmDVD->setAttribute("tempeject", att.fTempEject);
6107
6108 if (!att.uuid.isZero() && att.uuid.isValid())
6109 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
6110 else if (att.strHostDriveSrc.length())
6111 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6112 }
6113 }
6114 }
6115 else if (sctl.storageBus == StorageBus_Floppy)
6116 {
6117 size_t cFloppiesHere = sctl.llAttachedDevices.size();
6118 if (cFloppiesHere > 1)
6119 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
6120 if (cFloppiesHere)
6121 {
6122 const AttachedDevice &att = sctl.llAttachedDevices.front();
6123 pelmFloppy->setAttribute("enabled", true);
6124
6125 if (!att.uuid.isZero() && att.uuid.isValid())
6126 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
6127 else if (att.strHostDriveSrc.length())
6128 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6129 }
6130
6131 cFloppies += cFloppiesHere;
6132 }
6133 }
6134
6135 if (cFloppies == 0)
6136 pelmFloppy->setAttribute("enabled", false);
6137 else if (cFloppies > 1)
6138 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
6139 }
6140
6141 if (m->sv < SettingsVersion_v1_14)
6142 {
6143 bool fOhciEnabled = false;
6144 bool fEhciEnabled = false;
6145 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
6146
6147 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
6148 it != hw.usbSettings.llUSBControllers.end();
6149 ++it)
6150 {
6151 const USBController &ctrl = *it;
6152
6153 switch (ctrl.enmType)
6154 {
6155 case USBControllerType_OHCI:
6156 fOhciEnabled = true;
6157 break;
6158 case USBControllerType_EHCI:
6159 fEhciEnabled = true;
6160 break;
6161 default:
6162 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
6163 }
6164 }
6165
6166 pelmUSB->setAttribute("enabled", fOhciEnabled);
6167 pelmUSB->setAttribute("enabledEhci", fEhciEnabled);
6168
6169 buildUSBDeviceFilters(*pelmUSB, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
6170 }
6171 else
6172 {
6173 if ( hw.usbSettings.llUSBControllers.size()
6174 || hw.usbSettings.llDeviceFilters.size())
6175 {
6176 xml::ElementNode *pelmUSB = pelmHardware->createChild("USB");
6177 if (hw.usbSettings.llUSBControllers.size())
6178 {
6179 xml::ElementNode *pelmCtrls = pelmUSB->createChild("Controllers");
6180
6181 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
6182 it != hw.usbSettings.llUSBControllers.end();
6183 ++it)
6184 {
6185 const USBController &ctrl = *it;
6186 com::Utf8Str strType;
6187 xml::ElementNode *pelmCtrl = pelmCtrls->createChild("Controller");
6188
6189 switch (ctrl.enmType)
6190 {
6191 case USBControllerType_OHCI:
6192 strType = "OHCI";
6193 break;
6194 case USBControllerType_EHCI:
6195 strType = "EHCI";
6196 break;
6197 case USBControllerType_XHCI:
6198 strType = "XHCI";
6199 break;
6200 default:
6201 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
6202 }
6203
6204 pelmCtrl->setAttribute("name", ctrl.strName);
6205 pelmCtrl->setAttribute("type", strType);
6206 }
6207 }
6208
6209 if (hw.usbSettings.llDeviceFilters.size())
6210 {
6211 xml::ElementNode *pelmFilters = pelmUSB->createChild("DeviceFilters");
6212 buildUSBDeviceFilters(*pelmFilters, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
6213 }
6214 }
6215 }
6216
6217 if ( hw.llNetworkAdapters.size()
6218 && !hw.areAllNetworkAdaptersDefaultSettings(m->sv))
6219 {
6220 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
6221 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
6222 it != hw.llNetworkAdapters.end();
6223 ++it)
6224 {
6225 const NetworkAdapter &nic = *it;
6226
6227 if (!nic.areDefaultSettings(m->sv))
6228 {
6229 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
6230 pelmAdapter->setAttribute("slot", nic.ulSlot);
6231 if (nic.fEnabled)
6232 pelmAdapter->setAttribute("enabled", nic.fEnabled);
6233 if (!nic.strMACAddress.isEmpty())
6234 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
6235 if ( (m->sv >= SettingsVersion_v1_16 && !nic.fCableConnected)
6236 || (m->sv < SettingsVersion_v1_16 && nic.fCableConnected))
6237 pelmAdapter->setAttribute("cable", nic.fCableConnected);
6238 if (nic.ulLineSpeed)
6239 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
6240 if (nic.ulBootPriority != 0)
6241 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
6242 if (nic.fTraceEnabled)
6243 {
6244 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
6245 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
6246 }
6247 if (nic.strBandwidthGroup.isNotEmpty())
6248 pelmAdapter->setAttribute("bandwidthGroup", nic.strBandwidthGroup);
6249
6250 const char *pszPolicy;
6251 switch (nic.enmPromiscModePolicy)
6252 {
6253 case NetworkAdapterPromiscModePolicy_Deny: pszPolicy = NULL; break;
6254 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPolicy = "AllowNetwork"; break;
6255 case NetworkAdapterPromiscModePolicy_AllowAll: pszPolicy = "AllowAll"; break;
6256 default: pszPolicy = NULL; AssertFailed(); break;
6257 }
6258 if (pszPolicy)
6259 pelmAdapter->setAttribute("promiscuousModePolicy", pszPolicy);
6260
6261 if ( (m->sv >= SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C973)
6262 || (m->sv < SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C970A))
6263 {
6264 const char *pcszType;
6265 switch (nic.type)
6266 {
6267 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
6268 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
6269 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
6270 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
6271 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
6272 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
6273 }
6274 pelmAdapter->setAttribute("type", pcszType);
6275 }
6276
6277 xml::ElementNode *pelmNAT;
6278 if (m->sv < SettingsVersion_v1_10)
6279 {
6280 switch (nic.mode)
6281 {
6282 case NetworkAttachmentType_NAT:
6283 pelmNAT = pelmAdapter->createChild("NAT");
6284 if (nic.nat.strNetwork.length())
6285 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6286 break;
6287
6288 case NetworkAttachmentType_Bridged:
6289 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
6290 break;
6291
6292 case NetworkAttachmentType_Internal:
6293 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
6294 break;
6295
6296 case NetworkAttachmentType_HostOnly:
6297 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
6298 break;
6299
6300 default: /*case NetworkAttachmentType_Null:*/
6301 break;
6302 }
6303 }
6304 else
6305 {
6306 /* m->sv >= SettingsVersion_v1_10 */
6307 if (!nic.areDisabledDefaultSettings())
6308 {
6309 xml::ElementNode *pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
6310 if (nic.mode != NetworkAttachmentType_NAT)
6311 buildNetworkXML(NetworkAttachmentType_NAT, false, *pelmDisabledNode, nic);
6312 if (nic.mode != NetworkAttachmentType_Bridged)
6313 buildNetworkXML(NetworkAttachmentType_Bridged, false, *pelmDisabledNode, nic);
6314 if (nic.mode != NetworkAttachmentType_Internal)
6315 buildNetworkXML(NetworkAttachmentType_Internal, false, *pelmDisabledNode, nic);
6316 if (nic.mode != NetworkAttachmentType_HostOnly)
6317 buildNetworkXML(NetworkAttachmentType_HostOnly, false, *pelmDisabledNode, nic);
6318 if (nic.mode != NetworkAttachmentType_Generic)
6319 buildNetworkXML(NetworkAttachmentType_Generic, false, *pelmDisabledNode, nic);
6320 if (nic.mode != NetworkAttachmentType_NATNetwork)
6321 buildNetworkXML(NetworkAttachmentType_NATNetwork, false, *pelmDisabledNode, nic);
6322 }
6323 buildNetworkXML(nic.mode, true, *pelmAdapter, nic);
6324 }
6325 }
6326 }
6327 }
6328
6329 if (hw.llSerialPorts.size())
6330 {
6331 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
6332 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
6333 it != hw.llSerialPorts.end();
6334 ++it)
6335 {
6336 const SerialPort &port = *it;
6337 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6338 pelmPort->setAttribute("slot", port.ulSlot);
6339 pelmPort->setAttribute("enabled", port.fEnabled);
6340 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6341 pelmPort->setAttribute("IRQ", port.ulIRQ);
6342
6343 const char *pcszHostMode;
6344 switch (port.portMode)
6345 {
6346 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
6347 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
6348 case PortMode_TCP: pcszHostMode = "TCP"; break;
6349 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
6350 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
6351 }
6352 switch (port.portMode)
6353 {
6354 case PortMode_TCP:
6355 case PortMode_HostPipe:
6356 pelmPort->setAttribute("server", port.fServer);
6357 RT_FALL_THRU();
6358 case PortMode_HostDevice:
6359 case PortMode_RawFile:
6360 pelmPort->setAttribute("path", port.strPath);
6361 break;
6362
6363 default:
6364 break;
6365 }
6366 pelmPort->setAttribute("hostMode", pcszHostMode);
6367
6368 if ( m->sv >= SettingsVersion_v1_17
6369 && port.uartType != UartType_U16550A)
6370 {
6371 const char *pcszUartType;
6372
6373 switch (port.uartType)
6374 {
6375 case UartType_U16450: pcszUartType = "16450"; break;
6376 case UartType_U16550A: pcszUartType = "16550A"; break;
6377 case UartType_U16750: pcszUartType = "16750"; break;
6378 default: pcszUartType = "16550A"; break;
6379 }
6380 pelmPort->setAttribute("uartType", pcszUartType);
6381 }
6382 }
6383 }
6384
6385 if (hw.llParallelPorts.size())
6386 {
6387 xml::ElementNode *pelmPorts = pelmHardware->createChild("LPT");
6388 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
6389 it != hw.llParallelPorts.end();
6390 ++it)
6391 {
6392 const ParallelPort &port = *it;
6393 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6394 pelmPort->setAttribute("slot", port.ulSlot);
6395 pelmPort->setAttribute("enabled", port.fEnabled);
6396 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6397 pelmPort->setAttribute("IRQ", port.ulIRQ);
6398 if (port.strPath.length())
6399 pelmPort->setAttribute("path", port.strPath);
6400 }
6401 }
6402
6403 /* Always write the AudioAdapter config, intentionally not checking if
6404 * the settings are at the default, because that would be problematic
6405 * for the configured host driver type, which would automatically change
6406 * if the default host driver is detected differently. */
6407 {
6408 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
6409
6410 const char *pcszController;
6411 switch (hw.audioAdapter.controllerType)
6412 {
6413 case AudioControllerType_SB16:
6414 pcszController = "SB16";
6415 break;
6416 case AudioControllerType_HDA:
6417 if (m->sv >= SettingsVersion_v1_11)
6418 {
6419 pcszController = "HDA";
6420 break;
6421 }
6422 RT_FALL_THRU();
6423 case AudioControllerType_AC97:
6424 default:
6425 pcszController = NULL;
6426 break;
6427 }
6428 if (pcszController)
6429 pelmAudio->setAttribute("controller", pcszController);
6430
6431 const char *pcszCodec;
6432 switch (hw.audioAdapter.codecType)
6433 {
6434 /* Only write out the setting for non-default AC'97 codec
6435 * and leave the rest alone.
6436 */
6437#if 0
6438 case AudioCodecType_SB16:
6439 pcszCodec = "SB16";
6440 break;
6441 case AudioCodecType_STAC9221:
6442 pcszCodec = "STAC9221";
6443 break;
6444 case AudioCodecType_STAC9700:
6445 pcszCodec = "STAC9700";
6446 break;
6447#endif
6448 case AudioCodecType_AD1980:
6449 pcszCodec = "AD1980";
6450 break;
6451 default:
6452 /* Don't write out anything if unknown. */
6453 pcszCodec = NULL;
6454 }
6455 if (pcszCodec)
6456 pelmAudio->setAttribute("codec", pcszCodec);
6457
6458 const char *pcszDriver;
6459 switch (hw.audioAdapter.driverType)
6460 {
6461 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
6462 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
6463 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
6464 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
6465 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
6466 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
6467 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
6468 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
6469 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
6470 }
6471 /* Deliberately have the audio driver explicitly in the config file,
6472 * otherwise an unwritten default driver triggers auto-detection. */
6473 pelmAudio->setAttribute("driver", pcszDriver);
6474
6475 if (hw.audioAdapter.fEnabled || m->sv < SettingsVersion_v1_16)
6476 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
6477
6478 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledIn)
6479 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledIn))
6480 pelmAudio->setAttribute("enabledIn", hw.audioAdapter.fEnabledIn);
6481
6482 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledOut)
6483 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledOut))
6484 pelmAudio->setAttribute("enabledOut", hw.audioAdapter.fEnabledOut);
6485
6486 if (m->sv >= SettingsVersion_v1_15 && hw.audioAdapter.properties.size() > 0)
6487 {
6488 for (StringsMap::const_iterator it = hw.audioAdapter.properties.begin();
6489 it != hw.audioAdapter.properties.end();
6490 ++it)
6491 {
6492 const Utf8Str &strName = it->first;
6493 const Utf8Str &strValue = it->second;
6494 xml::ElementNode *pelm = pelmAudio->createChild("Property");
6495 pelm->setAttribute("name", strName);
6496 pelm->setAttribute("value", strValue);
6497 }
6498 }
6499 }
6500
6501 if (m->sv >= SettingsVersion_v1_10 && machineUserData.fRTCUseUTC)
6502 {
6503 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
6504 pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local");
6505 }
6506
6507 if (hw.llSharedFolders.size())
6508 {
6509 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
6510 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
6511 it != hw.llSharedFolders.end();
6512 ++it)
6513 {
6514 const SharedFolder &sf = *it;
6515 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
6516 pelmThis->setAttribute("name", sf.strName);
6517 pelmThis->setAttribute("hostPath", sf.strHostPath);
6518 pelmThis->setAttribute("writable", sf.fWritable);
6519 pelmThis->setAttribute("autoMount", sf.fAutoMount);
6520 if (sf.strAutoMountPoint.isNotEmpty())
6521 pelmThis->setAttribute("autoMountPoint", sf.strAutoMountPoint);
6522 }
6523 }
6524
6525 if (hw.clipboardMode != ClipboardMode_Disabled)
6526 {
6527 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
6528 const char *pcszClip;
6529 switch (hw.clipboardMode)
6530 {
6531 default: /*case ClipboardMode_Disabled:*/ pcszClip = "Disabled"; break;
6532 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
6533 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
6534 case ClipboardMode_Bidirectional: pcszClip = "Bidirectional"; break;
6535 }
6536 pelmClip->setAttribute("mode", pcszClip);
6537 }
6538
6539 if (hw.dndMode != DnDMode_Disabled)
6540 {
6541 xml::ElementNode *pelmDragAndDrop = pelmHardware->createChild("DragAndDrop");
6542 const char *pcszDragAndDrop;
6543 switch (hw.dndMode)
6544 {
6545 default: /*case DnDMode_Disabled:*/ pcszDragAndDrop = "Disabled"; break;
6546 case DnDMode_HostToGuest: pcszDragAndDrop = "HostToGuest"; break;
6547 case DnDMode_GuestToHost: pcszDragAndDrop = "GuestToHost"; break;
6548 case DnDMode_Bidirectional: pcszDragAndDrop = "Bidirectional"; break;
6549 }
6550 pelmDragAndDrop->setAttribute("mode", pcszDragAndDrop);
6551 }
6552
6553 if ( m->sv >= SettingsVersion_v1_10
6554 && !hw.ioSettings.areDefaultSettings())
6555 {
6556 xml::ElementNode *pelmIO = pelmHardware->createChild("IO");
6557 xml::ElementNode *pelmIOCache;
6558
6559 if (!hw.ioSettings.areDefaultSettings())
6560 {
6561 pelmIOCache = pelmIO->createChild("IoCache");
6562 if (!hw.ioSettings.fIOCacheEnabled)
6563 pelmIOCache->setAttribute("enabled", hw.ioSettings.fIOCacheEnabled);
6564 if (hw.ioSettings.ulIOCacheSize != 5)
6565 pelmIOCache->setAttribute("size", hw.ioSettings.ulIOCacheSize);
6566 }
6567
6568 if ( m->sv >= SettingsVersion_v1_11
6569 && hw.ioSettings.llBandwidthGroups.size())
6570 {
6571 xml::ElementNode *pelmBandwidthGroups = pelmIO->createChild("BandwidthGroups");
6572 for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin();
6573 it != hw.ioSettings.llBandwidthGroups.end();
6574 ++it)
6575 {
6576 const BandwidthGroup &gr = *it;
6577 const char *pcszType;
6578 xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup");
6579 pelmThis->setAttribute("name", gr.strName);
6580 switch (gr.enmType)
6581 {
6582 case BandwidthGroupType_Network: pcszType = "Network"; break;
6583 default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break;
6584 }
6585 pelmThis->setAttribute("type", pcszType);
6586 if (m->sv >= SettingsVersion_v1_13)
6587 pelmThis->setAttribute("maxBytesPerSec", gr.cMaxBytesPerSec);
6588 else
6589 pelmThis->setAttribute("maxMbPerSec", gr.cMaxBytesPerSec / _1M);
6590 }
6591 }
6592 }
6593
6594 if ( m->sv >= SettingsVersion_v1_12
6595 && hw.pciAttachments.size())
6596 {
6597 xml::ElementNode *pelmPCI = pelmHardware->createChild("HostPci");
6598 xml::ElementNode *pelmPCIDevices = pelmPCI->createChild("Devices");
6599
6600 for (HostPCIDeviceAttachmentList::const_iterator it = hw.pciAttachments.begin();
6601 it != hw.pciAttachments.end();
6602 ++it)
6603 {
6604 const HostPCIDeviceAttachment &hpda = *it;
6605
6606 xml::ElementNode *pelmThis = pelmPCIDevices->createChild("Device");
6607
6608 pelmThis->setAttribute("host", hpda.uHostAddress);
6609 pelmThis->setAttribute("guest", hpda.uGuestAddress);
6610 pelmThis->setAttribute("name", hpda.strDeviceName);
6611 }
6612 }
6613
6614 if ( m->sv >= SettingsVersion_v1_12
6615 && hw.fEmulatedUSBCardReader)
6616 {
6617 xml::ElementNode *pelmEmulatedUSB = pelmHardware->createChild("EmulatedUSB");
6618
6619 xml::ElementNode *pelmCardReader = pelmEmulatedUSB->createChild("CardReader");
6620 pelmCardReader->setAttribute("enabled", hw.fEmulatedUSBCardReader);
6621 }
6622
6623 if ( m->sv >= SettingsVersion_v1_14
6624 && !hw.strDefaultFrontend.isEmpty())
6625 {
6626 xml::ElementNode *pelmFrontend = pelmHardware->createChild("Frontend");
6627 xml::ElementNode *pelmDefault = pelmFrontend->createChild("Default");
6628 pelmDefault->setAttribute("type", hw.strDefaultFrontend);
6629 }
6630
6631 if (hw.ulMemoryBalloonSize)
6632 {
6633 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
6634 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
6635 }
6636
6637 if (hw.llGuestProperties.size())
6638 {
6639 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
6640 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
6641 it != hw.llGuestProperties.end();
6642 ++it)
6643 {
6644 const GuestProperty &prop = *it;
6645 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
6646 pelmProp->setAttribute("name", prop.strName);
6647 pelmProp->setAttribute("value", prop.strValue);
6648 pelmProp->setAttribute("timestamp", prop.timestamp);
6649 pelmProp->setAttribute("flags", prop.strFlags);
6650 }
6651 }
6652
6653 /** @todo In the future (6.0?) place the storage controllers under \<Hardware\>, because
6654 * this is where it always should've been. What else than hardware are they? */
6655 xml::ElementNode &elmStorageParent = (m->sv > SettingsVersion_Future) ? *pelmHardware : elmParent;
6656 buildStorageControllersXML(elmStorageParent,
6657 hw.storage,
6658 !!(fl & BuildMachineXML_SkipRemovableMedia),
6659 pllElementsWithUuidAttributes);
6660}
6661
6662/**
6663 * Fill a \<Network\> node. Only relevant for XML version >= v1_10.
6664 * @param mode
6665 * @param fEnabled
6666 * @param elmParent
6667 * @param nic
6668 */
6669void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
6670 bool fEnabled,
6671 xml::ElementNode &elmParent,
6672 const NetworkAdapter &nic)
6673{
6674 switch (mode)
6675 {
6676 case NetworkAttachmentType_NAT:
6677 // For the currently active network attachment type we have to
6678 // generate the tag, otherwise the attachment type is lost.
6679 if (fEnabled || !nic.nat.areDefaultSettings())
6680 {
6681 xml::ElementNode *pelmNAT = elmParent.createChild("NAT");
6682
6683 if (!nic.nat.areDefaultSettings())
6684 {
6685 if (nic.nat.strNetwork.length())
6686 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6687 if (nic.nat.strBindIP.length())
6688 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
6689 if (nic.nat.u32Mtu)
6690 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
6691 if (nic.nat.u32SockRcv)
6692 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
6693 if (nic.nat.u32SockSnd)
6694 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
6695 if (nic.nat.u32TcpRcv)
6696 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
6697 if (nic.nat.u32TcpSnd)
6698 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
6699 if (!nic.nat.areDNSDefaultSettings())
6700 {
6701 xml::ElementNode *pelmDNS = pelmNAT->createChild("DNS");
6702 if (!nic.nat.fDNSPassDomain)
6703 pelmDNS->setAttribute("pass-domain", nic.nat.fDNSPassDomain);
6704 if (nic.nat.fDNSProxy)
6705 pelmDNS->setAttribute("use-proxy", nic.nat.fDNSProxy);
6706 if (nic.nat.fDNSUseHostResolver)
6707 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDNSUseHostResolver);
6708 }
6709
6710 if (!nic.nat.areAliasDefaultSettings())
6711 {
6712 xml::ElementNode *pelmAlias = pelmNAT->createChild("Alias");
6713 if (nic.nat.fAliasLog)
6714 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
6715 if (nic.nat.fAliasProxyOnly)
6716 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
6717 if (nic.nat.fAliasUseSamePorts)
6718 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
6719 }
6720
6721 if (!nic.nat.areTFTPDefaultSettings())
6722 {
6723 xml::ElementNode *pelmTFTP;
6724 pelmTFTP = pelmNAT->createChild("TFTP");
6725 if (nic.nat.strTFTPPrefix.length())
6726 pelmTFTP->setAttribute("prefix", nic.nat.strTFTPPrefix);
6727 if (nic.nat.strTFTPBootFile.length())
6728 pelmTFTP->setAttribute("boot-file", nic.nat.strTFTPBootFile);
6729 if (nic.nat.strTFTPNextServer.length())
6730 pelmTFTP->setAttribute("next-server", nic.nat.strTFTPNextServer);
6731 }
6732 buildNATForwardRulesMap(*pelmNAT, nic.nat.mapRules);
6733 }
6734 }
6735 break;
6736
6737 case NetworkAttachmentType_Bridged:
6738 // For the currently active network attachment type we have to
6739 // generate the tag, otherwise the attachment type is lost.
6740 if (fEnabled || !nic.strBridgedName.isEmpty())
6741 {
6742 xml::ElementNode *pelmMode = elmParent.createChild("BridgedInterface");
6743 if (!nic.strBridgedName.isEmpty())
6744 pelmMode->setAttribute("name", nic.strBridgedName);
6745 }
6746 break;
6747
6748 case NetworkAttachmentType_Internal:
6749 // For the currently active network attachment type we have to
6750 // generate the tag, otherwise the attachment type is lost.
6751 if (fEnabled || !nic.strInternalNetworkName.isEmpty())
6752 {
6753 xml::ElementNode *pelmMode = elmParent.createChild("InternalNetwork");
6754 if (!nic.strInternalNetworkName.isEmpty())
6755 pelmMode->setAttribute("name", nic.strInternalNetworkName);
6756 }
6757 break;
6758
6759 case NetworkAttachmentType_HostOnly:
6760 // For the currently active network attachment type we have to
6761 // generate the tag, otherwise the attachment type is lost.
6762 if (fEnabled || !nic.strHostOnlyName.isEmpty())
6763 {
6764 xml::ElementNode *pelmMode = elmParent.createChild("HostOnlyInterface");
6765 if (!nic.strHostOnlyName.isEmpty())
6766 pelmMode->setAttribute("name", nic.strHostOnlyName);
6767 }
6768 break;
6769
6770 case NetworkAttachmentType_Generic:
6771 // For the currently active network attachment type we have to
6772 // generate the tag, otherwise the attachment type is lost.
6773 if (fEnabled || !nic.areGenericDriverDefaultSettings())
6774 {
6775 xml::ElementNode *pelmMode = elmParent.createChild("GenericInterface");
6776 if (!nic.areGenericDriverDefaultSettings())
6777 {
6778 pelmMode->setAttribute("driver", nic.strGenericDriver);
6779 for (StringsMap::const_iterator it = nic.genericProperties.begin();
6780 it != nic.genericProperties.end();
6781 ++it)
6782 {
6783 xml::ElementNode *pelmProp = pelmMode->createChild("Property");
6784 pelmProp->setAttribute("name", it->first);
6785 pelmProp->setAttribute("value", it->second);
6786 }
6787 }
6788 }
6789 break;
6790
6791 case NetworkAttachmentType_NATNetwork:
6792 // For the currently active network attachment type we have to
6793 // generate the tag, otherwise the attachment type is lost.
6794 if (fEnabled || !nic.strNATNetworkName.isEmpty())
6795 {
6796 xml::ElementNode *pelmMode = elmParent.createChild("NATNetwork");
6797 if (!nic.strNATNetworkName.isEmpty())
6798 pelmMode->setAttribute("name", nic.strNATNetworkName);
6799 }
6800 break;
6801
6802 default: /*case NetworkAttachmentType_Null:*/
6803 break;
6804 }
6805}
6806
6807/**
6808 * Creates a \<StorageControllers\> node under elmParent and then writes out the XML
6809 * keys under that. Called for both the \<Machine\> node and for snapshots.
6810 * @param elmParent
6811 * @param st
6812 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
6813 * an empty drive is always written instead. This is for the OVF export case.
6814 * This parameter is ignored unless the settings version is at least v1.9, which
6815 * is always the case when this gets called for OVF export.
6816 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
6817 * pointers to which we will append all elements that we created here that contain
6818 * UUID attributes. This allows the OVF export code to quickly replace the internal
6819 * media UUIDs with the UUIDs of the media that were exported.
6820 */
6821void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
6822 const Storage &st,
6823 bool fSkipRemovableMedia,
6824 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
6825{
6826 if (!st.llStorageControllers.size())
6827 return;
6828 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
6829
6830 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
6831 it != st.llStorageControllers.end();
6832 ++it)
6833 {
6834 const StorageController &sc = *it;
6835
6836 if ( (m->sv < SettingsVersion_v1_9)
6837 && (sc.controllerType == StorageControllerType_I82078)
6838 )
6839 // floppy controller already got written into <Hardware>/<FloppyController> in buildHardwareXML()
6840 // for pre-1.9 settings
6841 continue;
6842
6843 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
6844 com::Utf8Str name = sc.strName;
6845 if (m->sv < SettingsVersion_v1_8)
6846 {
6847 // pre-1.8 settings use shorter controller names, they are
6848 // expanded when reading the settings
6849 if (name == "IDE Controller")
6850 name = "IDE";
6851 else if (name == "SATA Controller")
6852 name = "SATA";
6853 else if (name == "SCSI Controller")
6854 name = "SCSI";
6855 }
6856 pelmController->setAttribute("name", sc.strName);
6857
6858 const char *pcszType;
6859 switch (sc.controllerType)
6860 {
6861 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
6862 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
6863 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
6864 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
6865 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
6866 case StorageControllerType_I82078: pcszType = "I82078"; break;
6867 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
6868 case StorageControllerType_USB: pcszType = "USB"; break;
6869 case StorageControllerType_NVMe: pcszType = "NVMe"; break;
6870 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
6871 }
6872 pelmController->setAttribute("type", pcszType);
6873
6874 pelmController->setAttribute("PortCount", sc.ulPortCount);
6875
6876 if (m->sv >= SettingsVersion_v1_9)
6877 if (sc.ulInstance)
6878 pelmController->setAttribute("Instance", sc.ulInstance);
6879
6880 if (m->sv >= SettingsVersion_v1_10)
6881 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
6882
6883 if (m->sv >= SettingsVersion_v1_11)
6884 pelmController->setAttribute("Bootable", sc.fBootable);
6885
6886 if (sc.controllerType == StorageControllerType_IntelAhci)
6887 {
6888 pelmController->setAttribute("IDE0MasterEmulationPort", 0);
6889 pelmController->setAttribute("IDE0SlaveEmulationPort", 1);
6890 pelmController->setAttribute("IDE1MasterEmulationPort", 2);
6891 pelmController->setAttribute("IDE1SlaveEmulationPort", 3);
6892 }
6893
6894 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
6895 it2 != sc.llAttachedDevices.end();
6896 ++it2)
6897 {
6898 const AttachedDevice &att = *it2;
6899
6900 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
6901 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
6902 // the floppy controller at the top of the loop
6903 if ( att.deviceType == DeviceType_DVD
6904 && m->sv < SettingsVersion_v1_9
6905 )
6906 continue;
6907
6908 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
6909
6910 pcszType = NULL;
6911
6912 switch (att.deviceType)
6913 {
6914 case DeviceType_HardDisk:
6915 pcszType = "HardDisk";
6916 if (att.fNonRotational)
6917 pelmDevice->setAttribute("nonrotational", att.fNonRotational);
6918 if (att.fDiscard)
6919 pelmDevice->setAttribute("discard", att.fDiscard);
6920 break;
6921
6922 case DeviceType_DVD:
6923 pcszType = "DVD";
6924 pelmDevice->setAttribute("passthrough", att.fPassThrough);
6925 if (att.fTempEject)
6926 pelmDevice->setAttribute("tempeject", att.fTempEject);
6927 break;
6928
6929 case DeviceType_Floppy:
6930 pcszType = "Floppy";
6931 break;
6932
6933 default: break; /* Shut up MSC. */
6934 }
6935
6936 pelmDevice->setAttribute("type", pcszType);
6937
6938 if (m->sv >= SettingsVersion_v1_15)
6939 pelmDevice->setAttribute("hotpluggable", att.fHotPluggable);
6940
6941 pelmDevice->setAttribute("port", att.lPort);
6942 pelmDevice->setAttribute("device", att.lDevice);
6943
6944 if (att.strBwGroup.length())
6945 pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup);
6946
6947 // attached image, if any
6948 if (!att.uuid.isZero()
6949 && att.uuid.isValid()
6950 && (att.deviceType == DeviceType_HardDisk
6951 || !fSkipRemovableMedia
6952 )
6953 )
6954 {
6955 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
6956 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
6957
6958 // if caller wants a list of UUID elements, give it to them
6959 if (pllElementsWithUuidAttributes)
6960 pllElementsWithUuidAttributes->push_back(pelmImage);
6961 }
6962 else if ( (m->sv >= SettingsVersion_v1_9)
6963 && (att.strHostDriveSrc.length())
6964 )
6965 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6966 }
6967 }
6968}
6969
6970/**
6971 * Creates a \<Debugging\> node under elmParent and then writes out the XML
6972 * keys under that. Called for both the \<Machine\> node and for snapshots.
6973 *
6974 * @param pElmParent Pointer to the parent element.
6975 * @param pDbg Pointer to the debugging settings.
6976 */
6977void MachineConfigFile::buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg)
6978{
6979 if (m->sv < SettingsVersion_v1_13 || pDbg->areDefaultSettings())
6980 return;
6981
6982 xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");
6983 xml::ElementNode *pElmTracing = pElmDebugging->createChild("Tracing");
6984 pElmTracing->setAttribute("enabled", pDbg->fTracingEnabled);
6985 pElmTracing->setAttribute("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
6986 pElmTracing->setAttribute("config", pDbg->strTracingConfig);
6987}
6988
6989/**
6990 * Creates a \<Autostart\> node under elmParent and then writes out the XML
6991 * keys under that. Called for both the \<Machine\> node and for snapshots.
6992 *
6993 * @param pElmParent Pointer to the parent element.
6994 * @param pAutostart Pointer to the autostart settings.
6995 */
6996void MachineConfigFile::buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart)
6997{
6998 const char *pcszAutostop = NULL;
6999
7000 if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())
7001 return;
7002
7003 xml::ElementNode *pElmAutostart = pElmParent->createChild("Autostart");
7004 pElmAutostart->setAttribute("enabled", pAutostart->fAutostartEnabled);
7005 pElmAutostart->setAttribute("delay", pAutostart->uAutostartDelay);
7006
7007 switch (pAutostart->enmAutostopType)
7008 {
7009 case AutostopType_Disabled: pcszAutostop = "Disabled"; break;
7010 case AutostopType_SaveState: pcszAutostop = "SaveState"; break;
7011 case AutostopType_PowerOff: pcszAutostop = "PowerOff"; break;
7012 case AutostopType_AcpiShutdown: pcszAutostop = "AcpiShutdown"; break;
7013 default: Assert(false); pcszAutostop = "Disabled"; break;
7014 }
7015 pElmAutostart->setAttribute("autostop", pcszAutostop);
7016}
7017
7018/**
7019 * Creates a \<Groups\> node under elmParent and then writes out the XML
7020 * keys under that. Called for the \<Machine\> node only.
7021 *
7022 * @param pElmParent Pointer to the parent element.
7023 * @param pllGroups Pointer to the groups list.
7024 */
7025void MachineConfigFile::buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups)
7026{
7027 if ( m->sv < SettingsVersion_v1_13 || pllGroups->size() == 0
7028 || (pllGroups->size() == 1 && pllGroups->front() == "/"))
7029 return;
7030
7031 xml::ElementNode *pElmGroups = pElmParent->createChild("Groups");
7032 for (StringsList::const_iterator it = pllGroups->begin();
7033 it != pllGroups->end();
7034 ++it)
7035 {
7036 const Utf8Str &group = *it;
7037 xml::ElementNode *pElmGroup = pElmGroups->createChild("Group");
7038 pElmGroup->setAttribute("name", group);
7039 }
7040}
7041
7042/**
7043 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
7044 * for the root snapshot of a machine, if present; elmParent then points to the \<Snapshots\> node under the
7045 * \<Machine\> node to which \<Snapshot\> must be added. This may then recurse for child snapshots.
7046 *
7047 * @param depth
7048 * @param elmParent
7049 * @param snap
7050 */
7051void MachineConfigFile::buildSnapshotXML(uint32_t depth,
7052 xml::ElementNode &elmParent,
7053 const Snapshot &snap)
7054{
7055 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
7056 throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
7057
7058 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
7059
7060 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
7061 pelmSnapshot->setAttribute("name", snap.strName);
7062 pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(snap.timestamp));
7063
7064 if (snap.strStateFile.length())
7065 pelmSnapshot->setAttributePath("stateFile", snap.strStateFile);
7066
7067 if (snap.strDescription.length())
7068 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
7069
7070 // We only skip removable media for OVF, but OVF never includes snapshots.
7071 buildHardwareXML(*pelmSnapshot, snap.hardware, 0 /* fl */, NULL /* pllElementsWithUuidAttributes */);
7072 buildDebuggingXML(pelmSnapshot, &snap.debugging);
7073 buildAutostartXML(pelmSnapshot, &snap.autostart);
7074 // note: Groups exist only for Machine, not for Snapshot
7075
7076 if (snap.llChildSnapshots.size())
7077 {
7078 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
7079 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
7080 it != snap.llChildSnapshots.end();
7081 ++it)
7082 {
7083 const Snapshot &child = *it;
7084 buildSnapshotXML(depth + 1, *pelmChildren, child);
7085 }
7086 }
7087}
7088
7089/**
7090 * Builds the XML DOM tree for the machine config under the given XML element.
7091 *
7092 * This has been separated out from write() so it can be called from elsewhere,
7093 * such as the OVF code, to build machine XML in an existing XML tree.
7094 *
7095 * As a result, this gets called from two locations:
7096 *
7097 * -- MachineConfigFile::write();
7098 *
7099 * -- Appliance::buildXMLForOneVirtualSystem()
7100 *
7101 * In fl, the following flag bits are recognized:
7102 *
7103 * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will
7104 * be written, if present. This is not set when called from OVF because OVF
7105 * has its own variant of a media registry. This flag is ignored unless the
7106 * settings version is at least v1.11 (VirtualBox 4.0).
7107 *
7108 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
7109 * of the machine and write out \<Snapshot\> and possibly more snapshots under
7110 * that, if snapshots are present. Otherwise all snapshots are suppressed
7111 * (when called from OVF).
7112 *
7113 * -- BuildMachineXML_WriteVBoxVersionAttribute: If set, add a settingsVersion
7114 * attribute to the machine tag with the vbox settings version. This is for
7115 * the OVF export case in which we don't have the settings version set in
7116 * the root element.
7117 *
7118 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
7119 * (DVDs, floppies) are silently skipped. This is for the OVF export case
7120 * until we support copying ISO and RAW media as well. This flag is ignored
7121 * unless the settings version is at least v1.9, which is always the case
7122 * when this gets called for OVF export.
7123 *
7124 * -- BuildMachineXML_SuppressSavedState: If set, the Machine/stateFile
7125 * attribute is never set. This is also for the OVF export case because we
7126 * cannot save states with OVF.
7127 *
7128 * @param elmMachine XML \<Machine\> element to add attributes and elements to.
7129 * @param fl Flags.
7130 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
7131 * see buildStorageControllersXML() for details.
7132 */
7133void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
7134 uint32_t fl,
7135 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
7136{
7137 if (fl & BuildMachineXML_WriteVBoxVersionAttribute)
7138 {
7139 // add settings version attribute to machine element
7140 setVersionAttribute(elmMachine);
7141 LogRel(("Exporting settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
7142 }
7143
7144 elmMachine.setAttribute("uuid", uuid.toStringCurly());
7145 elmMachine.setAttribute("name", machineUserData.strName);
7146 if (machineUserData.fDirectoryIncludesUUID)
7147 elmMachine.setAttribute("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
7148 if (!machineUserData.fNameSync)
7149 elmMachine.setAttribute("nameSync", machineUserData.fNameSync);
7150 if (machineUserData.strDescription.length())
7151 elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
7152 elmMachine.setAttribute("OSType", machineUserData.strOsType);
7153 if ( strStateFile.length()
7154 && !(fl & BuildMachineXML_SuppressSavedState)
7155 )
7156 elmMachine.setAttributePath("stateFile", strStateFile);
7157
7158 if ((fl & BuildMachineXML_IncludeSnapshots)
7159 && !uuidCurrentSnapshot.isZero()
7160 && uuidCurrentSnapshot.isValid())
7161 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
7162
7163 if (machineUserData.strSnapshotFolder.length())
7164 elmMachine.setAttributePath("snapshotFolder", machineUserData.strSnapshotFolder);
7165 if (!fCurrentStateModified)
7166 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
7167 elmMachine.setAttribute("lastStateChange", stringifyTimestamp(timeLastStateChange));
7168 if (fAborted)
7169 elmMachine.setAttribute("aborted", fAborted);
7170
7171 switch (machineUserData.enmVMPriority)
7172 {
7173 case VMProcPriority_Flat:
7174 elmMachine.setAttribute("processPriority", "Flat");
7175 break;
7176 case VMProcPriority_Low:
7177 elmMachine.setAttribute("processPriority", "Low");
7178 break;
7179 case VMProcPriority_Normal:
7180 elmMachine.setAttribute("processPriority", "Normal");
7181 break;
7182 case VMProcPriority_High:
7183 elmMachine.setAttribute("processPriority", "High");
7184 break;
7185 default:
7186 break;
7187 }
7188 // Please keep the icon last so that one doesn't have to check if there
7189 // is anything in the line after this very long attribute in the XML.
7190 if (machineUserData.ovIcon.size())
7191 {
7192 Utf8Str strIcon;
7193 toBase64(strIcon, machineUserData.ovIcon);
7194 elmMachine.setAttribute("icon", strIcon);
7195 }
7196 if ( m->sv >= SettingsVersion_v1_9
7197 && ( machineUserData.fTeleporterEnabled
7198 || machineUserData.uTeleporterPort
7199 || !machineUserData.strTeleporterAddress.isEmpty()
7200 || !machineUserData.strTeleporterPassword.isEmpty()
7201 )
7202 )
7203 {
7204 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
7205 pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled);
7206 pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort);
7207 pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress);
7208 pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword);
7209 }
7210
7211 if ( m->sv >= SettingsVersion_v1_11
7212 && ( machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
7213 || machineUserData.uFaultTolerancePort
7214 || machineUserData.uFaultToleranceInterval
7215 || !machineUserData.strFaultToleranceAddress.isEmpty()
7216 )
7217 )
7218 {
7219 xml::ElementNode *pelmFaultTolerance = elmMachine.createChild("FaultTolerance");
7220 switch (machineUserData.enmFaultToleranceState)
7221 {
7222 case FaultToleranceState_Inactive:
7223 pelmFaultTolerance->setAttribute("state", "inactive");
7224 break;
7225 case FaultToleranceState_Master:
7226 pelmFaultTolerance->setAttribute("state", "master");
7227 break;
7228 case FaultToleranceState_Standby:
7229 pelmFaultTolerance->setAttribute("state", "standby");
7230 break;
7231#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
7232 case FaultToleranceState_32BitHack: /* (compiler warnings) */
7233 AssertFailedBreak();
7234#endif
7235 }
7236
7237 pelmFaultTolerance->setAttribute("port", machineUserData.uFaultTolerancePort);
7238 pelmFaultTolerance->setAttribute("address", machineUserData.strFaultToleranceAddress);
7239 pelmFaultTolerance->setAttribute("interval", machineUserData.uFaultToleranceInterval);
7240 pelmFaultTolerance->setAttribute("password", machineUserData.strFaultTolerancePassword);
7241 }
7242
7243 if ( (fl & BuildMachineXML_MediaRegistry)
7244 && (m->sv >= SettingsVersion_v1_11)
7245 )
7246 buildMediaRegistry(elmMachine, mediaRegistry);
7247
7248 buildExtraData(elmMachine, mapExtraDataItems);
7249
7250 if ( (fl & BuildMachineXML_IncludeSnapshots)
7251 && llFirstSnapshot.size())
7252 buildSnapshotXML(1, elmMachine, llFirstSnapshot.front());
7253
7254 buildHardwareXML(elmMachine, hardwareMachine, fl, pllElementsWithUuidAttributes);
7255 buildDebuggingXML(&elmMachine, &debugging);
7256 buildAutostartXML(&elmMachine, &autostart);
7257 buildGroupsXML(&elmMachine, &machineUserData.llGroups);
7258}
7259
7260/**
7261 * Returns true only if the given AudioDriverType is supported on
7262 * the current host platform. For example, this would return false
7263 * for AudioDriverType_DirectSound when compiled on a Linux host.
7264 * @param drv AudioDriverType_* enum to test.
7265 * @return true only if the current host supports that driver.
7266 */
7267/*static*/
7268bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
7269{
7270 switch (drv)
7271 {
7272 case AudioDriverType_Null:
7273#ifdef RT_OS_WINDOWS
7274 case AudioDriverType_DirectSound:
7275#endif
7276#ifdef VBOX_WITH_AUDIO_OSS
7277 case AudioDriverType_OSS:
7278#endif
7279#ifdef VBOX_WITH_AUDIO_ALSA
7280 case AudioDriverType_ALSA:
7281#endif
7282#ifdef VBOX_WITH_AUDIO_PULSE
7283 case AudioDriverType_Pulse:
7284#endif
7285#ifdef RT_OS_DARWIN
7286 case AudioDriverType_CoreAudio:
7287#endif
7288#ifdef RT_OS_OS2
7289 case AudioDriverType_MMPM:
7290#endif
7291 return true;
7292 default: break; /* Shut up MSC. */
7293 }
7294
7295 return false;
7296}
7297
7298/**
7299 * Returns the AudioDriverType_* which should be used by default on this
7300 * host platform. On Linux, this will check at runtime whether PulseAudio
7301 * or ALSA are actually supported on the first call.
7302 *
7303 * @return Default audio driver type for this host platform.
7304 */
7305/*static*/
7306AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
7307{
7308#if defined(RT_OS_WINDOWS)
7309 return AudioDriverType_DirectSound;
7310
7311#elif defined(RT_OS_LINUX)
7312 /* On Linux, we need to check at runtime what's actually supported. */
7313 static RTCLockMtx s_mtx;
7314 static AudioDriverType_T s_enmLinuxDriver = AudioDriverType_Null;
7315 RTCLock lock(s_mtx);
7316 if (s_enmLinuxDriver == AudioDriverType_Null)
7317 {
7318# ifdef VBOX_WITH_AUDIO_PULSE
7319 /* Check for the pulse library & that the pulse audio daemon is running. */
7320 if (RTProcIsRunningByName("pulseaudio") &&
7321 RTLdrIsLoadable("libpulse.so.0"))
7322 s_enmLinuxDriver = AudioDriverType_Pulse;
7323 else
7324# endif /* VBOX_WITH_AUDIO_PULSE */
7325# ifdef VBOX_WITH_AUDIO_ALSA
7326 /* Check if we can load the ALSA library */
7327 if (RTLdrIsLoadable("libasound.so.2"))
7328 s_enmLinuxDriver = AudioDriverType_ALSA;
7329 else
7330# endif /* VBOX_WITH_AUDIO_ALSA */
7331 s_enmLinuxDriver = AudioDriverType_OSS;
7332 }
7333 return s_enmLinuxDriver;
7334
7335#elif defined(RT_OS_DARWIN)
7336 return AudioDriverType_CoreAudio;
7337
7338#elif defined(RT_OS_OS2)
7339 return AudioDriverType_MMPM;
7340
7341#else /* All other platforms. */
7342# ifdef VBOX_WITH_AUDIO_OSS
7343 return AudioDriverType_OSS;
7344# else
7345 /* Return NULL driver as a fallback if nothing of the above is available. */
7346 return AudioDriverType_Null;
7347# endif
7348#endif
7349}
7350
7351/**
7352 * Called from write() before calling ConfigFileBase::createStubDocument().
7353 * This adjusts the settings version in m->sv if incompatible settings require
7354 * a settings bump, whereas otherwise we try to preserve the settings version
7355 * to avoid breaking compatibility with older versions.
7356 *
7357 * We do the checks in here in reverse order: newest first, oldest last, so
7358 * that we avoid unnecessary checks since some of these are expensive.
7359 */
7360void MachineConfigFile::bumpSettingsVersionIfNeeded()
7361{
7362 if (m->sv < SettingsVersion_v1_17)
7363 {
7364 if (machineUserData.enmVMPriority != VMProcPriority_Default)
7365 {
7366 m->sv = SettingsVersion_v1_17;
7367 return;
7368 }
7369
7370 // VirtualBox 6.0 adds nested hardware virtualization, using native API (NEM).
7371 if ( hardwareMachine.fNestedHWVirt
7372 || hardwareMachine.fUseNativeApi)
7373 {
7374 m->sv = SettingsVersion_v1_17;
7375 return;
7376 }
7377 if (hardwareMachine.llSharedFolders.size())
7378 for (SharedFoldersList::const_iterator it = hardwareMachine.llSharedFolders.begin();
7379 it != hardwareMachine.llSharedFolders.end();
7380 ++it)
7381 if (it->strAutoMountPoint.isNotEmpty())
7382 {
7383 m->sv = SettingsVersion_v1_17;
7384 return;
7385 }
7386
7387 /*
7388 * Check if any serial port uses a non 16550A serial port.
7389 */
7390 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
7391 it != hardwareMachine.llSerialPorts.end();
7392 ++it)
7393 {
7394 const SerialPort &port = *it;
7395 if (port.uartType != UartType_U16550A)
7396 {
7397 m->sv = SettingsVersion_v1_17;
7398 return;
7399 }
7400 }
7401 }
7402
7403 if (m->sv < SettingsVersion_v1_16)
7404 {
7405 // VirtualBox 5.1 adds a NVMe storage controller, paravirt debug
7406 // options, cpu profile, APIC settings (CPU capability and BIOS).
7407
7408 if ( hardwareMachine.strParavirtDebug.isNotEmpty()
7409 || (!hardwareMachine.strCpuProfile.equals("host") && hardwareMachine.strCpuProfile.isNotEmpty())
7410 || hardwareMachine.biosSettings.apicMode != APICMode_APIC
7411 || !hardwareMachine.fAPIC
7412 || hardwareMachine.fX2APIC
7413 || hardwareMachine.fIBPBOnVMExit
7414 || hardwareMachine.fIBPBOnVMEntry
7415 || hardwareMachine.fSpecCtrl
7416 || hardwareMachine.fSpecCtrlByHost
7417 || !hardwareMachine.fL1DFlushOnSched
7418 || hardwareMachine.fL1DFlushOnVMEntry)
7419 {
7420 m->sv = SettingsVersion_v1_16;
7421 return;
7422 }
7423
7424 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7425 it != hardwareMachine.storage.llStorageControllers.end();
7426 ++it)
7427 {
7428 const StorageController &sctl = *it;
7429
7430 if (sctl.controllerType == StorageControllerType_NVMe)
7431 {
7432 m->sv = SettingsVersion_v1_16;
7433 return;
7434 }
7435 }
7436
7437 for (CpuIdLeafsList::const_iterator it = hardwareMachine.llCpuIdLeafs.begin();
7438 it != hardwareMachine.llCpuIdLeafs.end();
7439 ++it)
7440 if (it->idxSub != 0)
7441 {
7442 m->sv = SettingsVersion_v1_16;
7443 return;
7444 }
7445 }
7446
7447 if (m->sv < SettingsVersion_v1_15)
7448 {
7449 // VirtualBox 5.0 adds paravirt providers, explicit AHCI port hotplug
7450 // setting, USB storage controller, xHCI, serial port TCP backend
7451 // and VM process priority.
7452
7453 /*
7454 * Check simple configuration bits first, loopy stuff afterwards.
7455 */
7456 if ( hardwareMachine.paravirtProvider != ParavirtProvider_Legacy
7457 || hardwareMachine.uCpuIdPortabilityLevel != 0)
7458 {
7459 m->sv = SettingsVersion_v1_15;
7460 return;
7461 }
7462
7463 /*
7464 * Check whether the hotpluggable flag of all storage devices differs
7465 * from the default for old settings.
7466 * AHCI ports are hotpluggable by default every other device is not.
7467 * Also check if there are USB storage controllers.
7468 */
7469 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7470 it != hardwareMachine.storage.llStorageControllers.end();
7471 ++it)
7472 {
7473 const StorageController &sctl = *it;
7474
7475 if (sctl.controllerType == StorageControllerType_USB)
7476 {
7477 m->sv = SettingsVersion_v1_15;
7478 return;
7479 }
7480
7481 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
7482 it2 != sctl.llAttachedDevices.end();
7483 ++it2)
7484 {
7485 const AttachedDevice &att = *it2;
7486
7487 if ( ( att.fHotPluggable
7488 && sctl.controllerType != StorageControllerType_IntelAhci)
7489 || ( !att.fHotPluggable
7490 && sctl.controllerType == StorageControllerType_IntelAhci))
7491 {
7492 m->sv = SettingsVersion_v1_15;
7493 return;
7494 }
7495 }
7496 }
7497
7498 /*
7499 * Check if there is an xHCI (USB3) USB controller.
7500 */
7501 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
7502 it != hardwareMachine.usbSettings.llUSBControllers.end();
7503 ++it)
7504 {
7505 const USBController &ctrl = *it;
7506 if (ctrl.enmType == USBControllerType_XHCI)
7507 {
7508 m->sv = SettingsVersion_v1_15;
7509 return;
7510 }
7511 }
7512
7513 /*
7514 * Check if any serial port uses the TCP backend.
7515 */
7516 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
7517 it != hardwareMachine.llSerialPorts.end();
7518 ++it)
7519 {
7520 const SerialPort &port = *it;
7521 if (port.portMode == PortMode_TCP)
7522 {
7523 m->sv = SettingsVersion_v1_15;
7524 return;
7525 }
7526 }
7527 }
7528
7529 if (m->sv < SettingsVersion_v1_14)
7530 {
7531 // VirtualBox 4.3 adds default frontend setting, graphics controller
7532 // setting, explicit long mode setting, (video) capturing and NAT networking.
7533 if ( !hardwareMachine.strDefaultFrontend.isEmpty()
7534 || hardwareMachine.graphicsControllerType != GraphicsControllerType_VBoxVGA
7535 || hardwareMachine.enmLongMode != Hardware::LongMode_Legacy
7536 || machineUserData.ovIcon.size() > 0
7537 || hardwareMachine.recordingSettings.fEnabled)
7538 {
7539 m->sv = SettingsVersion_v1_14;
7540 return;
7541 }
7542 NetworkAdaptersList::const_iterator netit;
7543 for (netit = hardwareMachine.llNetworkAdapters.begin();
7544 netit != hardwareMachine.llNetworkAdapters.end();
7545 ++netit)
7546 {
7547 if (netit->mode == NetworkAttachmentType_NATNetwork)
7548 {
7549 m->sv = SettingsVersion_v1_14;
7550 break;
7551 }
7552 }
7553 }
7554
7555 if (m->sv < SettingsVersion_v1_14)
7556 {
7557 unsigned cOhciCtrls = 0;
7558 unsigned cEhciCtrls = 0;
7559 bool fNonStdName = false;
7560
7561 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
7562 it != hardwareMachine.usbSettings.llUSBControllers.end();
7563 ++it)
7564 {
7565 const USBController &ctrl = *it;
7566
7567 switch (ctrl.enmType)
7568 {
7569 case USBControllerType_OHCI:
7570 cOhciCtrls++;
7571 if (ctrl.strName != "OHCI")
7572 fNonStdName = true;
7573 break;
7574 case USBControllerType_EHCI:
7575 cEhciCtrls++;
7576 if (ctrl.strName != "EHCI")
7577 fNonStdName = true;
7578 break;
7579 default:
7580 /* Anything unknown forces a bump. */
7581 fNonStdName = true;
7582 }
7583
7584 /* Skip checking other controllers if the settings bump is necessary. */
7585 if (cOhciCtrls > 1 || cEhciCtrls > 1 || fNonStdName)
7586 {
7587 m->sv = SettingsVersion_v1_14;
7588 break;
7589 }
7590 }
7591 }
7592
7593 if (m->sv < SettingsVersion_v1_13)
7594 {
7595 // VirtualBox 4.2 adds tracing, autostart, UUID in directory and groups.
7596 if ( !debugging.areDefaultSettings()
7597 || !autostart.areDefaultSettings()
7598 || machineUserData.fDirectoryIncludesUUID
7599 || machineUserData.llGroups.size() > 1
7600 || machineUserData.llGroups.front() != "/")
7601 m->sv = SettingsVersion_v1_13;
7602 }
7603
7604 if (m->sv < SettingsVersion_v1_13)
7605 {
7606 // VirtualBox 4.2 changes the units for bandwidth group limits.
7607 for (BandwidthGroupList::const_iterator it = hardwareMachine.ioSettings.llBandwidthGroups.begin();
7608 it != hardwareMachine.ioSettings.llBandwidthGroups.end();
7609 ++it)
7610 {
7611 const BandwidthGroup &gr = *it;
7612 if (gr.cMaxBytesPerSec % _1M)
7613 {
7614 // Bump version if a limit cannot be expressed in megabytes
7615 m->sv = SettingsVersion_v1_13;
7616 break;
7617 }
7618 }
7619 }
7620
7621 if (m->sv < SettingsVersion_v1_12)
7622 {
7623 // VirtualBox 4.1 adds PCI passthrough and emulated USB Smart Card reader
7624 if ( hardwareMachine.pciAttachments.size()
7625 || hardwareMachine.fEmulatedUSBCardReader)
7626 m->sv = SettingsVersion_v1_12;
7627 }
7628
7629 if (m->sv < SettingsVersion_v1_12)
7630 {
7631 // VirtualBox 4.1 adds a promiscuous mode policy to the network
7632 // adapters and a generic network driver transport.
7633 NetworkAdaptersList::const_iterator netit;
7634 for (netit = hardwareMachine.llNetworkAdapters.begin();
7635 netit != hardwareMachine.llNetworkAdapters.end();
7636 ++netit)
7637 {
7638 if ( netit->enmPromiscModePolicy != NetworkAdapterPromiscModePolicy_Deny
7639 || netit->mode == NetworkAttachmentType_Generic
7640 || !netit->areGenericDriverDefaultSettings()
7641 )
7642 {
7643 m->sv = SettingsVersion_v1_12;
7644 break;
7645 }
7646 }
7647 }
7648
7649 if (m->sv < SettingsVersion_v1_11)
7650 {
7651 // VirtualBox 4.0 adds HD audio, CPU priorities, fault tolerance,
7652 // per-machine media registries, VRDE, JRockitVE, bandwidth groups,
7653 // ICH9 chipset
7654 if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
7655 || hardwareMachine.ulCpuExecutionCap != 100
7656 || machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
7657 || machineUserData.uFaultTolerancePort
7658 || machineUserData.uFaultToleranceInterval
7659 || !machineUserData.strFaultToleranceAddress.isEmpty()
7660 || mediaRegistry.llHardDisks.size()
7661 || mediaRegistry.llDvdImages.size()
7662 || mediaRegistry.llFloppyImages.size()
7663 || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty()
7664 || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty()
7665 || machineUserData.strOsType == "JRockitVE"
7666 || hardwareMachine.ioSettings.llBandwidthGroups.size()
7667 || hardwareMachine.chipsetType == ChipsetType_ICH9
7668 )
7669 m->sv = SettingsVersion_v1_11;
7670 }
7671
7672 if (m->sv < SettingsVersion_v1_10)
7673 {
7674 /* If the properties contain elements other than "TCP/Ports" and "TCP/Address",
7675 * then increase the version to at least VBox 3.2, which can have video channel properties.
7676 */
7677 unsigned cOldProperties = 0;
7678
7679 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
7680 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7681 cOldProperties++;
7682 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
7683 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7684 cOldProperties++;
7685
7686 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
7687 m->sv = SettingsVersion_v1_10;
7688 }
7689
7690 if (m->sv < SettingsVersion_v1_11)
7691 {
7692 /* If the properties contain elements other than "TCP/Ports", "TCP/Address",
7693 * "VideoChannel/Enabled" and "VideoChannel/Quality" then increase the version to VBox 4.0.
7694 */
7695 unsigned cOldProperties = 0;
7696
7697 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
7698 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7699 cOldProperties++;
7700 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
7701 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7702 cOldProperties++;
7703 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
7704 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7705 cOldProperties++;
7706 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Quality");
7707 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7708 cOldProperties++;
7709
7710 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
7711 m->sv = SettingsVersion_v1_11;
7712 }
7713
7714 // settings version 1.9 is required if there is not exactly one DVD
7715 // or more than one floppy drive present or the DVD is not at the secondary
7716 // master; this check is a bit more complicated
7717 //
7718 // settings version 1.10 is required if the host cache should be disabled
7719 //
7720 // settings version 1.11 is required for bandwidth limits and if more than
7721 // one controller of each type is present.
7722 if (m->sv < SettingsVersion_v1_11)
7723 {
7724 // count attached DVDs and floppies (only if < v1.9)
7725 size_t cDVDs = 0;
7726 size_t cFloppies = 0;
7727
7728 // count storage controllers (if < v1.11)
7729 size_t cSata = 0;
7730 size_t cScsiLsi = 0;
7731 size_t cScsiBuslogic = 0;
7732 size_t cSas = 0;
7733 size_t cIde = 0;
7734 size_t cFloppy = 0;
7735
7736 // need to run thru all the storage controllers and attached devices to figure this out
7737 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7738 it != hardwareMachine.storage.llStorageControllers.end();
7739 ++it)
7740 {
7741 const StorageController &sctl = *it;
7742
7743 // count storage controllers of each type; 1.11 is required if more than one
7744 // controller of one type is present
7745 switch (sctl.storageBus)
7746 {
7747 case StorageBus_IDE:
7748 cIde++;
7749 break;
7750 case StorageBus_SATA:
7751 cSata++;
7752 break;
7753 case StorageBus_SAS:
7754 cSas++;
7755 break;
7756 case StorageBus_SCSI:
7757 if (sctl.controllerType == StorageControllerType_LsiLogic)
7758 cScsiLsi++;
7759 else
7760 cScsiBuslogic++;
7761 break;
7762 case StorageBus_Floppy:
7763 cFloppy++;
7764 break;
7765 default:
7766 // Do nothing
7767 break;
7768 }
7769
7770 if ( cSata > 1
7771 || cScsiLsi > 1
7772 || cScsiBuslogic > 1
7773 || cSas > 1
7774 || cIde > 1
7775 || cFloppy > 1)
7776 {
7777 m->sv = SettingsVersion_v1_11;
7778 break; // abort the loop -- we will not raise the version further
7779 }
7780
7781 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
7782 it2 != sctl.llAttachedDevices.end();
7783 ++it2)
7784 {
7785 const AttachedDevice &att = *it2;
7786
7787 // Bandwidth limitations are new in VirtualBox 4.0 (1.11)
7788 if (m->sv < SettingsVersion_v1_11)
7789 {
7790 if (att.strBwGroup.length() != 0)
7791 {
7792 m->sv = SettingsVersion_v1_11;
7793 break; // abort the loop -- we will not raise the version further
7794 }
7795 }
7796
7797 // disabling the host IO cache requires settings version 1.10
7798 if ( (m->sv < SettingsVersion_v1_10)
7799 && (!sctl.fUseHostIOCache)
7800 )
7801 m->sv = SettingsVersion_v1_10;
7802
7803 // we can only write the StorageController/@Instance attribute with v1.9
7804 if ( (m->sv < SettingsVersion_v1_9)
7805 && (sctl.ulInstance != 0)
7806 )
7807 m->sv = SettingsVersion_v1_9;
7808
7809 if (m->sv < SettingsVersion_v1_9)
7810 {
7811 if (att.deviceType == DeviceType_DVD)
7812 {
7813 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
7814 || (att.lPort != 1) // DVDs not at secondary master?
7815 || (att.lDevice != 0)
7816 )
7817 m->sv = SettingsVersion_v1_9;
7818
7819 ++cDVDs;
7820 }
7821 else if (att.deviceType == DeviceType_Floppy)
7822 ++cFloppies;
7823 }
7824 }
7825
7826 if (m->sv >= SettingsVersion_v1_11)
7827 break; // abort the loop -- we will not raise the version further
7828 }
7829
7830 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
7831 // so any deviation from that will require settings version 1.9
7832 if ( (m->sv < SettingsVersion_v1_9)
7833 && ( (cDVDs != 1)
7834 || (cFloppies > 1)
7835 )
7836 )
7837 m->sv = SettingsVersion_v1_9;
7838 }
7839
7840 // VirtualBox 3.2: Check for non default I/O settings
7841 if (m->sv < SettingsVersion_v1_10)
7842 {
7843 if ( (hardwareMachine.ioSettings.fIOCacheEnabled != true)
7844 || (hardwareMachine.ioSettings.ulIOCacheSize != 5)
7845 // and page fusion
7846 || (hardwareMachine.fPageFusionEnabled)
7847 // and CPU hotplug, RTC timezone control, HID type and HPET
7848 || machineUserData.fRTCUseUTC
7849 || hardwareMachine.fCpuHotPlug
7850 || hardwareMachine.pointingHIDType != PointingHIDType_PS2Mouse
7851 || hardwareMachine.keyboardHIDType != KeyboardHIDType_PS2Keyboard
7852 || hardwareMachine.fHPETEnabled
7853 )
7854 m->sv = SettingsVersion_v1_10;
7855 }
7856
7857 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main
7858 // VirtualBox 4.0 adds network bandwitdth
7859 if (m->sv < SettingsVersion_v1_11)
7860 {
7861 NetworkAdaptersList::const_iterator netit;
7862 for (netit = hardwareMachine.llNetworkAdapters.begin();
7863 netit != hardwareMachine.llNetworkAdapters.end();
7864 ++netit)
7865 {
7866 if ( (m->sv < SettingsVersion_v1_12)
7867 && (netit->strBandwidthGroup.isNotEmpty())
7868 )
7869 {
7870 /* New in VirtualBox 4.1 */
7871 m->sv = SettingsVersion_v1_12;
7872 break;
7873 }
7874 else if ( (m->sv < SettingsVersion_v1_10)
7875 && (netit->fEnabled)
7876 && (netit->mode == NetworkAttachmentType_NAT)
7877 && ( netit->nat.u32Mtu != 0
7878 || netit->nat.u32SockRcv != 0
7879 || netit->nat.u32SockSnd != 0
7880 || netit->nat.u32TcpRcv != 0
7881 || netit->nat.u32TcpSnd != 0
7882 || !netit->nat.fDNSPassDomain
7883 || netit->nat.fDNSProxy
7884 || netit->nat.fDNSUseHostResolver
7885 || netit->nat.fAliasLog
7886 || netit->nat.fAliasProxyOnly
7887 || netit->nat.fAliasUseSamePorts
7888 || netit->nat.strTFTPPrefix.length()
7889 || netit->nat.strTFTPBootFile.length()
7890 || netit->nat.strTFTPNextServer.length()
7891 || netit->nat.mapRules.size()
7892 )
7893 )
7894 {
7895 m->sv = SettingsVersion_v1_10;
7896 // no break because we still might need v1.11 above
7897 }
7898 else if ( (m->sv < SettingsVersion_v1_10)
7899 && (netit->fEnabled)
7900 && (netit->ulBootPriority != 0)
7901 )
7902 {
7903 m->sv = SettingsVersion_v1_10;
7904 // no break because we still might need v1.11 above
7905 }
7906 }
7907 }
7908
7909 // all the following require settings version 1.9
7910 if ( (m->sv < SettingsVersion_v1_9)
7911 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
7912 || machineUserData.fTeleporterEnabled
7913 || machineUserData.uTeleporterPort
7914 || !machineUserData.strTeleporterAddress.isEmpty()
7915 || !machineUserData.strTeleporterPassword.isEmpty()
7916 || (!hardwareMachine.uuid.isZero() && hardwareMachine.uuid.isValid())
7917 )
7918 )
7919 m->sv = SettingsVersion_v1_9;
7920
7921 // "accelerate 2d video" requires settings version 1.8
7922 if ( (m->sv < SettingsVersion_v1_8)
7923 && (hardwareMachine.fAccelerate2DVideo)
7924 )
7925 m->sv = SettingsVersion_v1_8;
7926
7927 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
7928 if ( m->sv < SettingsVersion_v1_4
7929 && hardwareMachine.strVersion != "1"
7930 )
7931 m->sv = SettingsVersion_v1_4;
7932}
7933
7934/**
7935 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
7936 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
7937 * in particular if the file cannot be written.
7938 */
7939void MachineConfigFile::write(const com::Utf8Str &strFilename)
7940{
7941 try
7942 {
7943 // createStubDocument() sets the settings version to at least 1.7; however,
7944 // we might need to enfore a later settings version if incompatible settings
7945 // are present:
7946 bumpSettingsVersionIfNeeded();
7947
7948 m->strFilename = strFilename;
7949 specialBackupIfFirstBump();
7950 createStubDocument();
7951
7952 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
7953 buildMachineXML(*pelmMachine,
7954 MachineConfigFile::BuildMachineXML_IncludeSnapshots
7955 | MachineConfigFile::BuildMachineXML_MediaRegistry,
7956 // but not BuildMachineXML_WriteVBoxVersionAttribute
7957 NULL); /* pllElementsWithUuidAttributes */
7958
7959 // now go write the XML
7960 xml::XmlFileWriter writer(*m->pDoc);
7961 writer.write(m->strFilename.c_str(), true /*fSafe*/);
7962
7963 m->fFileExists = true;
7964 clearDocument();
7965 }
7966 catch (...)
7967 {
7968 clearDocument();
7969 throw;
7970 }
7971}
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