VirtualBox

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

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

Forward ported 130474,130475,130477,130479. bugref:9453

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