VirtualBox

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

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

Recording/Main: Docs.

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