VirtualBox

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

Last change on this file since 74900 was 74474, checked in by vboxsync, 6 years ago

Main,Devices: Graphics controller updates. bugref:8893

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 307.6 KB
Line 
1/* $Id: Settings.cpp 74474 2018-09-26 11:55:47Z 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
2373/**
2374 * Constructor. Needs to set sane defaults which stand the test of time.
2375 */
2376USBController::USBController() :
2377 enmType(USBControllerType_Null)
2378{
2379}
2380
2381/**
2382 * Comparison operator. This gets called from MachineConfigFile::operator==,
2383 * which in turn gets called from Machine::saveSettings to figure out whether
2384 * machine settings have really changed and thus need to be written out to disk.
2385 */
2386bool USBController::operator==(const USBController &u) const
2387{
2388 return (this == &u)
2389 || ( strName == u.strName
2390 && enmType == u.enmType);
2391}
2392
2393/**
2394 * Constructor. Needs to set sane defaults which stand the test of time.
2395 */
2396USB::USB()
2397{
2398}
2399
2400/**
2401 * Comparison operator. This gets called from MachineConfigFile::operator==,
2402 * which in turn gets called from Machine::saveSettings to figure out whether
2403 * machine settings have really changed and thus need to be written out to disk.
2404 */
2405bool USB::operator==(const USB &u) const
2406{
2407 return (this == &u)
2408 || ( llUSBControllers == u.llUSBControllers
2409 && llDeviceFilters == u.llDeviceFilters);
2410}
2411
2412/**
2413 * Constructor. Needs to set sane defaults which stand the test of time.
2414 */
2415NAT::NAT() :
2416 u32Mtu(0),
2417 u32SockRcv(0),
2418 u32SockSnd(0),
2419 u32TcpRcv(0),
2420 u32TcpSnd(0),
2421 fDNSPassDomain(true), /* historically this value is true */
2422 fDNSProxy(false),
2423 fDNSUseHostResolver(false),
2424 fAliasLog(false),
2425 fAliasProxyOnly(false),
2426 fAliasUseSamePorts(false)
2427{
2428}
2429
2430/**
2431 * Check if all DNS settings have default values.
2432 */
2433bool NAT::areDNSDefaultSettings() const
2434{
2435 return fDNSPassDomain && !fDNSProxy && !fDNSUseHostResolver;
2436}
2437
2438/**
2439 * Check if all Alias settings have default values.
2440 */
2441bool NAT::areAliasDefaultSettings() const
2442{
2443 return !fAliasLog && !fAliasProxyOnly && !fAliasUseSamePorts;
2444}
2445
2446/**
2447 * Check if all TFTP settings have default values.
2448 */
2449bool NAT::areTFTPDefaultSettings() const
2450{
2451 return strTFTPPrefix.isEmpty()
2452 && strTFTPBootFile.isEmpty()
2453 && strTFTPNextServer.isEmpty();
2454}
2455
2456/**
2457 * Check if all settings have default values.
2458 */
2459bool NAT::areDefaultSettings() const
2460{
2461 return strNetwork.isEmpty()
2462 && strBindIP.isEmpty()
2463 && u32Mtu == 0
2464 && u32SockRcv == 0
2465 && u32SockSnd == 0
2466 && u32TcpRcv == 0
2467 && u32TcpSnd == 0
2468 && areDNSDefaultSettings()
2469 && areAliasDefaultSettings()
2470 && areTFTPDefaultSettings()
2471 && mapRules.size() == 0;
2472}
2473
2474/**
2475 * Comparison operator. This gets called from MachineConfigFile::operator==,
2476 * which in turn gets called from Machine::saveSettings to figure out whether
2477 * machine settings have really changed and thus need to be written out to disk.
2478 */
2479bool NAT::operator==(const NAT &n) const
2480{
2481 return (this == &n)
2482 || ( strNetwork == n.strNetwork
2483 && strBindIP == n.strBindIP
2484 && u32Mtu == n.u32Mtu
2485 && u32SockRcv == n.u32SockRcv
2486 && u32SockSnd == n.u32SockSnd
2487 && u32TcpSnd == n.u32TcpSnd
2488 && u32TcpRcv == n.u32TcpRcv
2489 && strTFTPPrefix == n.strTFTPPrefix
2490 && strTFTPBootFile == n.strTFTPBootFile
2491 && strTFTPNextServer == n.strTFTPNextServer
2492 && fDNSPassDomain == n.fDNSPassDomain
2493 && fDNSProxy == n.fDNSProxy
2494 && fDNSUseHostResolver == n.fDNSUseHostResolver
2495 && fAliasLog == n.fAliasLog
2496 && fAliasProxyOnly == n.fAliasProxyOnly
2497 && fAliasUseSamePorts == n.fAliasUseSamePorts
2498 && mapRules == n.mapRules);
2499}
2500
2501/**
2502 * Constructor. Needs to set sane defaults which stand the test of time.
2503 */
2504NetworkAdapter::NetworkAdapter() :
2505 ulSlot(0),
2506 type(NetworkAdapterType_Am79C970A), // default for old VMs, for new ones it's Am79C973
2507 fEnabled(false),
2508 fCableConnected(false), // default for old VMs, for new ones it's true
2509 ulLineSpeed(0),
2510 enmPromiscModePolicy(NetworkAdapterPromiscModePolicy_Deny),
2511 fTraceEnabled(false),
2512 mode(NetworkAttachmentType_Null),
2513 ulBootPriority(0)
2514{
2515}
2516
2517/**
2518 * Check if all Generic Driver settings have default values.
2519 */
2520bool NetworkAdapter::areGenericDriverDefaultSettings() const
2521{
2522 return strGenericDriver.isEmpty()
2523 && genericProperties.size() == 0;
2524}
2525
2526/**
2527 * Check if all settings have default values.
2528 */
2529bool NetworkAdapter::areDefaultSettings(SettingsVersion_T sv) const
2530{
2531 // 5.0 and earlier had a default of fCableConnected=false, which doesn't
2532 // make a lot of sense (but it's a fact). Later versions don't save the
2533 // setting if it's at the default value and thus must get it right.
2534 return !fEnabled
2535 && strMACAddress.isEmpty()
2536 && ( (sv >= SettingsVersion_v1_16 && fCableConnected && type == NetworkAdapterType_Am79C973)
2537 || (sv < SettingsVersion_v1_16 && !fCableConnected && type == NetworkAdapterType_Am79C970A))
2538 && ulLineSpeed == 0
2539 && enmPromiscModePolicy == NetworkAdapterPromiscModePolicy_Deny
2540 && mode == NetworkAttachmentType_Null
2541 && nat.areDefaultSettings()
2542 && strBridgedName.isEmpty()
2543 && strInternalNetworkName.isEmpty()
2544 && strHostOnlyName.isEmpty()
2545 && areGenericDriverDefaultSettings()
2546 && strNATNetworkName.isEmpty();
2547}
2548
2549/**
2550 * Special check if settings of the non-current attachment type have default values.
2551 */
2552bool NetworkAdapter::areDisabledDefaultSettings() const
2553{
2554 return (mode != NetworkAttachmentType_NAT ? nat.areDefaultSettings() : true)
2555 && (mode != NetworkAttachmentType_Bridged ? strBridgedName.isEmpty() : true)
2556 && (mode != NetworkAttachmentType_Internal ? strInternalNetworkName.isEmpty() : true)
2557 && (mode != NetworkAttachmentType_HostOnly ? strHostOnlyName.isEmpty() : true)
2558 && (mode != NetworkAttachmentType_Generic ? areGenericDriverDefaultSettings() : true)
2559 && (mode != NetworkAttachmentType_NATNetwork ? strNATNetworkName.isEmpty() : true);
2560}
2561
2562/**
2563 * Comparison operator. This gets called from MachineConfigFile::operator==,
2564 * which in turn gets called from Machine::saveSettings to figure out whether
2565 * machine settings have really changed and thus need to be written out to disk.
2566 */
2567bool NetworkAdapter::operator==(const NetworkAdapter &n) const
2568{
2569 return (this == &n)
2570 || ( ulSlot == n.ulSlot
2571 && type == n.type
2572 && fEnabled == n.fEnabled
2573 && strMACAddress == n.strMACAddress
2574 && fCableConnected == n.fCableConnected
2575 && ulLineSpeed == n.ulLineSpeed
2576 && enmPromiscModePolicy == n.enmPromiscModePolicy
2577 && fTraceEnabled == n.fTraceEnabled
2578 && strTraceFile == n.strTraceFile
2579 && mode == n.mode
2580 && nat == n.nat
2581 && strBridgedName == n.strBridgedName
2582 && strHostOnlyName == n.strHostOnlyName
2583 && strInternalNetworkName == n.strInternalNetworkName
2584 && strGenericDriver == n.strGenericDriver
2585 && genericProperties == n.genericProperties
2586 && ulBootPriority == n.ulBootPriority
2587 && strBandwidthGroup == n.strBandwidthGroup);
2588}
2589
2590/**
2591 * Constructor. Needs to set sane defaults which stand the test of time.
2592 */
2593SerialPort::SerialPort() :
2594 ulSlot(0),
2595 fEnabled(false),
2596 ulIOBase(0x3f8),
2597 ulIRQ(4),
2598 portMode(PortMode_Disconnected),
2599 fServer(false),
2600 uartType(UartType_U16550A)
2601{
2602}
2603
2604/**
2605 * Comparison operator. This gets called from MachineConfigFile::operator==,
2606 * which in turn gets called from Machine::saveSettings to figure out whether
2607 * machine settings have really changed and thus need to be written out to disk.
2608 */
2609bool SerialPort::operator==(const SerialPort &s) const
2610{
2611 return (this == &s)
2612 || ( ulSlot == s.ulSlot
2613 && fEnabled == s.fEnabled
2614 && ulIOBase == s.ulIOBase
2615 && ulIRQ == s.ulIRQ
2616 && portMode == s.portMode
2617 && strPath == s.strPath
2618 && fServer == s.fServer
2619 && uartType == s.uartType);
2620}
2621
2622/**
2623 * Constructor. Needs to set sane defaults which stand the test of time.
2624 */
2625ParallelPort::ParallelPort() :
2626 ulSlot(0),
2627 fEnabled(false),
2628 ulIOBase(0x378),
2629 ulIRQ(7)
2630{
2631}
2632
2633/**
2634 * Comparison operator. This gets called from MachineConfigFile::operator==,
2635 * which in turn gets called from Machine::saveSettings to figure out whether
2636 * machine settings have really changed and thus need to be written out to disk.
2637 */
2638bool ParallelPort::operator==(const ParallelPort &s) const
2639{
2640 return (this == &s)
2641 || ( ulSlot == s.ulSlot
2642 && fEnabled == s.fEnabled
2643 && ulIOBase == s.ulIOBase
2644 && ulIRQ == s.ulIRQ
2645 && strPath == s.strPath);
2646}
2647
2648/**
2649 * Constructor. Needs to set sane defaults which stand the test of time.
2650 */
2651AudioAdapter::AudioAdapter() :
2652 fEnabled(true), // default for old VMs, for new ones it's false
2653 fEnabledIn(true), // default for old VMs, for new ones it's false
2654 fEnabledOut(true), // default for old VMs, for new ones it's false
2655 controllerType(AudioControllerType_AC97),
2656 codecType(AudioCodecType_STAC9700),
2657 driverType(AudioDriverType_Null)
2658{
2659}
2660
2661/**
2662 * Check if all settings have default values.
2663 */
2664bool AudioAdapter::areDefaultSettings(SettingsVersion_T sv) const
2665{
2666 return (sv < SettingsVersion_v1_16 ? false : !fEnabled)
2667 && (sv <= SettingsVersion_v1_16 ? fEnabledIn : !fEnabledIn)
2668 && (sv <= SettingsVersion_v1_16 ? fEnabledOut : !fEnabledOut)
2669 && fEnabledOut == true
2670 && controllerType == AudioControllerType_AC97
2671 && codecType == AudioCodecType_STAC9700
2672 && properties.size() == 0;
2673}
2674
2675/**
2676 * Comparison operator. This gets called from MachineConfigFile::operator==,
2677 * which in turn gets called from Machine::saveSettings to figure out whether
2678 * machine settings have really changed and thus need to be written out to disk.
2679 */
2680bool AudioAdapter::operator==(const AudioAdapter &a) const
2681{
2682 return (this == &a)
2683 || ( fEnabled == a.fEnabled
2684 && fEnabledIn == a.fEnabledIn
2685 && fEnabledOut == a.fEnabledOut
2686 && controllerType == a.controllerType
2687 && codecType == a.codecType
2688 && driverType == a.driverType
2689 && properties == a.properties);
2690}
2691
2692/**
2693 * Constructor. Needs to set sane defaults which stand the test of time.
2694 */
2695SharedFolder::SharedFolder() :
2696 fWritable(false),
2697 fAutoMount(false)
2698{
2699}
2700
2701/**
2702 * Comparison operator. This gets called from MachineConfigFile::operator==,
2703 * which in turn gets called from Machine::saveSettings to figure out whether
2704 * machine settings have really changed and thus need to be written out to disk.
2705 */
2706bool SharedFolder::operator==(const SharedFolder &g) const
2707{
2708 return (this == &g)
2709 || ( strName == g.strName
2710 && strHostPath == g.strHostPath
2711 && fWritable == g.fWritable
2712 && fAutoMount == g.fAutoMount);
2713}
2714
2715/**
2716 * Constructor. Needs to set sane defaults which stand the test of time.
2717 */
2718GuestProperty::GuestProperty() :
2719 timestamp(0)
2720{
2721}
2722
2723/**
2724 * Comparison operator. This gets called from MachineConfigFile::operator==,
2725 * which in turn gets called from Machine::saveSettings to figure out whether
2726 * machine settings have really changed and thus need to be written out to disk.
2727 */
2728bool GuestProperty::operator==(const GuestProperty &g) const
2729{
2730 return (this == &g)
2731 || ( strName == g.strName
2732 && strValue == g.strValue
2733 && timestamp == g.timestamp
2734 && strFlags == g.strFlags);
2735}
2736
2737/**
2738 * Constructor. Needs to set sane defaults which stand the test of time.
2739 */
2740CpuIdLeaf::CpuIdLeaf() :
2741 idx(UINT32_MAX),
2742 idxSub(0),
2743 uEax(0),
2744 uEbx(0),
2745 uEcx(0),
2746 uEdx(0)
2747{
2748}
2749
2750/**
2751 * Comparison operator. This gets called from MachineConfigFile::operator==,
2752 * which in turn gets called from Machine::saveSettings to figure out whether
2753 * machine settings have really changed and thus need to be written out to disk.
2754 */
2755bool CpuIdLeaf::operator==(const CpuIdLeaf &c) const
2756{
2757 return (this == &c)
2758 || ( idx == c.idx
2759 && idxSub == c.idxSub
2760 && uEax == c.uEax
2761 && uEbx == c.uEbx
2762 && uEcx == c.uEcx
2763 && uEdx == c.uEdx);
2764}
2765
2766/**
2767 * Constructor. Needs to set sane defaults which stand the test of time.
2768 */
2769Cpu::Cpu() :
2770 ulId(UINT32_MAX)
2771{
2772}
2773
2774/**
2775 * Comparison operator. This gets called from MachineConfigFile::operator==,
2776 * which in turn gets called from Machine::saveSettings to figure out whether
2777 * machine settings have really changed and thus need to be written out to disk.
2778 */
2779bool Cpu::operator==(const Cpu &c) const
2780{
2781 return (this == &c)
2782 || (ulId == c.ulId);
2783}
2784
2785/**
2786 * Constructor. Needs to set sane defaults which stand the test of time.
2787 */
2788BandwidthGroup::BandwidthGroup() :
2789 cMaxBytesPerSec(0),
2790 enmType(BandwidthGroupType_Null)
2791{
2792}
2793
2794/**
2795 * Comparison operator. This gets called from MachineConfigFile::operator==,
2796 * which in turn gets called from Machine::saveSettings to figure out whether
2797 * machine settings have really changed and thus need to be written out to disk.
2798 */
2799bool BandwidthGroup::operator==(const BandwidthGroup &i) const
2800{
2801 return (this == &i)
2802 || ( strName == i.strName
2803 && cMaxBytesPerSec == i.cMaxBytesPerSec
2804 && enmType == i.enmType);
2805}
2806
2807/**
2808 * IOSettings constructor.
2809 */
2810IOSettings::IOSettings() :
2811 fIOCacheEnabled(true),
2812 ulIOCacheSize(5)
2813{
2814}
2815
2816/**
2817 * Check if all IO Cache settings have default values.
2818 */
2819bool IOSettings::areIOCacheDefaultSettings() const
2820{
2821 return fIOCacheEnabled
2822 && ulIOCacheSize == 5;
2823}
2824
2825/**
2826 * Check if all settings have default values.
2827 */
2828bool IOSettings::areDefaultSettings() const
2829{
2830 return areIOCacheDefaultSettings()
2831 && llBandwidthGroups.size() == 0;
2832}
2833
2834/**
2835 * Comparison operator. This gets called from MachineConfigFile::operator==,
2836 * which in turn gets called from Machine::saveSettings to figure out whether
2837 * machine settings have really changed and thus need to be written out to disk.
2838 */
2839bool IOSettings::operator==(const IOSettings &i) const
2840{
2841 return (this == &i)
2842 || ( fIOCacheEnabled == i.fIOCacheEnabled
2843 && ulIOCacheSize == i.ulIOCacheSize
2844 && llBandwidthGroups == i.llBandwidthGroups);
2845}
2846
2847/**
2848 * Constructor. Needs to set sane defaults which stand the test of time.
2849 */
2850HostPCIDeviceAttachment::HostPCIDeviceAttachment() :
2851 uHostAddress(0),
2852 uGuestAddress(0)
2853{
2854}
2855
2856/**
2857 * Comparison operator. This gets called from MachineConfigFile::operator==,
2858 * which in turn gets called from Machine::saveSettings to figure out whether
2859 * machine settings have really changed and thus need to be written out to disk.
2860 */
2861bool HostPCIDeviceAttachment::operator==(const HostPCIDeviceAttachment &a) const
2862{
2863 return (this == &a)
2864 || ( uHostAddress == a.uHostAddress
2865 && uGuestAddress == a.uGuestAddress
2866 && strDeviceName == a.strDeviceName);
2867}
2868
2869
2870/**
2871 * Constructor. Needs to set sane defaults which stand the test of time.
2872 */
2873Hardware::Hardware() :
2874 strVersion("1"),
2875 fHardwareVirt(true),
2876 fNestedPaging(true),
2877 fVPID(true),
2878 fUnrestrictedExecution(true),
2879 fHardwareVirtForce(false),
2880 fUseNativeApi(false),
2881 fTripleFaultReset(false),
2882 fPAE(false),
2883 fAPIC(true),
2884 fX2APIC(false),
2885 fIBPBOnVMExit(false),
2886 fIBPBOnVMEntry(false),
2887 fSpecCtrl(false),
2888 fSpecCtrlByHost(false),
2889 fNestedHWVirt(false),
2890 enmLongMode(HC_ARCH_BITS == 64 ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled),
2891 cCPUs(1),
2892 fCpuHotPlug(false),
2893 fHPETEnabled(false),
2894 ulCpuExecutionCap(100),
2895 uCpuIdPortabilityLevel(0),
2896 strCpuProfile("host"),
2897 ulMemorySizeMB((uint32_t)-1),
2898 graphicsControllerType(GraphicsControllerType_VBoxVGA),
2899 ulVRAMSizeMB(8),
2900 cMonitors(1),
2901 fAccelerate3D(false),
2902 fAccelerate2DVideo(false),
2903 ulVideoCaptureHorzRes(1024),
2904 ulVideoCaptureVertRes(768),
2905 ulVideoCaptureRate(512),
2906 ulVideoCaptureFPS(25),
2907 ulVideoCaptureMaxTime(0),
2908 ulVideoCaptureMaxSize(0),
2909 fVideoCaptureEnabled(false),
2910 u64VideoCaptureScreens(UINT64_C(0xffffffffffffffff)),
2911 strVideoCaptureFile(""),
2912 firmwareType(FirmwareType_BIOS),
2913 pointingHIDType(PointingHIDType_PS2Mouse),
2914 keyboardHIDType(KeyboardHIDType_PS2Keyboard),
2915 chipsetType(ChipsetType_PIIX3),
2916 paravirtProvider(ParavirtProvider_Legacy), // default for old VMs, for new ones it's ParavirtProvider_Default
2917 strParavirtDebug(""),
2918 fEmulatedUSBCardReader(false),
2919 clipboardMode(ClipboardMode_Disabled),
2920 dndMode(DnDMode_Disabled),
2921 ulMemoryBalloonSize(0),
2922 fPageFusionEnabled(false)
2923{
2924 mapBootOrder[0] = DeviceType_Floppy;
2925 mapBootOrder[1] = DeviceType_DVD;
2926 mapBootOrder[2] = DeviceType_HardDisk;
2927
2928 /* The default value for PAE depends on the host:
2929 * - 64 bits host -> always true
2930 * - 32 bits host -> true for Windows & Darwin (masked off if the host cpu doesn't support it anyway)
2931 */
2932#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
2933 fPAE = true;
2934#endif
2935
2936 /* The default value of large page supports depends on the host:
2937 * - 64 bits host -> true, unless it's Linux (pending further prediction work due to excessively expensive large page allocations)
2938 * - 32 bits host -> false
2939 */
2940#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
2941 fLargePages = true;
2942#else
2943 /* Not supported on 32 bits hosts. */
2944 fLargePages = false;
2945#endif
2946}
2947
2948/**
2949 * Check if all Paravirt settings have default values.
2950 */
2951bool Hardware::areParavirtDefaultSettings(SettingsVersion_T sv) const
2952{
2953 // 5.0 didn't save the paravirt settings if it is ParavirtProvider_Legacy,
2954 // so this default must be kept. Later versions don't save the setting if
2955 // it's at the default value.
2956 return ( (sv >= SettingsVersion_v1_16 && paravirtProvider == ParavirtProvider_Default)
2957 || (sv < SettingsVersion_v1_16 && paravirtProvider == ParavirtProvider_Legacy))
2958 && strParavirtDebug.isEmpty();
2959}
2960
2961/**
2962 * Check if all Boot Order settings have default values.
2963 */
2964bool Hardware::areBootOrderDefaultSettings() const
2965{
2966 BootOrderMap::const_iterator it0 = mapBootOrder.find(0);
2967 BootOrderMap::const_iterator it1 = mapBootOrder.find(1);
2968 BootOrderMap::const_iterator it2 = mapBootOrder.find(2);
2969 BootOrderMap::const_iterator it3 = mapBootOrder.find(3);
2970 return ( mapBootOrder.size() == 3
2971 || ( mapBootOrder.size() == 4
2972 && (it3 != mapBootOrder.end() && it3->second == DeviceType_Null)))
2973 && (it0 != mapBootOrder.end() && it0->second == DeviceType_Floppy)
2974 && (it1 != mapBootOrder.end() && it1->second == DeviceType_DVD)
2975 && (it2 != mapBootOrder.end() && it2->second == DeviceType_HardDisk);
2976}
2977
2978/**
2979 * Check if all Display settings have default values.
2980 */
2981bool Hardware::areDisplayDefaultSettings() const
2982{
2983 return graphicsControllerType == GraphicsControllerType_VBoxVGA
2984 && ulVRAMSizeMB == 8
2985 && cMonitors <= 1
2986 && !fAccelerate3D
2987 && !fAccelerate2DVideo;
2988}
2989
2990/**
2991 * Check if all Video Capture settings have default values.
2992 */
2993bool Hardware::areVideoCaptureDefaultSettings() const
2994{
2995 return !fVideoCaptureEnabled
2996 && u64VideoCaptureScreens == UINT64_C(0xffffffffffffffff)
2997 && strVideoCaptureFile.isEmpty()
2998 && ulVideoCaptureHorzRes == 1024
2999 && ulVideoCaptureVertRes == 768
3000 && ulVideoCaptureRate == 512
3001 && ulVideoCaptureFPS == 25
3002 && ulVideoCaptureMaxTime == 0
3003 && ulVideoCaptureMaxSize == 0
3004 && strVideoCaptureOptions.isEmpty();
3005}
3006
3007/**
3008 * Check if all Network Adapter settings have default values.
3009 */
3010bool Hardware::areAllNetworkAdaptersDefaultSettings(SettingsVersion_T sv) const
3011{
3012 for (NetworkAdaptersList::const_iterator it = llNetworkAdapters.begin();
3013 it != llNetworkAdapters.end();
3014 ++it)
3015 {
3016 if (!it->areDefaultSettings(sv))
3017 return false;
3018 }
3019 return true;
3020}
3021
3022/**
3023 * Comparison operator. This gets called from MachineConfigFile::operator==,
3024 * which in turn gets called from Machine::saveSettings to figure out whether
3025 * machine settings have really changed and thus need to be written out to disk.
3026 */
3027bool Hardware::operator==(const Hardware& h) const
3028{
3029 return (this == &h)
3030 || ( strVersion == h.strVersion
3031 && uuid == h.uuid
3032 && fHardwareVirt == h.fHardwareVirt
3033 && fNestedPaging == h.fNestedPaging
3034 && fLargePages == h.fLargePages
3035 && fVPID == h.fVPID
3036 && fUnrestrictedExecution == h.fUnrestrictedExecution
3037 && fHardwareVirtForce == h.fHardwareVirtForce
3038 && fUseNativeApi == h.fUseNativeApi
3039 && fPAE == h.fPAE
3040 && enmLongMode == h.enmLongMode
3041 && fTripleFaultReset == h.fTripleFaultReset
3042 && fAPIC == h.fAPIC
3043 && fX2APIC == h.fX2APIC
3044 && fIBPBOnVMExit == h.fIBPBOnVMExit
3045 && fIBPBOnVMEntry == h.fIBPBOnVMEntry
3046 && fSpecCtrl == h.fSpecCtrl
3047 && fSpecCtrlByHost == h.fSpecCtrlByHost
3048 && fNestedHWVirt == h.fNestedHWVirt
3049 && cCPUs == h.cCPUs
3050 && fCpuHotPlug == h.fCpuHotPlug
3051 && ulCpuExecutionCap == h.ulCpuExecutionCap
3052 && uCpuIdPortabilityLevel == h.uCpuIdPortabilityLevel
3053 && strCpuProfile == h.strCpuProfile
3054 && fHPETEnabled == h.fHPETEnabled
3055 && llCpus == h.llCpus
3056 && llCpuIdLeafs == h.llCpuIdLeafs
3057 && ulMemorySizeMB == h.ulMemorySizeMB
3058 && mapBootOrder == h.mapBootOrder
3059 && graphicsControllerType == h.graphicsControllerType
3060 && ulVRAMSizeMB == h.ulVRAMSizeMB
3061 && cMonitors == h.cMonitors
3062 && fAccelerate3D == h.fAccelerate3D
3063 && fAccelerate2DVideo == h.fAccelerate2DVideo
3064 && fVideoCaptureEnabled == h.fVideoCaptureEnabled
3065 && u64VideoCaptureScreens == h.u64VideoCaptureScreens
3066 && strVideoCaptureFile == h.strVideoCaptureFile
3067 && ulVideoCaptureHorzRes == h.ulVideoCaptureHorzRes
3068 && ulVideoCaptureVertRes == h.ulVideoCaptureVertRes
3069 && ulVideoCaptureRate == h.ulVideoCaptureRate
3070 && ulVideoCaptureFPS == h.ulVideoCaptureFPS
3071 && ulVideoCaptureMaxTime == h.ulVideoCaptureMaxTime
3072 && ulVideoCaptureMaxSize == h.ulVideoCaptureMaxTime
3073 && strVideoCaptureOptions == h.strVideoCaptureOptions
3074 && firmwareType == h.firmwareType
3075 && pointingHIDType == h.pointingHIDType
3076 && keyboardHIDType == h.keyboardHIDType
3077 && chipsetType == h.chipsetType
3078 && paravirtProvider == h.paravirtProvider
3079 && strParavirtDebug == h.strParavirtDebug
3080 && fEmulatedUSBCardReader == h.fEmulatedUSBCardReader
3081 && vrdeSettings == h.vrdeSettings
3082 && biosSettings == h.biosSettings
3083 && usbSettings == h.usbSettings
3084 && llNetworkAdapters == h.llNetworkAdapters
3085 && llSerialPorts == h.llSerialPorts
3086 && llParallelPorts == h.llParallelPorts
3087 && audioAdapter == h.audioAdapter
3088 && storage == h.storage
3089 && llSharedFolders == h.llSharedFolders
3090 && clipboardMode == h.clipboardMode
3091 && dndMode == h.dndMode
3092 && ulMemoryBalloonSize == h.ulMemoryBalloonSize
3093 && fPageFusionEnabled == h.fPageFusionEnabled
3094 && llGuestProperties == h.llGuestProperties
3095 && ioSettings == h.ioSettings
3096 && pciAttachments == h.pciAttachments
3097 && strDefaultFrontend == h.strDefaultFrontend);
3098}
3099
3100/**
3101 * Constructor. Needs to set sane defaults which stand the test of time.
3102 */
3103AttachedDevice::AttachedDevice() :
3104 deviceType(DeviceType_Null),
3105 fPassThrough(false),
3106 fTempEject(false),
3107 fNonRotational(false),
3108 fDiscard(false),
3109 fHotPluggable(false),
3110 lPort(0),
3111 lDevice(0)
3112{
3113}
3114
3115/**
3116 * Comparison operator. This gets called from MachineConfigFile::operator==,
3117 * which in turn gets called from Machine::saveSettings to figure out whether
3118 * machine settings have really changed and thus need to be written out to disk.
3119 */
3120bool AttachedDevice::operator==(const AttachedDevice &a) const
3121{
3122 return (this == &a)
3123 || ( deviceType == a.deviceType
3124 && fPassThrough == a.fPassThrough
3125 && fTempEject == a.fTempEject
3126 && fNonRotational == a.fNonRotational
3127 && fDiscard == a.fDiscard
3128 && fHotPluggable == a.fHotPluggable
3129 && lPort == a.lPort
3130 && lDevice == a.lDevice
3131 && uuid == a.uuid
3132 && strHostDriveSrc == a.strHostDriveSrc
3133 && strBwGroup == a.strBwGroup);
3134}
3135
3136/**
3137 * Constructor. Needs to set sane defaults which stand the test of time.
3138 */
3139StorageController::StorageController() :
3140 storageBus(StorageBus_IDE),
3141 controllerType(StorageControllerType_PIIX3),
3142 ulPortCount(2),
3143 ulInstance(0),
3144 fUseHostIOCache(true),
3145 fBootable(true)
3146{
3147}
3148
3149/**
3150 * Comparison operator. This gets called from MachineConfigFile::operator==,
3151 * which in turn gets called from Machine::saveSettings to figure out whether
3152 * machine settings have really changed and thus need to be written out to disk.
3153 */
3154bool StorageController::operator==(const StorageController &s) const
3155{
3156 return (this == &s)
3157 || ( strName == s.strName
3158 && storageBus == s.storageBus
3159 && controllerType == s.controllerType
3160 && ulPortCount == s.ulPortCount
3161 && ulInstance == s.ulInstance
3162 && fUseHostIOCache == s.fUseHostIOCache
3163 && llAttachedDevices == s.llAttachedDevices);
3164}
3165
3166/**
3167 * Comparison operator. This gets called from MachineConfigFile::operator==,
3168 * which in turn gets called from Machine::saveSettings to figure out whether
3169 * machine settings have really changed and thus need to be written out to disk.
3170 */
3171bool Storage::operator==(const Storage &s) const
3172{
3173 return (this == &s)
3174 || (llStorageControllers == s.llStorageControllers); // deep compare
3175}
3176
3177/**
3178 * Constructor. Needs to set sane defaults which stand the test of time.
3179 */
3180Debugging::Debugging() :
3181 fTracingEnabled(false),
3182 fAllowTracingToAccessVM(false),
3183 strTracingConfig()
3184{
3185}
3186
3187/**
3188 * Check if all settings have default values.
3189 */
3190bool Debugging::areDefaultSettings() const
3191{
3192 return !fTracingEnabled
3193 && !fAllowTracingToAccessVM
3194 && strTracingConfig.isEmpty();
3195}
3196
3197/**
3198 * Comparison operator. This gets called from MachineConfigFile::operator==,
3199 * which in turn gets called from Machine::saveSettings to figure out whether
3200 * machine settings have really changed and thus need to be written out to disk.
3201 */
3202bool Debugging::operator==(const Debugging &d) const
3203{
3204 return (this == &d)
3205 || ( fTracingEnabled == d.fTracingEnabled
3206 && fAllowTracingToAccessVM == d.fAllowTracingToAccessVM
3207 && strTracingConfig == d.strTracingConfig);
3208}
3209
3210/**
3211 * Constructor. Needs to set sane defaults which stand the test of time.
3212 */
3213Autostart::Autostart() :
3214 fAutostartEnabled(false),
3215 uAutostartDelay(0),
3216 enmAutostopType(AutostopType_Disabled)
3217{
3218}
3219
3220/**
3221 * Check if all settings have default values.
3222 */
3223bool Autostart::areDefaultSettings() const
3224{
3225 return !fAutostartEnabled
3226 && !uAutostartDelay
3227 && enmAutostopType == AutostopType_Disabled;
3228}
3229
3230/**
3231 * Comparison operator. This gets called from MachineConfigFile::operator==,
3232 * which in turn gets called from Machine::saveSettings to figure out whether
3233 * machine settings have really changed and thus need to be written out to disk.
3234 */
3235bool Autostart::operator==(const Autostart &a) const
3236{
3237 return (this == &a)
3238 || ( fAutostartEnabled == a.fAutostartEnabled
3239 && uAutostartDelay == a.uAutostartDelay
3240 && enmAutostopType == a.enmAutostopType);
3241}
3242
3243/**
3244 * Constructor. Needs to set sane defaults which stand the test of time.
3245 */
3246Snapshot::Snapshot()
3247{
3248 RTTimeSpecSetNano(&timestamp, 0);
3249}
3250
3251/**
3252 * Comparison operator. This gets called from MachineConfigFile::operator==,
3253 * which in turn gets called from Machine::saveSettings to figure out whether
3254 * machine settings have really changed and thus need to be written out to disk.
3255 */
3256bool Snapshot::operator==(const Snapshot &s) const
3257{
3258 return (this == &s)
3259 || ( uuid == s.uuid
3260 && strName == s.strName
3261 && strDescription == s.strDescription
3262 && RTTimeSpecIsEqual(&timestamp, &s.timestamp)
3263 && strStateFile == s.strStateFile
3264 && hardware == s.hardware // deep compare
3265 && llChildSnapshots == s.llChildSnapshots // deep compare
3266 && debugging == s.debugging
3267 && autostart == s.autostart);
3268}
3269
3270const struct Snapshot settings::Snapshot::Empty; /* default ctor is OK */
3271
3272/**
3273 * Constructor. Needs to set sane defaults which stand the test of time.
3274 */
3275MachineUserData::MachineUserData() :
3276 fDirectoryIncludesUUID(false),
3277 fNameSync(true),
3278 fTeleporterEnabled(false),
3279 uTeleporterPort(0),
3280 enmFaultToleranceState(FaultToleranceState_Inactive),
3281 uFaultTolerancePort(0),
3282 uFaultToleranceInterval(0),
3283 fRTCUseUTC(false),
3284 strVMPriority()
3285{
3286 llGroups.push_back("/");
3287}
3288
3289/**
3290 * Comparison operator. This gets called from MachineConfigFile::operator==,
3291 * which in turn gets called from Machine::saveSettings to figure out whether
3292 * machine settings have really changed and thus need to be written out to disk.
3293 */
3294bool MachineUserData::operator==(const MachineUserData &c) const
3295{
3296 return (this == &c)
3297 || ( strName == c.strName
3298 && fDirectoryIncludesUUID == c.fDirectoryIncludesUUID
3299 && fNameSync == c.fNameSync
3300 && strDescription == c.strDescription
3301 && llGroups == c.llGroups
3302 && strOsType == c.strOsType
3303 && strSnapshotFolder == c.strSnapshotFolder
3304 && fTeleporterEnabled == c.fTeleporterEnabled
3305 && uTeleporterPort == c.uTeleporterPort
3306 && strTeleporterAddress == c.strTeleporterAddress
3307 && strTeleporterPassword == c.strTeleporterPassword
3308 && enmFaultToleranceState == c.enmFaultToleranceState
3309 && uFaultTolerancePort == c.uFaultTolerancePort
3310 && uFaultToleranceInterval == c.uFaultToleranceInterval
3311 && strFaultToleranceAddress == c.strFaultToleranceAddress
3312 && strFaultTolerancePassword == c.strFaultTolerancePassword
3313 && fRTCUseUTC == c.fRTCUseUTC
3314 && ovIcon == c.ovIcon
3315 && strVMPriority == c.strVMPriority);
3316}
3317
3318
3319////////////////////////////////////////////////////////////////////////////////
3320//
3321// MachineConfigFile
3322//
3323////////////////////////////////////////////////////////////////////////////////
3324
3325/**
3326 * Constructor.
3327 *
3328 * If pstrFilename is != NULL, this reads the given settings file into the member
3329 * variables and various substructures and lists. Otherwise, the member variables
3330 * are initialized with default values.
3331 *
3332 * Throws variants of xml::Error for I/O, XML and logical content errors, which
3333 * the caller should catch; if this constructor does not throw, then the member
3334 * variables contain meaningful values (either from the file or defaults).
3335 *
3336 * @param pstrFilename
3337 */
3338MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
3339 : ConfigFileBase(pstrFilename),
3340 fCurrentStateModified(true),
3341 fAborted(false)
3342{
3343 RTTimeNow(&timeLastStateChange);
3344
3345 if (pstrFilename)
3346 {
3347 // the ConfigFileBase constructor has loaded the XML file, so now
3348 // we need only analyze what is in there
3349
3350 xml::NodesLoop nlRootChildren(*m->pelmRoot);
3351 const xml::ElementNode *pelmRootChild;
3352 while ((pelmRootChild = nlRootChildren.forAllNodes()))
3353 {
3354 if (pelmRootChild->nameEquals("Machine"))
3355 readMachine(*pelmRootChild);
3356 }
3357
3358 // clean up memory allocated by XML engine
3359 clearDocument();
3360 }
3361}
3362
3363/**
3364 * Public routine which returns true if this machine config file can have its
3365 * own media registry (which is true for settings version v1.11 and higher,
3366 * i.e. files created by VirtualBox 4.0 and higher).
3367 * @return
3368 */
3369bool MachineConfigFile::canHaveOwnMediaRegistry() const
3370{
3371 return (m->sv >= SettingsVersion_v1_11);
3372}
3373
3374/**
3375 * Public routine which allows for importing machine XML from an external DOM tree.
3376 * Use this after having called the constructor with a NULL argument.
3377 *
3378 * This is used by the OVF code if a <vbox:Machine> element has been encountered
3379 * in an OVF VirtualSystem element.
3380 *
3381 * @param elmMachine
3382 */
3383void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine)
3384{
3385 // Ideally the version should be mandatory, but since VirtualBox didn't
3386 // care about it until 5.1 came with different defaults, there are OVF
3387 // files created by magicians (not using VirtualBox, which always wrote it)
3388 // which lack this information. Let's hope that they learn to add the
3389 // version when they switch to the newer settings style/defaults of 5.1.
3390 if (!(elmMachine.getAttributeValue("version", m->strSettingsVersionFull)))
3391 m->strSettingsVersionFull = VBOX_XML_IMPORT_VERSION_FULL;
3392
3393 LogRel(("Import settings with version \"%s\"\n", m->strSettingsVersionFull.c_str()));
3394
3395 m->sv = parseVersion(m->strSettingsVersionFull, &elmMachine);
3396
3397 // remember the settings version we read in case it gets upgraded later,
3398 // so we know when to make backups
3399 m->svRead = m->sv;
3400
3401 readMachine(elmMachine);
3402}
3403
3404/**
3405 * Comparison operator. This gets called from Machine::saveSettings to figure out
3406 * whether machine settings have really changed and thus need to be written out to disk.
3407 *
3408 * Even though this is called operator==, this does NOT compare all fields; the "equals"
3409 * should be understood as "has the same machine config as". The following fields are
3410 * NOT compared:
3411 * -- settings versions and file names inherited from ConfigFileBase;
3412 * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
3413 *
3414 * The "deep" comparisons marked below will invoke the operator== functions of the
3415 * structs defined in this file, which may in turn go into comparing lists of
3416 * other structures. As a result, invoking this can be expensive, but it's
3417 * less expensive than writing out XML to disk.
3418 */
3419bool MachineConfigFile::operator==(const MachineConfigFile &c) const
3420{
3421 return (this == &c)
3422 || ( uuid == c.uuid
3423 && machineUserData == c.machineUserData
3424 && strStateFile == c.strStateFile
3425 && uuidCurrentSnapshot == c.uuidCurrentSnapshot
3426 // skip fCurrentStateModified!
3427 && RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange)
3428 && fAborted == c.fAborted
3429 && hardwareMachine == c.hardwareMachine // this one's deep
3430 && mediaRegistry == c.mediaRegistry // this one's deep
3431 // skip mapExtraDataItems! there is no old state available as it's always forced
3432 && llFirstSnapshot == c.llFirstSnapshot); // this one's deep
3433}
3434
3435/**
3436 * Called from MachineConfigFile::readHardware() to read cpu information.
3437 * @param elmCpu
3438 * @param ll
3439 */
3440void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
3441 CpuList &ll)
3442{
3443 xml::NodesLoop nl1(elmCpu, "Cpu");
3444 const xml::ElementNode *pelmCpu;
3445 while ((pelmCpu = nl1.forAllNodes()))
3446 {
3447 Cpu cpu;
3448
3449 if (!pelmCpu->getAttributeValue("id", cpu.ulId))
3450 throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
3451
3452 ll.push_back(cpu);
3453 }
3454}
3455
3456/**
3457 * Called from MachineConfigFile::readHardware() to cpuid information.
3458 * @param elmCpuid
3459 * @param ll
3460 */
3461void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid,
3462 CpuIdLeafsList &ll)
3463{
3464 xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf");
3465 const xml::ElementNode *pelmCpuIdLeaf;
3466 while ((pelmCpuIdLeaf = nl1.forAllNodes()))
3467 {
3468 CpuIdLeaf leaf;
3469
3470 if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.idx))
3471 throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing"));
3472
3473 if (!pelmCpuIdLeaf->getAttributeValue("subleaf", leaf.idxSub))
3474 leaf.idxSub = 0;
3475 pelmCpuIdLeaf->getAttributeValue("eax", leaf.uEax);
3476 pelmCpuIdLeaf->getAttributeValue("ebx", leaf.uEbx);
3477 pelmCpuIdLeaf->getAttributeValue("ecx", leaf.uEcx);
3478 pelmCpuIdLeaf->getAttributeValue("edx", leaf.uEdx);
3479
3480 ll.push_back(leaf);
3481 }
3482}
3483
3484/**
3485 * Called from MachineConfigFile::readHardware() to network information.
3486 * @param elmNetwork
3487 * @param ll
3488 */
3489void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
3490 NetworkAdaptersList &ll)
3491{
3492 xml::NodesLoop nl1(elmNetwork, "Adapter");
3493 const xml::ElementNode *pelmAdapter;
3494 while ((pelmAdapter = nl1.forAllNodes()))
3495 {
3496 NetworkAdapter nic;
3497
3498 if (m->sv >= SettingsVersion_v1_16)
3499 {
3500 /* Starting with VirtualBox 5.1 the default is cable connected and
3501 * PCnet-FAST III. Needs to match NetworkAdapter.areDefaultSettings(). */
3502 nic.fCableConnected = true;
3503 nic.type = NetworkAdapterType_Am79C973;
3504 }
3505
3506 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
3507 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
3508
3509 Utf8Str strTemp;
3510 if (pelmAdapter->getAttributeValue("type", strTemp))
3511 {
3512 if (strTemp == "Am79C970A")
3513 nic.type = NetworkAdapterType_Am79C970A;
3514 else if (strTemp == "Am79C973")
3515 nic.type = NetworkAdapterType_Am79C973;
3516 else if (strTemp == "82540EM")
3517 nic.type = NetworkAdapterType_I82540EM;
3518 else if (strTemp == "82543GC")
3519 nic.type = NetworkAdapterType_I82543GC;
3520 else if (strTemp == "82545EM")
3521 nic.type = NetworkAdapterType_I82545EM;
3522 else if (strTemp == "virtio")
3523 nic.type = NetworkAdapterType_Virtio;
3524 else
3525 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
3526 }
3527
3528 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
3529 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
3530 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
3531 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
3532
3533 if (pelmAdapter->getAttributeValue("promiscuousModePolicy", strTemp))
3534 {
3535 if (strTemp == "Deny")
3536 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
3537 else if (strTemp == "AllowNetwork")
3538 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
3539 else if (strTemp == "AllowAll")
3540 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
3541 else
3542 throw ConfigFileError(this, pelmAdapter,
3543 N_("Invalid value '%s' in Adapter/@promiscuousModePolicy attribute"), strTemp.c_str());
3544 }
3545
3546 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
3547 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
3548 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
3549 pelmAdapter->getAttributeValue("bandwidthGroup", nic.strBandwidthGroup);
3550
3551 xml::ElementNodesList llNetworkModes;
3552 pelmAdapter->getChildElements(llNetworkModes);
3553 xml::ElementNodesList::iterator it;
3554 /* We should have only active mode descriptor and disabled modes set */
3555 if (llNetworkModes.size() > 2)
3556 {
3557 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
3558 }
3559 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
3560 {
3561 const xml::ElementNode *pelmNode = *it;
3562 if (pelmNode->nameEquals("DisabledModes"))
3563 {
3564 xml::ElementNodesList llDisabledNetworkModes;
3565 xml::ElementNodesList::iterator itDisabled;
3566 pelmNode->getChildElements(llDisabledNetworkModes);
3567 /* run over disabled list and load settings */
3568 for (itDisabled = llDisabledNetworkModes.begin();
3569 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
3570 {
3571 const xml::ElementNode *pelmDisabledNode = *itDisabled;
3572 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
3573 }
3574 }
3575 else
3576 readAttachedNetworkMode(*pelmNode, true, nic);
3577 }
3578 // else: default is NetworkAttachmentType_Null
3579
3580 ll.push_back(nic);
3581 }
3582}
3583
3584void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
3585{
3586 NetworkAttachmentType_T enmAttachmentType = NetworkAttachmentType_Null;
3587
3588 if (elmMode.nameEquals("NAT"))
3589 {
3590 enmAttachmentType = NetworkAttachmentType_NAT;
3591
3592 elmMode.getAttributeValue("network", nic.nat.strNetwork);
3593 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
3594 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
3595 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
3596 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
3597 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
3598 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
3599 const xml::ElementNode *pelmDNS;
3600 if ((pelmDNS = elmMode.findChildElement("DNS")))
3601 {
3602 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDNSPassDomain);
3603 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDNSProxy);
3604 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDNSUseHostResolver);
3605 }
3606 const xml::ElementNode *pelmAlias;
3607 if ((pelmAlias = elmMode.findChildElement("Alias")))
3608 {
3609 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
3610 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
3611 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
3612 }
3613 const xml::ElementNode *pelmTFTP;
3614 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
3615 {
3616 pelmTFTP->getAttributeValue("prefix", nic.nat.strTFTPPrefix);
3617 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTFTPBootFile);
3618 pelmTFTP->getAttributeValue("next-server", nic.nat.strTFTPNextServer);
3619 }
3620
3621 readNATForwardRulesMap(elmMode, nic.nat.mapRules);
3622 }
3623 else if ( elmMode.nameEquals("HostInterface")
3624 || elmMode.nameEquals("BridgedInterface"))
3625 {
3626 enmAttachmentType = NetworkAttachmentType_Bridged;
3627
3628 // optional network name, cannot be required or we have trouble with
3629 // settings which are saved before configuring the network name
3630 elmMode.getAttributeValue("name", nic.strBridgedName);
3631 }
3632 else if (elmMode.nameEquals("InternalNetwork"))
3633 {
3634 enmAttachmentType = NetworkAttachmentType_Internal;
3635
3636 // optional network name, cannot be required or we have trouble with
3637 // settings which are saved before configuring the network name
3638 elmMode.getAttributeValue("name", nic.strInternalNetworkName);
3639 }
3640 else if (elmMode.nameEquals("HostOnlyInterface"))
3641 {
3642 enmAttachmentType = NetworkAttachmentType_HostOnly;
3643
3644 // optional network name, cannot be required or we have trouble with
3645 // settings which are saved before configuring the network name
3646 elmMode.getAttributeValue("name", nic.strHostOnlyName);
3647 }
3648 else if (elmMode.nameEquals("GenericInterface"))
3649 {
3650 enmAttachmentType = NetworkAttachmentType_Generic;
3651
3652 elmMode.getAttributeValue("driver", nic.strGenericDriver); // optional network attachment driver
3653
3654 // get all properties
3655 xml::NodesLoop nl(elmMode);
3656 const xml::ElementNode *pelmModeChild;
3657 while ((pelmModeChild = nl.forAllNodes()))
3658 {
3659 if (pelmModeChild->nameEquals("Property"))
3660 {
3661 Utf8Str strPropName, strPropValue;
3662 if ( pelmModeChild->getAttributeValue("name", strPropName)
3663 && pelmModeChild->getAttributeValue("value", strPropValue) )
3664 nic.genericProperties[strPropName] = strPropValue;
3665 else
3666 throw ConfigFileError(this, pelmModeChild, N_("Required GenericInterface/Property/@name or @value attribute is missing"));
3667 }
3668 }
3669 }
3670 else if (elmMode.nameEquals("NATNetwork"))
3671 {
3672 enmAttachmentType = NetworkAttachmentType_NATNetwork;
3673
3674 // optional network name, cannot be required or we have trouble with
3675 // settings which are saved before configuring the network name
3676 elmMode.getAttributeValue("name", nic.strNATNetworkName);
3677 }
3678 else if (elmMode.nameEquals("VDE"))
3679 {
3680 // inofficial hack (VDE networking was never part of the official
3681 // settings, so it's not mentioned in VirtualBox-settings.xsd)
3682 enmAttachmentType = NetworkAttachmentType_Generic;
3683
3684 com::Utf8Str strVDEName;
3685 elmMode.getAttributeValue("network", strVDEName); // optional network name
3686 nic.strGenericDriver = "VDE";
3687 nic.genericProperties["network"] = strVDEName;
3688 }
3689
3690 if (fEnabled && enmAttachmentType != NetworkAttachmentType_Null)
3691 nic.mode = enmAttachmentType;
3692}
3693
3694/**
3695 * Called from MachineConfigFile::readHardware() to read serial port information.
3696 * @param elmUART
3697 * @param ll
3698 */
3699void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
3700 SerialPortsList &ll)
3701{
3702 xml::NodesLoop nl1(elmUART, "Port");
3703 const xml::ElementNode *pelmPort;
3704 while ((pelmPort = nl1.forAllNodes()))
3705 {
3706 SerialPort port;
3707 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
3708 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
3709
3710 // slot must be unique
3711 for (SerialPortsList::const_iterator it = ll.begin();
3712 it != ll.end();
3713 ++it)
3714 if ((*it).ulSlot == port.ulSlot)
3715 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
3716
3717 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
3718 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
3719 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
3720 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
3721 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
3722 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
3723
3724 Utf8Str strPortMode;
3725 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
3726 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
3727 if (strPortMode == "RawFile")
3728 port.portMode = PortMode_RawFile;
3729 else if (strPortMode == "HostPipe")
3730 port.portMode = PortMode_HostPipe;
3731 else if (strPortMode == "HostDevice")
3732 port.portMode = PortMode_HostDevice;
3733 else if (strPortMode == "Disconnected")
3734 port.portMode = PortMode_Disconnected;
3735 else if (strPortMode == "TCP")
3736 port.portMode = PortMode_TCP;
3737 else
3738 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
3739
3740 pelmPort->getAttributeValue("path", port.strPath);
3741 pelmPort->getAttributeValue("server", port.fServer);
3742
3743 Utf8Str strUartType;
3744 if (pelmPort->getAttributeValue("uartType", strUartType))
3745 {
3746 if (strUartType == "16450")
3747 port.uartType = UartType_U16450;
3748 else if (strUartType == "16550A")
3749 port.uartType = UartType_U16550A;
3750 else if (strUartType == "16750")
3751 port.uartType = UartType_U16750;
3752 else
3753 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@uartType attribute"), strUartType.c_str());
3754 }
3755
3756 ll.push_back(port);
3757 }
3758}
3759
3760/**
3761 * Called from MachineConfigFile::readHardware() to read parallel port information.
3762 * @param elmLPT
3763 * @param ll
3764 */
3765void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
3766 ParallelPortsList &ll)
3767{
3768 xml::NodesLoop nl1(elmLPT, "Port");
3769 const xml::ElementNode *pelmPort;
3770 while ((pelmPort = nl1.forAllNodes()))
3771 {
3772 ParallelPort port;
3773 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
3774 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
3775
3776 // slot must be unique
3777 for (ParallelPortsList::const_iterator it = ll.begin();
3778 it != ll.end();
3779 ++it)
3780 if ((*it).ulSlot == port.ulSlot)
3781 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
3782
3783 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
3784 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
3785 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
3786 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
3787 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
3788 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
3789
3790 pelmPort->getAttributeValue("path", port.strPath);
3791
3792 ll.push_back(port);
3793 }
3794}
3795
3796/**
3797 * Called from MachineConfigFile::readHardware() to read audio adapter information
3798 * and maybe fix driver information depending on the current host hardware.
3799 *
3800 * @param elmAudioAdapter "AudioAdapter" XML element.
3801 * @param aa
3802 */
3803void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
3804 AudioAdapter &aa)
3805{
3806 if (m->sv >= SettingsVersion_v1_15)
3807 {
3808 // get all properties
3809 xml::NodesLoop nl1(elmAudioAdapter, "Property");
3810 const xml::ElementNode *pelmModeChild;
3811 while ((pelmModeChild = nl1.forAllNodes()))
3812 {
3813 Utf8Str strPropName, strPropValue;
3814 if ( pelmModeChild->getAttributeValue("name", strPropName)
3815 && pelmModeChild->getAttributeValue("value", strPropValue) )
3816 aa.properties[strPropName] = strPropValue;
3817 else
3818 throw ConfigFileError(this, pelmModeChild, N_("Required AudioAdapter/Property/@name or @value attribute "
3819 "is missing"));
3820 }
3821 }
3822
3823 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
3824 elmAudioAdapter.getAttributeValue("enabledIn", aa.fEnabledIn);
3825 elmAudioAdapter.getAttributeValue("enabledOut", aa.fEnabledOut);
3826
3827 Utf8Str strTemp;
3828 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
3829 {
3830 if (strTemp == "SB16")
3831 aa.controllerType = AudioControllerType_SB16;
3832 else if (strTemp == "AC97")
3833 aa.controllerType = AudioControllerType_AC97;
3834 else if (strTemp == "HDA")
3835 aa.controllerType = AudioControllerType_HDA;
3836 else
3837 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
3838 }
3839
3840 if (elmAudioAdapter.getAttributeValue("codec", strTemp))
3841 {
3842 if (strTemp == "SB16")
3843 aa.codecType = AudioCodecType_SB16;
3844 else if (strTemp == "STAC9700")
3845 aa.codecType = AudioCodecType_STAC9700;
3846 else if (strTemp == "AD1980")
3847 aa.codecType = AudioCodecType_AD1980;
3848 else if (strTemp == "STAC9221")
3849 aa.codecType = AudioCodecType_STAC9221;
3850 else
3851 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@codec attribute"), strTemp.c_str());
3852 }
3853 else
3854 {
3855 /* No codec attribute provided; use defaults. */
3856 switch (aa.controllerType)
3857 {
3858 case AudioControllerType_AC97:
3859 aa.codecType = AudioCodecType_STAC9700;
3860 break;
3861 case AudioControllerType_SB16:
3862 aa.codecType = AudioCodecType_SB16;
3863 break;
3864 case AudioControllerType_HDA:
3865 aa.codecType = AudioCodecType_STAC9221;
3866 break;
3867 default:
3868 Assert(false); /* We just checked the controller type above. */
3869 }
3870 }
3871
3872 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
3873 {
3874 // settings before 1.3 used lower case so make sure this is case-insensitive
3875 strTemp.toUpper();
3876 if (strTemp == "NULL")
3877 aa.driverType = AudioDriverType_Null;
3878 else if (strTemp == "WINMM")
3879 aa.driverType = AudioDriverType_WinMM;
3880 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
3881 aa.driverType = AudioDriverType_DirectSound;
3882 else if (strTemp == "SOLAUDIO") /* Deprecated -- Solaris will use OSS by default now. */
3883 aa.driverType = AudioDriverType_SolAudio;
3884 else if (strTemp == "ALSA")
3885 aa.driverType = AudioDriverType_ALSA;
3886 else if (strTemp == "PULSE")
3887 aa.driverType = AudioDriverType_Pulse;
3888 else if (strTemp == "OSS")
3889 aa.driverType = AudioDriverType_OSS;
3890 else if (strTemp == "COREAUDIO")
3891 aa.driverType = AudioDriverType_CoreAudio;
3892 else if (strTemp == "MMPM")
3893 aa.driverType = AudioDriverType_MMPM;
3894 else
3895 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
3896
3897 // now check if this is actually supported on the current host platform;
3898 // people might be opening a file created on a Windows host, and that
3899 // VM should still start on a Linux host
3900 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
3901 aa.driverType = getHostDefaultAudioDriver();
3902 }
3903}
3904
3905/**
3906 * Called from MachineConfigFile::readHardware() to read guest property information.
3907 * @param elmGuestProperties
3908 * @param hw
3909 */
3910void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
3911 Hardware &hw)
3912{
3913 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
3914 const xml::ElementNode *pelmProp;
3915 while ((pelmProp = nl1.forAllNodes()))
3916 {
3917 GuestProperty prop;
3918 pelmProp->getAttributeValue("name", prop.strName);
3919 pelmProp->getAttributeValue("value", prop.strValue);
3920
3921 pelmProp->getAttributeValue("timestamp", prop.timestamp);
3922 pelmProp->getAttributeValue("flags", prop.strFlags);
3923 hw.llGuestProperties.push_back(prop);
3924 }
3925}
3926
3927/**
3928 * Helper function to read attributes that are common to \<SATAController\> (pre-1.7)
3929 * and \<StorageController\>.
3930 * @param elmStorageController
3931 * @param sctl
3932 */
3933void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
3934 StorageController &sctl)
3935{
3936 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
3937 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
3938}
3939
3940/**
3941 * Reads in a \<Hardware\> block and stores it in the given structure. Used
3942 * both directly from readMachine and from readSnapshot, since snapshots
3943 * have their own hardware sections.
3944 *
3945 * For legacy pre-1.7 settings we also need a storage structure because
3946 * the IDE and SATA controllers used to be defined under \<Hardware\>.
3947 *
3948 * @param elmHardware
3949 * @param hw
3950 */
3951void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
3952 Hardware &hw)
3953{
3954 if (m->sv >= SettingsVersion_v1_16)
3955 {
3956 /* Starting with VirtualBox 5.1 the default is Default, before it was
3957 * Legacy. This needs to matched by areParavirtDefaultSettings(). */
3958 hw.paravirtProvider = ParavirtProvider_Default;
3959 /* The new default is disabled, before it was enabled by default. */
3960 hw.vrdeSettings.fEnabled = false;
3961 /* The new default is disabled, before it was enabled by default. */
3962 hw.audioAdapter.fEnabled = false;
3963 }
3964 else if (m->sv >= SettingsVersion_v1_17)
3965 {
3966 /* Starting with VirtualBox 5.2 the default is disabled, before it was
3967 * enabled. This needs to matched by AudioAdapter::areDefaultSettings(). */
3968 hw.audioAdapter.fEnabledIn = false;
3969 /* The new default is disabled, before it was enabled by default. */
3970 hw.audioAdapter.fEnabledOut = false;
3971 }
3972
3973 if (!elmHardware.getAttributeValue("version", hw.strVersion))
3974 {
3975 /* KLUDGE ALERT! For a while during the 3.1 development this was not
3976 written because it was thought to have a default value of "2". For
3977 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
3978 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
3979 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
3980 missing the hardware version, then it probably should be "2" instead
3981 of "1". */
3982 if (m->sv < SettingsVersion_v1_7)
3983 hw.strVersion = "1";
3984 else
3985 hw.strVersion = "2";
3986 }
3987 Utf8Str strUUID;
3988 if (elmHardware.getAttributeValue("uuid", strUUID))
3989 parseUUID(hw.uuid, strUUID, &elmHardware);
3990
3991 xml::NodesLoop nl1(elmHardware);
3992 const xml::ElementNode *pelmHwChild;
3993 while ((pelmHwChild = nl1.forAllNodes()))
3994 {
3995 if (pelmHwChild->nameEquals("CPU"))
3996 {
3997 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
3998 {
3999 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
4000 const xml::ElementNode *pelmCPUChild;
4001 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
4002 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
4003 }
4004
4005 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
4006 pelmHwChild->getAttributeValue("executionCap", hw.ulCpuExecutionCap);
4007
4008 const xml::ElementNode *pelmCPUChild;
4009 if (hw.fCpuHotPlug)
4010 {
4011 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
4012 readCpuTree(*pelmCPUChild, hw.llCpus);
4013 }
4014
4015 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
4016 {
4017 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
4018 }
4019 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
4020 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
4021 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
4022 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
4023 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
4024 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
4025 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUX")))
4026 pelmCPUChild->getAttributeValue("enabled", hw.fUnrestrictedExecution);
4027 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtForce")))
4028 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirtForce);
4029 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUseNativeApi")))
4030 pelmCPUChild->getAttributeValue("enabled", hw.fUseNativeApi);
4031
4032 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
4033 {
4034 /* The default for pre 3.1 was false, so we must respect that. */
4035 if (m->sv < SettingsVersion_v1_9)
4036 hw.fPAE = false;
4037 }
4038 else
4039 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
4040
4041 bool fLongMode;
4042 if ( (pelmCPUChild = pelmHwChild->findChildElement("LongMode"))
4043 && pelmCPUChild->getAttributeValue("enabled", fLongMode) )
4044 hw.enmLongMode = fLongMode ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled;
4045 else
4046 hw.enmLongMode = Hardware::LongMode_Legacy;
4047
4048 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
4049 {
4050 bool fSyntheticCpu = false;
4051 pelmCPUChild->getAttributeValue("enabled", fSyntheticCpu);
4052 hw.uCpuIdPortabilityLevel = fSyntheticCpu ? 1 : 0;
4053 }
4054 pelmHwChild->getAttributeValue("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
4055 pelmHwChild->getAttributeValue("CpuProfile", hw.strCpuProfile);
4056
4057 if ((pelmCPUChild = pelmHwChild->findChildElement("TripleFaultReset")))
4058 pelmCPUChild->getAttributeValue("enabled", hw.fTripleFaultReset);
4059
4060 if ((pelmCPUChild = pelmHwChild->findChildElement("APIC")))
4061 pelmCPUChild->getAttributeValue("enabled", hw.fAPIC);
4062 if ((pelmCPUChild = pelmHwChild->findChildElement("X2APIC")))
4063 pelmCPUChild->getAttributeValue("enabled", hw.fX2APIC);
4064 if (hw.fX2APIC)
4065 hw.fAPIC = true;
4066 pelmCPUChild = pelmHwChild->findChildElement("IBPBOn");
4067 if (pelmCPUChild)
4068 {
4069 pelmCPUChild->getAttributeValue("vmexit", hw.fIBPBOnVMExit);
4070 pelmCPUChild->getAttributeValue("vmentry", hw.fIBPBOnVMEntry);
4071 }
4072 pelmCPUChild = pelmHwChild->findChildElement("SpecCtrl");
4073 if (pelmCPUChild)
4074 pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrl);
4075 pelmCPUChild = pelmHwChild->findChildElement("SpecCtrlByHost");
4076 if (pelmCPUChild)
4077 pelmCPUChild->getAttributeValue("enabled", hw.fSpecCtrlByHost);
4078 pelmCPUChild = pelmHwChild->findChildElement("NestedHWVirt");
4079 if (pelmCPUChild)
4080 pelmCPUChild->getAttributeValue("enabled", hw.fNestedHWVirt);
4081
4082 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
4083 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
4084 }
4085 else if (pelmHwChild->nameEquals("Memory"))
4086 {
4087 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
4088 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
4089 }
4090 else if (pelmHwChild->nameEquals("Firmware"))
4091 {
4092 Utf8Str strFirmwareType;
4093 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
4094 {
4095 if ( (strFirmwareType == "BIOS")
4096 || (strFirmwareType == "1") // some trunk builds used the number here
4097 )
4098 hw.firmwareType = FirmwareType_BIOS;
4099 else if ( (strFirmwareType == "EFI")
4100 || (strFirmwareType == "2") // some trunk builds used the number here
4101 )
4102 hw.firmwareType = FirmwareType_EFI;
4103 else if ( strFirmwareType == "EFI32")
4104 hw.firmwareType = FirmwareType_EFI32;
4105 else if ( strFirmwareType == "EFI64")
4106 hw.firmwareType = FirmwareType_EFI64;
4107 else if ( strFirmwareType == "EFIDUAL")
4108 hw.firmwareType = FirmwareType_EFIDUAL;
4109 else
4110 throw ConfigFileError(this,
4111 pelmHwChild,
4112 N_("Invalid value '%s' in Firmware/@type"),
4113 strFirmwareType.c_str());
4114 }
4115 }
4116 else if (pelmHwChild->nameEquals("HID"))
4117 {
4118 Utf8Str strHIDType;
4119 if (pelmHwChild->getAttributeValue("Keyboard", strHIDType))
4120 {
4121 if (strHIDType == "None")
4122 hw.keyboardHIDType = KeyboardHIDType_None;
4123 else if (strHIDType == "USBKeyboard")
4124 hw.keyboardHIDType = KeyboardHIDType_USBKeyboard;
4125 else if (strHIDType == "PS2Keyboard")
4126 hw.keyboardHIDType = KeyboardHIDType_PS2Keyboard;
4127 else if (strHIDType == "ComboKeyboard")
4128 hw.keyboardHIDType = KeyboardHIDType_ComboKeyboard;
4129 else
4130 throw ConfigFileError(this,
4131 pelmHwChild,
4132 N_("Invalid value '%s' in HID/Keyboard/@type"),
4133 strHIDType.c_str());
4134 }
4135 if (pelmHwChild->getAttributeValue("Pointing", strHIDType))
4136 {
4137 if (strHIDType == "None")
4138 hw.pointingHIDType = PointingHIDType_None;
4139 else if (strHIDType == "USBMouse")
4140 hw.pointingHIDType = PointingHIDType_USBMouse;
4141 else if (strHIDType == "USBTablet")
4142 hw.pointingHIDType = PointingHIDType_USBTablet;
4143 else if (strHIDType == "PS2Mouse")
4144 hw.pointingHIDType = PointingHIDType_PS2Mouse;
4145 else if (strHIDType == "ComboMouse")
4146 hw.pointingHIDType = PointingHIDType_ComboMouse;
4147 else if (strHIDType == "USBMultiTouch")
4148 hw.pointingHIDType = PointingHIDType_USBMultiTouch;
4149 else
4150 throw ConfigFileError(this,
4151 pelmHwChild,
4152 N_("Invalid value '%s' in HID/Pointing/@type"),
4153 strHIDType.c_str());
4154 }
4155 }
4156 else if (pelmHwChild->nameEquals("Chipset"))
4157 {
4158 Utf8Str strChipsetType;
4159 if (pelmHwChild->getAttributeValue("type", strChipsetType))
4160 {
4161 if (strChipsetType == "PIIX3")
4162 hw.chipsetType = ChipsetType_PIIX3;
4163 else if (strChipsetType == "ICH9")
4164 hw.chipsetType = ChipsetType_ICH9;
4165 else
4166 throw ConfigFileError(this,
4167 pelmHwChild,
4168 N_("Invalid value '%s' in Chipset/@type"),
4169 strChipsetType.c_str());
4170 }
4171 }
4172 else if (pelmHwChild->nameEquals("Paravirt"))
4173 {
4174 Utf8Str strProvider;
4175 if (pelmHwChild->getAttributeValue("provider", strProvider))
4176 {
4177 if (strProvider == "None")
4178 hw.paravirtProvider = ParavirtProvider_None;
4179 else if (strProvider == "Default")
4180 hw.paravirtProvider = ParavirtProvider_Default;
4181 else if (strProvider == "Legacy")
4182 hw.paravirtProvider = ParavirtProvider_Legacy;
4183 else if (strProvider == "Minimal")
4184 hw.paravirtProvider = ParavirtProvider_Minimal;
4185 else if (strProvider == "HyperV")
4186 hw.paravirtProvider = ParavirtProvider_HyperV;
4187 else if (strProvider == "KVM")
4188 hw.paravirtProvider = ParavirtProvider_KVM;
4189 else
4190 throw ConfigFileError(this,
4191 pelmHwChild,
4192 N_("Invalid value '%s' in Paravirt/@provider attribute"),
4193 strProvider.c_str());
4194 }
4195
4196 pelmHwChild->getAttributeValue("debug", hw.strParavirtDebug);
4197 }
4198 else if (pelmHwChild->nameEquals("HPET"))
4199 {
4200 pelmHwChild->getAttributeValue("enabled", hw.fHPETEnabled);
4201 }
4202 else if (pelmHwChild->nameEquals("Boot"))
4203 {
4204 hw.mapBootOrder.clear();
4205
4206 xml::NodesLoop nl2(*pelmHwChild, "Order");
4207 const xml::ElementNode *pelmOrder;
4208 while ((pelmOrder = nl2.forAllNodes()))
4209 {
4210 uint32_t ulPos;
4211 Utf8Str strDevice;
4212 if (!pelmOrder->getAttributeValue("position", ulPos))
4213 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
4214
4215 if ( ulPos < 1
4216 || ulPos > SchemaDefs::MaxBootPosition
4217 )
4218 throw ConfigFileError(this,
4219 pelmOrder,
4220 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
4221 ulPos,
4222 SchemaDefs::MaxBootPosition + 1);
4223 // XML is 1-based but internal data is 0-based
4224 --ulPos;
4225
4226 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
4227 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
4228
4229 if (!pelmOrder->getAttributeValue("device", strDevice))
4230 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
4231
4232 DeviceType_T type;
4233 if (strDevice == "None")
4234 type = DeviceType_Null;
4235 else if (strDevice == "Floppy")
4236 type = DeviceType_Floppy;
4237 else if (strDevice == "DVD")
4238 type = DeviceType_DVD;
4239 else if (strDevice == "HardDisk")
4240 type = DeviceType_HardDisk;
4241 else if (strDevice == "Network")
4242 type = DeviceType_Network;
4243 else
4244 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
4245 hw.mapBootOrder[ulPos] = type;
4246 }
4247 }
4248 else if (pelmHwChild->nameEquals("Display"))
4249 {
4250 Utf8Str strGraphicsControllerType;
4251 if (!pelmHwChild->getAttributeValue("controller", strGraphicsControllerType))
4252 hw.graphicsControllerType = GraphicsControllerType_VBoxVGA;
4253 else
4254 {
4255 strGraphicsControllerType.toUpper();
4256 GraphicsControllerType_T type;
4257 if (strGraphicsControllerType == "VBOXVGA")
4258 type = GraphicsControllerType_VBoxVGA;
4259 else if (strGraphicsControllerType == "VMSVGA")
4260 type = GraphicsControllerType_VMSVGA;
4261 else if (strGraphicsControllerType == "VBOXSVGA")
4262 type = GraphicsControllerType_VBoxSVGA;
4263 else if (strGraphicsControllerType == "NONE")
4264 type = GraphicsControllerType_Null;
4265 else
4266 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Display/@controller attribute"), strGraphicsControllerType.c_str());
4267 hw.graphicsControllerType = type;
4268 }
4269 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
4270 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
4271 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
4272 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
4273 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
4274 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
4275 }
4276 else if (pelmHwChild->nameEquals("VideoCapture"))
4277 {
4278 pelmHwChild->getAttributeValue("enabled", hw.fVideoCaptureEnabled);
4279 pelmHwChild->getAttributeValue("screens", hw.u64VideoCaptureScreens);
4280 pelmHwChild->getAttributeValuePath("file", hw.strVideoCaptureFile);
4281 pelmHwChild->getAttributeValue("horzRes", hw.ulVideoCaptureHorzRes);
4282 pelmHwChild->getAttributeValue("vertRes", hw.ulVideoCaptureVertRes);
4283 pelmHwChild->getAttributeValue("rate", hw.ulVideoCaptureRate);
4284 pelmHwChild->getAttributeValue("fps", hw.ulVideoCaptureFPS);
4285 pelmHwChild->getAttributeValue("maxTime", hw.ulVideoCaptureMaxTime);
4286 pelmHwChild->getAttributeValue("maxSize", hw.ulVideoCaptureMaxSize);
4287 pelmHwChild->getAttributeValue("options", hw.strVideoCaptureOptions);
4288 }
4289 else if (pelmHwChild->nameEquals("RemoteDisplay"))
4290 {
4291 pelmHwChild->getAttributeValue("enabled", hw.vrdeSettings.fEnabled);
4292
4293 Utf8Str str;
4294 if (pelmHwChild->getAttributeValue("port", str))
4295 hw.vrdeSettings.mapProperties["TCP/Ports"] = str;
4296 if (pelmHwChild->getAttributeValue("netAddress", str))
4297 hw.vrdeSettings.mapProperties["TCP/Address"] = str;
4298
4299 Utf8Str strAuthType;
4300 if (pelmHwChild->getAttributeValue("authType", strAuthType))
4301 {
4302 // settings before 1.3 used lower case so make sure this is case-insensitive
4303 strAuthType.toUpper();
4304 if (strAuthType == "NULL")
4305 hw.vrdeSettings.authType = AuthType_Null;
4306 else if (strAuthType == "GUEST")
4307 hw.vrdeSettings.authType = AuthType_Guest;
4308 else if (strAuthType == "EXTERNAL")
4309 hw.vrdeSettings.authType = AuthType_External;
4310 else
4311 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
4312 }
4313
4314 pelmHwChild->getAttributeValue("authLibrary", hw.vrdeSettings.strAuthLibrary);
4315 pelmHwChild->getAttributeValue("authTimeout", hw.vrdeSettings.ulAuthTimeout);
4316 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
4317 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
4318
4319 /* 3.2 and 4.0 betas, 4.0 has this information in VRDEProperties. */
4320 const xml::ElementNode *pelmVideoChannel;
4321 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
4322 {
4323 bool fVideoChannel = false;
4324 pelmVideoChannel->getAttributeValue("enabled", fVideoChannel);
4325 hw.vrdeSettings.mapProperties["VideoChannel/Enabled"] = fVideoChannel? "true": "false";
4326
4327 uint32_t ulVideoChannelQuality = 75;
4328 pelmVideoChannel->getAttributeValue("quality", ulVideoChannelQuality);
4329 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
4330 char *pszBuffer = NULL;
4331 if (RTStrAPrintf(&pszBuffer, "%d", ulVideoChannelQuality) >= 0)
4332 {
4333 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = pszBuffer;
4334 RTStrFree(pszBuffer);
4335 }
4336 else
4337 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = "75";
4338 }
4339 pelmHwChild->getAttributeValue("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
4340
4341 const xml::ElementNode *pelmProperties = pelmHwChild->findChildElement("VRDEProperties");
4342 if (pelmProperties != NULL)
4343 {
4344 xml::NodesLoop nl(*pelmProperties);
4345 const xml::ElementNode *pelmProperty;
4346 while ((pelmProperty = nl.forAllNodes()))
4347 {
4348 if (pelmProperty->nameEquals("Property"))
4349 {
4350 /* <Property name="TCP/Ports" value="3000-3002"/> */
4351 Utf8Str strName, strValue;
4352 if ( pelmProperty->getAttributeValue("name", strName)
4353 && pelmProperty->getAttributeValue("value", strValue))
4354 hw.vrdeSettings.mapProperties[strName] = strValue;
4355 else
4356 throw ConfigFileError(this, pelmProperty, N_("Required VRDE Property/@name or @value attribute is missing"));
4357 }
4358 }
4359 }
4360 }
4361 else if (pelmHwChild->nameEquals("BIOS"))
4362 {
4363 const xml::ElementNode *pelmBIOSChild;
4364 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
4365 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
4366 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
4367 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
4368 if ((pelmBIOSChild = pelmHwChild->findChildElement("APIC")))
4369 {
4370 Utf8Str strAPIC;
4371 if (pelmBIOSChild->getAttributeValue("mode", strAPIC))
4372 {
4373 strAPIC.toUpper();
4374 if (strAPIC == "DISABLED")
4375 hw.biosSettings.apicMode = APICMode_Disabled;
4376 else if (strAPIC == "APIC")
4377 hw.biosSettings.apicMode = APICMode_APIC;
4378 else if (strAPIC == "X2APIC")
4379 hw.biosSettings.apicMode = APICMode_X2APIC;
4380 else
4381 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in APIC/@mode attribute"), strAPIC.c_str());
4382 }
4383 }
4384 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
4385 {
4386 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
4387 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
4388 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
4389 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
4390 }
4391 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
4392 {
4393 Utf8Str strBootMenuMode;
4394 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
4395 {
4396 // settings before 1.3 used lower case so make sure this is case-insensitive
4397 strBootMenuMode.toUpper();
4398 if (strBootMenuMode == "DISABLED")
4399 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
4400 else if (strBootMenuMode == "MENUONLY")
4401 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
4402 else if (strBootMenuMode == "MESSAGEANDMENU")
4403 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
4404 else
4405 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
4406 }
4407 }
4408 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
4409 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
4410 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
4411 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
4412
4413 // legacy BIOS/IDEController (pre 1.7)
4414 if ( (m->sv < SettingsVersion_v1_7)
4415 && (pelmBIOSChild = pelmHwChild->findChildElement("IDEController"))
4416 )
4417 {
4418 StorageController sctl;
4419 sctl.strName = "IDE Controller";
4420 sctl.storageBus = StorageBus_IDE;
4421
4422 Utf8Str strType;
4423 if (pelmBIOSChild->getAttributeValue("type", strType))
4424 {
4425 if (strType == "PIIX3")
4426 sctl.controllerType = StorageControllerType_PIIX3;
4427 else if (strType == "PIIX4")
4428 sctl.controllerType = StorageControllerType_PIIX4;
4429 else if (strType == "ICH6")
4430 sctl.controllerType = StorageControllerType_ICH6;
4431 else
4432 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
4433 }
4434 sctl.ulPortCount = 2;
4435 hw.storage.llStorageControllers.push_back(sctl);
4436 }
4437 }
4438 else if ( (m->sv <= SettingsVersion_v1_14)
4439 && pelmHwChild->nameEquals("USBController"))
4440 {
4441 bool fEnabled = false;
4442
4443 pelmHwChild->getAttributeValue("enabled", fEnabled);
4444 if (fEnabled)
4445 {
4446 /* Create OHCI controller with default name. */
4447 USBController ctrl;
4448
4449 ctrl.strName = "OHCI";
4450 ctrl.enmType = USBControllerType_OHCI;
4451 hw.usbSettings.llUSBControllers.push_back(ctrl);
4452 }
4453
4454 pelmHwChild->getAttributeValue("enabledEhci", fEnabled);
4455 if (fEnabled)
4456 {
4457 /* Create OHCI controller with default name. */
4458 USBController ctrl;
4459
4460 ctrl.strName = "EHCI";
4461 ctrl.enmType = USBControllerType_EHCI;
4462 hw.usbSettings.llUSBControllers.push_back(ctrl);
4463 }
4464
4465 readUSBDeviceFilters(*pelmHwChild,
4466 hw.usbSettings.llDeviceFilters);
4467 }
4468 else if (pelmHwChild->nameEquals("USB"))
4469 {
4470 const xml::ElementNode *pelmUSBChild;
4471
4472 if ((pelmUSBChild = pelmHwChild->findChildElement("Controllers")))
4473 {
4474 xml::NodesLoop nl2(*pelmUSBChild, "Controller");
4475 const xml::ElementNode *pelmCtrl;
4476
4477 while ((pelmCtrl = nl2.forAllNodes()))
4478 {
4479 USBController ctrl;
4480 com::Utf8Str strCtrlType;
4481
4482 pelmCtrl->getAttributeValue("name", ctrl.strName);
4483
4484 if (pelmCtrl->getAttributeValue("type", strCtrlType))
4485 {
4486 if (strCtrlType == "OHCI")
4487 ctrl.enmType = USBControllerType_OHCI;
4488 else if (strCtrlType == "EHCI")
4489 ctrl.enmType = USBControllerType_EHCI;
4490 else if (strCtrlType == "XHCI")
4491 ctrl.enmType = USBControllerType_XHCI;
4492 else
4493 throw ConfigFileError(this, pelmCtrl, N_("Invalid value '%s' for Controller/@type attribute"), strCtrlType.c_str());
4494 }
4495
4496 hw.usbSettings.llUSBControllers.push_back(ctrl);
4497 }
4498 }
4499
4500 if ((pelmUSBChild = pelmHwChild->findChildElement("DeviceFilters")))
4501 readUSBDeviceFilters(*pelmUSBChild, hw.usbSettings.llDeviceFilters);
4502 }
4503 else if ( m->sv < SettingsVersion_v1_7
4504 && pelmHwChild->nameEquals("SATAController"))
4505 {
4506 bool f;
4507 if ( pelmHwChild->getAttributeValue("enabled", f)
4508 && f)
4509 {
4510 StorageController sctl;
4511 sctl.strName = "SATA Controller";
4512 sctl.storageBus = StorageBus_SATA;
4513 sctl.controllerType = StorageControllerType_IntelAhci;
4514
4515 readStorageControllerAttributes(*pelmHwChild, sctl);
4516
4517 hw.storage.llStorageControllers.push_back(sctl);
4518 }
4519 }
4520 else if (pelmHwChild->nameEquals("Network"))
4521 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
4522 else if (pelmHwChild->nameEquals("RTC"))
4523 {
4524 Utf8Str strLocalOrUTC;
4525 machineUserData.fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
4526 && strLocalOrUTC == "UTC";
4527 }
4528 else if ( pelmHwChild->nameEquals("UART")
4529 || pelmHwChild->nameEquals("Uart") // used before 1.3
4530 )
4531 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
4532 else if ( pelmHwChild->nameEquals("LPT")
4533 || pelmHwChild->nameEquals("Lpt") // used before 1.3
4534 )
4535 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
4536 else if (pelmHwChild->nameEquals("AudioAdapter"))
4537 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
4538 else if (pelmHwChild->nameEquals("SharedFolders"))
4539 {
4540 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
4541 const xml::ElementNode *pelmFolder;
4542 while ((pelmFolder = nl2.forAllNodes()))
4543 {
4544 SharedFolder sf;
4545 pelmFolder->getAttributeValue("name", sf.strName);
4546 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
4547 pelmFolder->getAttributeValue("writable", sf.fWritable);
4548 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
4549 hw.llSharedFolders.push_back(sf);
4550 }
4551 }
4552 else if (pelmHwChild->nameEquals("Clipboard"))
4553 {
4554 Utf8Str strTemp;
4555 if (pelmHwChild->getAttributeValue("mode", strTemp))
4556 {
4557 if (strTemp == "Disabled")
4558 hw.clipboardMode = ClipboardMode_Disabled;
4559 else if (strTemp == "HostToGuest")
4560 hw.clipboardMode = ClipboardMode_HostToGuest;
4561 else if (strTemp == "GuestToHost")
4562 hw.clipboardMode = ClipboardMode_GuestToHost;
4563 else if (strTemp == "Bidirectional")
4564 hw.clipboardMode = ClipboardMode_Bidirectional;
4565 else
4566 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
4567 }
4568 }
4569 else if (pelmHwChild->nameEquals("DragAndDrop"))
4570 {
4571 Utf8Str strTemp;
4572 if (pelmHwChild->getAttributeValue("mode", strTemp))
4573 {
4574 if (strTemp == "Disabled")
4575 hw.dndMode = DnDMode_Disabled;
4576 else if (strTemp == "HostToGuest")
4577 hw.dndMode = DnDMode_HostToGuest;
4578 else if (strTemp == "GuestToHost")
4579 hw.dndMode = DnDMode_GuestToHost;
4580 else if (strTemp == "Bidirectional")
4581 hw.dndMode = DnDMode_Bidirectional;
4582 else
4583 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in DragAndDrop/@mode attribute"), strTemp.c_str());
4584 }
4585 }
4586 else if (pelmHwChild->nameEquals("Guest"))
4587 {
4588 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
4589 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
4590 }
4591 else if (pelmHwChild->nameEquals("GuestProperties"))
4592 readGuestProperties(*pelmHwChild, hw);
4593 else if (pelmHwChild->nameEquals("IO"))
4594 {
4595 const xml::ElementNode *pelmBwGroups;
4596 const xml::ElementNode *pelmIOChild;
4597
4598 if ((pelmIOChild = pelmHwChild->findChildElement("IoCache")))
4599 {
4600 pelmIOChild->getAttributeValue("enabled", hw.ioSettings.fIOCacheEnabled);
4601 pelmIOChild->getAttributeValue("size", hw.ioSettings.ulIOCacheSize);
4602 }
4603
4604 if ((pelmBwGroups = pelmHwChild->findChildElement("BandwidthGroups")))
4605 {
4606 xml::NodesLoop nl2(*pelmBwGroups, "BandwidthGroup");
4607 const xml::ElementNode *pelmBandwidthGroup;
4608 while ((pelmBandwidthGroup = nl2.forAllNodes()))
4609 {
4610 BandwidthGroup gr;
4611 Utf8Str strTemp;
4612
4613 pelmBandwidthGroup->getAttributeValue("name", gr.strName);
4614
4615 if (pelmBandwidthGroup->getAttributeValue("type", strTemp))
4616 {
4617 if (strTemp == "Disk")
4618 gr.enmType = BandwidthGroupType_Disk;
4619 else if (strTemp == "Network")
4620 gr.enmType = BandwidthGroupType_Network;
4621 else
4622 throw ConfigFileError(this, pelmBandwidthGroup, N_("Invalid value '%s' in BandwidthGroup/@type attribute"), strTemp.c_str());
4623 }
4624 else
4625 throw ConfigFileError(this, pelmBandwidthGroup, N_("Missing BandwidthGroup/@type attribute"));
4626
4627 if (!pelmBandwidthGroup->getAttributeValue("maxBytesPerSec", gr.cMaxBytesPerSec))
4628 {
4629 pelmBandwidthGroup->getAttributeValue("maxMbPerSec", gr.cMaxBytesPerSec);
4630 gr.cMaxBytesPerSec *= _1M;
4631 }
4632 hw.ioSettings.llBandwidthGroups.push_back(gr);
4633 }
4634 }
4635 }
4636 else if (pelmHwChild->nameEquals("HostPci"))
4637 {
4638 const xml::ElementNode *pelmDevices;
4639
4640 if ((pelmDevices = pelmHwChild->findChildElement("Devices")))
4641 {
4642 xml::NodesLoop nl2(*pelmDevices, "Device");
4643 const xml::ElementNode *pelmDevice;
4644 while ((pelmDevice = nl2.forAllNodes()))
4645 {
4646 HostPCIDeviceAttachment hpda;
4647
4648 if (!pelmDevice->getAttributeValue("host", hpda.uHostAddress))
4649 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@host attribute"));
4650
4651 if (!pelmDevice->getAttributeValue("guest", hpda.uGuestAddress))
4652 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@guest attribute"));
4653
4654 /* name is optional */
4655 pelmDevice->getAttributeValue("name", hpda.strDeviceName);
4656
4657 hw.pciAttachments.push_back(hpda);
4658 }
4659 }
4660 }
4661 else if (pelmHwChild->nameEquals("EmulatedUSB"))
4662 {
4663 const xml::ElementNode *pelmCardReader;
4664
4665 if ((pelmCardReader = pelmHwChild->findChildElement("CardReader")))
4666 {
4667 pelmCardReader->getAttributeValue("enabled", hw.fEmulatedUSBCardReader);
4668 }
4669 }
4670 else if (pelmHwChild->nameEquals("Frontend"))
4671 {
4672 const xml::ElementNode *pelmDefault;
4673
4674 if ((pelmDefault = pelmHwChild->findChildElement("Default")))
4675 {
4676 pelmDefault->getAttributeValue("type", hw.strDefaultFrontend);
4677 }
4678 }
4679 else if (pelmHwChild->nameEquals("StorageControllers"))
4680 readStorageControllers(*pelmHwChild, hw.storage);
4681 }
4682
4683 if (hw.ulMemorySizeMB == (uint32_t)-1)
4684 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
4685}
4686
4687/**
4688 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
4689 * files which have a \<HardDiskAttachments\> node and storage controller settings
4690 * hidden in the \<Hardware\> settings. We set the StorageControllers fields just the
4691 * same, just from different sources.
4692 * @param elmHardDiskAttachments \<HardDiskAttachments\> XML node.
4693 * @param strg
4694 */
4695void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
4696 Storage &strg)
4697{
4698 StorageController *pIDEController = NULL;
4699 StorageController *pSATAController = NULL;
4700
4701 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
4702 it != strg.llStorageControllers.end();
4703 ++it)
4704 {
4705 StorageController &s = *it;
4706 if (s.storageBus == StorageBus_IDE)
4707 pIDEController = &s;
4708 else if (s.storageBus == StorageBus_SATA)
4709 pSATAController = &s;
4710 }
4711
4712 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
4713 const xml::ElementNode *pelmAttachment;
4714 while ((pelmAttachment = nl1.forAllNodes()))
4715 {
4716 AttachedDevice att;
4717 Utf8Str strUUID, strBus;
4718
4719 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
4720 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
4721 parseUUID(att.uuid, strUUID, pelmAttachment);
4722
4723 if (!pelmAttachment->getAttributeValue("bus", strBus))
4724 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
4725 // pre-1.7 'channel' is now port
4726 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
4727 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
4728 // pre-1.7 'device' is still device
4729 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
4730 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
4731
4732 att.deviceType = DeviceType_HardDisk;
4733
4734 if (strBus == "IDE")
4735 {
4736 if (!pIDEController)
4737 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
4738 pIDEController->llAttachedDevices.push_back(att);
4739 }
4740 else if (strBus == "SATA")
4741 {
4742 if (!pSATAController)
4743 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
4744 pSATAController->llAttachedDevices.push_back(att);
4745 }
4746 else
4747 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
4748 }
4749}
4750
4751/**
4752 * Reads in a \<StorageControllers\> block and stores it in the given Storage structure.
4753 * Used both directly from readMachine and from readSnapshot, since snapshots
4754 * have their own storage controllers sections.
4755 *
4756 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
4757 * for earlier versions.
4758 *
4759 * @param elmStorageControllers
4760 * @param strg
4761 */
4762void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
4763 Storage &strg)
4764{
4765 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
4766 const xml::ElementNode *pelmController;
4767 while ((pelmController = nlStorageControllers.forAllNodes()))
4768 {
4769 StorageController sctl;
4770
4771 if (!pelmController->getAttributeValue("name", sctl.strName))
4772 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
4773 // canonicalize storage controller names for configs in the switchover
4774 // period.
4775 if (m->sv < SettingsVersion_v1_9)
4776 {
4777 if (sctl.strName == "IDE")
4778 sctl.strName = "IDE Controller";
4779 else if (sctl.strName == "SATA")
4780 sctl.strName = "SATA Controller";
4781 else if (sctl.strName == "SCSI")
4782 sctl.strName = "SCSI Controller";
4783 }
4784
4785 pelmController->getAttributeValue("Instance", sctl.ulInstance);
4786 // default from constructor is 0
4787
4788 pelmController->getAttributeValue("Bootable", sctl.fBootable);
4789 // default from constructor is true which is true
4790 // for settings below version 1.11 because they allowed only
4791 // one controller per type.
4792
4793 Utf8Str strType;
4794 if (!pelmController->getAttributeValue("type", strType))
4795 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
4796
4797 if (strType == "AHCI")
4798 {
4799 sctl.storageBus = StorageBus_SATA;
4800 sctl.controllerType = StorageControllerType_IntelAhci;
4801 }
4802 else if (strType == "LsiLogic")
4803 {
4804 sctl.storageBus = StorageBus_SCSI;
4805 sctl.controllerType = StorageControllerType_LsiLogic;
4806 }
4807 else if (strType == "BusLogic")
4808 {
4809 sctl.storageBus = StorageBus_SCSI;
4810 sctl.controllerType = StorageControllerType_BusLogic;
4811 }
4812 else if (strType == "PIIX3")
4813 {
4814 sctl.storageBus = StorageBus_IDE;
4815 sctl.controllerType = StorageControllerType_PIIX3;
4816 }
4817 else if (strType == "PIIX4")
4818 {
4819 sctl.storageBus = StorageBus_IDE;
4820 sctl.controllerType = StorageControllerType_PIIX4;
4821 }
4822 else if (strType == "ICH6")
4823 {
4824 sctl.storageBus = StorageBus_IDE;
4825 sctl.controllerType = StorageControllerType_ICH6;
4826 }
4827 else if ( (m->sv >= SettingsVersion_v1_9)
4828 && (strType == "I82078")
4829 )
4830 {
4831 sctl.storageBus = StorageBus_Floppy;
4832 sctl.controllerType = StorageControllerType_I82078;
4833 }
4834 else if (strType == "LsiLogicSas")
4835 {
4836 sctl.storageBus = StorageBus_SAS;
4837 sctl.controllerType = StorageControllerType_LsiLogicSas;
4838 }
4839 else if (strType == "USB")
4840 {
4841 sctl.storageBus = StorageBus_USB;
4842 sctl.controllerType = StorageControllerType_USB;
4843 }
4844 else if (strType == "NVMe")
4845 {
4846 sctl.storageBus = StorageBus_PCIe;
4847 sctl.controllerType = StorageControllerType_NVMe;
4848 }
4849 else
4850 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
4851
4852 readStorageControllerAttributes(*pelmController, sctl);
4853
4854 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
4855 const xml::ElementNode *pelmAttached;
4856 while ((pelmAttached = nlAttached.forAllNodes()))
4857 {
4858 AttachedDevice att;
4859 Utf8Str strTemp;
4860 pelmAttached->getAttributeValue("type", strTemp);
4861
4862 att.fDiscard = false;
4863 att.fNonRotational = false;
4864 att.fHotPluggable = false;
4865 att.fPassThrough = false;
4866
4867 if (strTemp == "HardDisk")
4868 {
4869 att.deviceType = DeviceType_HardDisk;
4870 pelmAttached->getAttributeValue("nonrotational", att.fNonRotational);
4871 pelmAttached->getAttributeValue("discard", att.fDiscard);
4872 }
4873 else if (m->sv >= SettingsVersion_v1_9)
4874 {
4875 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
4876 if (strTemp == "DVD")
4877 {
4878 att.deviceType = DeviceType_DVD;
4879 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
4880 pelmAttached->getAttributeValue("tempeject", att.fTempEject);
4881 }
4882 else if (strTemp == "Floppy")
4883 att.deviceType = DeviceType_Floppy;
4884 }
4885
4886 if (att.deviceType != DeviceType_Null)
4887 {
4888 const xml::ElementNode *pelmImage;
4889 // all types can have images attached, but for HardDisk it's required
4890 if (!(pelmImage = pelmAttached->findChildElement("Image")))
4891 {
4892 if (att.deviceType == DeviceType_HardDisk)
4893 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
4894 else
4895 {
4896 // DVDs and floppies can also have <HostDrive> instead of <Image>
4897 const xml::ElementNode *pelmHostDrive;
4898 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
4899 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
4900 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
4901 }
4902 }
4903 else
4904 {
4905 if (!pelmImage->getAttributeValue("uuid", strTemp))
4906 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
4907 parseUUID(att.uuid, strTemp, pelmImage);
4908 }
4909
4910 if (!pelmAttached->getAttributeValue("port", att.lPort))
4911 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
4912 if (!pelmAttached->getAttributeValue("device", att.lDevice))
4913 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
4914
4915 /* AHCI controller ports are hotpluggable by default, keep compatibility with existing settings. */
4916 if (m->sv >= SettingsVersion_v1_15)
4917 pelmAttached->getAttributeValue("hotpluggable", att.fHotPluggable);
4918 else if (sctl.controllerType == StorageControllerType_IntelAhci)
4919 att.fHotPluggable = true;
4920
4921 pelmAttached->getAttributeValue("bandwidthGroup", att.strBwGroup);
4922 sctl.llAttachedDevices.push_back(att);
4923 }
4924 }
4925
4926 strg.llStorageControllers.push_back(sctl);
4927 }
4928}
4929
4930/**
4931 * This gets called for legacy pre-1.9 settings files after having parsed the
4932 * \<Hardware\> and \<StorageControllers\> sections to parse \<Hardware\> once more
4933 * for the \<DVDDrive\> and \<FloppyDrive\> sections.
4934 *
4935 * Before settings version 1.9, DVD and floppy drives were specified separately
4936 * under \<Hardware\>; we then need this extra loop to make sure the storage
4937 * controller structs are already set up so we can add stuff to them.
4938 *
4939 * @param elmHardware
4940 * @param strg
4941 */
4942void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
4943 Storage &strg)
4944{
4945 xml::NodesLoop nl1(elmHardware);
4946 const xml::ElementNode *pelmHwChild;
4947 while ((pelmHwChild = nl1.forAllNodes()))
4948 {
4949 if (pelmHwChild->nameEquals("DVDDrive"))
4950 {
4951 // create a DVD "attached device" and attach it to the existing IDE controller
4952 AttachedDevice att;
4953 att.deviceType = DeviceType_DVD;
4954 // legacy DVD drive is always secondary master (port 1, device 0)
4955 att.lPort = 1;
4956 att.lDevice = 0;
4957 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
4958 pelmHwChild->getAttributeValue("tempeject", att.fTempEject);
4959
4960 const xml::ElementNode *pDriveChild;
4961 Utf8Str strTmp;
4962 if ( (pDriveChild = pelmHwChild->findChildElement("Image")) != NULL
4963 && pDriveChild->getAttributeValue("uuid", strTmp))
4964 parseUUID(att.uuid, strTmp, pDriveChild);
4965 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
4966 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
4967
4968 // find the IDE controller and attach the DVD drive
4969 bool fFound = false;
4970 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
4971 it != strg.llStorageControllers.end();
4972 ++it)
4973 {
4974 StorageController &sctl = *it;
4975 if (sctl.storageBus == StorageBus_IDE)
4976 {
4977 sctl.llAttachedDevices.push_back(att);
4978 fFound = true;
4979 break;
4980 }
4981 }
4982
4983 if (!fFound)
4984 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
4985 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
4986 // which should have gotten parsed in <StorageControllers> before this got called
4987 }
4988 else if (pelmHwChild->nameEquals("FloppyDrive"))
4989 {
4990 bool fEnabled;
4991 if ( pelmHwChild->getAttributeValue("enabled", fEnabled)
4992 && fEnabled)
4993 {
4994 // create a new floppy controller and attach a floppy "attached device"
4995 StorageController sctl;
4996 sctl.strName = "Floppy Controller";
4997 sctl.storageBus = StorageBus_Floppy;
4998 sctl.controllerType = StorageControllerType_I82078;
4999 sctl.ulPortCount = 1;
5000
5001 AttachedDevice att;
5002 att.deviceType = DeviceType_Floppy;
5003 att.lPort = 0;
5004 att.lDevice = 0;
5005
5006 const xml::ElementNode *pDriveChild;
5007 Utf8Str strTmp;
5008 if ( (pDriveChild = pelmHwChild->findChildElement("Image"))
5009 && pDriveChild->getAttributeValue("uuid", strTmp) )
5010 parseUUID(att.uuid, strTmp, pDriveChild);
5011 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
5012 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
5013
5014 // store attachment with controller
5015 sctl.llAttachedDevices.push_back(att);
5016 // store controller with storage
5017 strg.llStorageControllers.push_back(sctl);
5018 }
5019 }
5020 }
5021}
5022
5023/**
5024 * Called for reading the \<Teleporter\> element under \<Machine\>.
5025 */
5026void MachineConfigFile::readTeleporter(const xml::ElementNode *pElmTeleporter,
5027 MachineUserData *pUserData)
5028{
5029 pElmTeleporter->getAttributeValue("enabled", pUserData->fTeleporterEnabled);
5030 pElmTeleporter->getAttributeValue("port", pUserData->uTeleporterPort);
5031 pElmTeleporter->getAttributeValue("address", pUserData->strTeleporterAddress);
5032 pElmTeleporter->getAttributeValue("password", pUserData->strTeleporterPassword);
5033
5034 if ( pUserData->strTeleporterPassword.isNotEmpty()
5035 && !VBoxIsPasswordHashed(&pUserData->strTeleporterPassword))
5036 VBoxHashPassword(&pUserData->strTeleporterPassword);
5037}
5038
5039/**
5040 * Called for reading the \<Debugging\> element under \<Machine\> or \<Snapshot\>.
5041 */
5042void MachineConfigFile::readDebugging(const xml::ElementNode *pElmDebugging, Debugging *pDbg)
5043{
5044 if (!pElmDebugging || m->sv < SettingsVersion_v1_13)
5045 return;
5046
5047 const xml::ElementNode * const pelmTracing = pElmDebugging->findChildElement("Tracing");
5048 if (pelmTracing)
5049 {
5050 pelmTracing->getAttributeValue("enabled", pDbg->fTracingEnabled);
5051 pelmTracing->getAttributeValue("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
5052 pelmTracing->getAttributeValue("config", pDbg->strTracingConfig);
5053 }
5054}
5055
5056/**
5057 * Called for reading the \<Autostart\> element under \<Machine\> or \<Snapshot\>.
5058 */
5059void MachineConfigFile::readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart)
5060{
5061 Utf8Str strAutostop;
5062
5063 if (!pElmAutostart || m->sv < SettingsVersion_v1_13)
5064 return;
5065
5066 pElmAutostart->getAttributeValue("enabled", pAutostart->fAutostartEnabled);
5067 pElmAutostart->getAttributeValue("delay", pAutostart->uAutostartDelay);
5068 pElmAutostart->getAttributeValue("autostop", strAutostop);
5069 if (strAutostop == "Disabled")
5070 pAutostart->enmAutostopType = AutostopType_Disabled;
5071 else if (strAutostop == "SaveState")
5072 pAutostart->enmAutostopType = AutostopType_SaveState;
5073 else if (strAutostop == "PowerOff")
5074 pAutostart->enmAutostopType = AutostopType_PowerOff;
5075 else if (strAutostop == "AcpiShutdown")
5076 pAutostart->enmAutostopType = AutostopType_AcpiShutdown;
5077 else
5078 throw ConfigFileError(this, pElmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str());
5079}
5080
5081/**
5082 * Called for reading the \<Groups\> element under \<Machine\>.
5083 */
5084void MachineConfigFile::readGroups(const xml::ElementNode *pElmGroups, StringsList *pllGroups)
5085{
5086 pllGroups->clear();
5087 if (!pElmGroups || m->sv < SettingsVersion_v1_13)
5088 {
5089 pllGroups->push_back("/");
5090 return;
5091 }
5092
5093 xml::NodesLoop nlGroups(*pElmGroups);
5094 const xml::ElementNode *pelmGroup;
5095 while ((pelmGroup = nlGroups.forAllNodes()))
5096 {
5097 if (pelmGroup->nameEquals("Group"))
5098 {
5099 Utf8Str strGroup;
5100 if (!pelmGroup->getAttributeValue("name", strGroup))
5101 throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing"));
5102 pllGroups->push_back(strGroup);
5103 }
5104 }
5105}
5106
5107/**
5108 * Called initially for the \<Snapshot\> element under \<Machine\>, if present,
5109 * to store the snapshot's data into the given Snapshot structure (which is
5110 * then the one in the Machine struct). This might then recurse if
5111 * a \<Snapshots\> (plural) element is found in the snapshot, which should
5112 * contain a list of child snapshots; such lists are maintained in the
5113 * Snapshot structure.
5114 *
5115 * @param curSnapshotUuid
5116 * @param depth
5117 * @param elmSnapshot
5118 * @param snap
5119 * @returns true if curSnapshotUuid is in this snapshot subtree, otherwise false
5120 */
5121bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid,
5122 uint32_t depth,
5123 const xml::ElementNode &elmSnapshot,
5124 Snapshot &snap)
5125{
5126 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
5127 throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
5128
5129 Utf8Str strTemp;
5130
5131 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
5132 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
5133 parseUUID(snap.uuid, strTemp, &elmSnapshot);
5134 bool foundCurrentSnapshot = (snap.uuid == curSnapshotUuid);
5135
5136 if (!elmSnapshot.getAttributeValue("name", snap.strName))
5137 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
5138
5139 // 3.1 dev builds added Description as an attribute, read it silently
5140 // and write it back as an element
5141 elmSnapshot.getAttributeValue("Description", snap.strDescription);
5142
5143 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
5144 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
5145 parseTimestamp(snap.timestamp, strTemp, &elmSnapshot);
5146
5147 elmSnapshot.getAttributeValuePath("stateFile", snap.strStateFile); // online snapshots only
5148
5149 // parse Hardware before the other elements because other things depend on it
5150 const xml::ElementNode *pelmHardware;
5151 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
5152 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
5153 readHardware(*pelmHardware, snap.hardware);
5154
5155 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
5156 const xml::ElementNode *pelmSnapshotChild;
5157 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
5158 {
5159 if (pelmSnapshotChild->nameEquals("Description"))
5160 snap.strDescription = pelmSnapshotChild->getValue();
5161 else if ( m->sv < SettingsVersion_v1_7
5162 && pelmSnapshotChild->nameEquals("HardDiskAttachments"))
5163 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.hardware.storage);
5164 else if ( m->sv >= SettingsVersion_v1_7
5165 && pelmSnapshotChild->nameEquals("StorageControllers"))
5166 readStorageControllers(*pelmSnapshotChild, snap.hardware.storage);
5167 else if (pelmSnapshotChild->nameEquals("Snapshots"))
5168 {
5169 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
5170 const xml::ElementNode *pelmChildSnapshot;
5171 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
5172 {
5173 if (pelmChildSnapshot->nameEquals("Snapshot"))
5174 {
5175 // recurse with this element and put the child at the
5176 // end of the list. XPCOM has very small stack, avoid
5177 // big local variables and use the list element.
5178 snap.llChildSnapshots.push_back(Snapshot::Empty);
5179 bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, snap.llChildSnapshots.back());
5180 foundCurrentSnapshot = foundCurrentSnapshot || found;
5181 }
5182 }
5183 }
5184 }
5185
5186 if (m->sv < SettingsVersion_v1_9)
5187 // go through Hardware once more to repair the settings controller structures
5188 // with data from old DVDDrive and FloppyDrive elements
5189 readDVDAndFloppies_pre1_9(*pelmHardware, snap.hardware.storage);
5190
5191 readDebugging(elmSnapshot.findChildElement("Debugging"), &snap.debugging);
5192 readAutostart(elmSnapshot.findChildElement("Autostart"), &snap.autostart);
5193 // note: Groups exist only for Machine, not for Snapshot
5194
5195 return foundCurrentSnapshot;
5196}
5197
5198const struct {
5199 const char *pcszOld;
5200 const char *pcszNew;
5201} aConvertOSTypes[] =
5202{
5203 { "unknown", "Other" },
5204 { "dos", "DOS" },
5205 { "win31", "Windows31" },
5206 { "win95", "Windows95" },
5207 { "win98", "Windows98" },
5208 { "winme", "WindowsMe" },
5209 { "winnt4", "WindowsNT4" },
5210 { "win2k", "Windows2000" },
5211 { "winxp", "WindowsXP" },
5212 { "win2k3", "Windows2003" },
5213 { "winvista", "WindowsVista" },
5214 { "win2k8", "Windows2008" },
5215 { "os2warp3", "OS2Warp3" },
5216 { "os2warp4", "OS2Warp4" },
5217 { "os2warp45", "OS2Warp45" },
5218 { "ecs", "OS2eCS" },
5219 { "linux22", "Linux22" },
5220 { "linux24", "Linux24" },
5221 { "linux26", "Linux26" },
5222 { "archlinux", "ArchLinux" },
5223 { "debian", "Debian" },
5224 { "opensuse", "OpenSUSE" },
5225 { "fedoracore", "Fedora" },
5226 { "gentoo", "Gentoo" },
5227 { "mandriva", "Mandriva" },
5228 { "redhat", "RedHat" },
5229 { "ubuntu", "Ubuntu" },
5230 { "xandros", "Xandros" },
5231 { "freebsd", "FreeBSD" },
5232 { "openbsd", "OpenBSD" },
5233 { "netbsd", "NetBSD" },
5234 { "netware", "Netware" },
5235 { "solaris", "Solaris" },
5236 { "opensolaris", "OpenSolaris" },
5237 { "l4", "L4" }
5238};
5239
5240void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
5241{
5242 for (unsigned u = 0;
5243 u < RT_ELEMENTS(aConvertOSTypes);
5244 ++u)
5245 {
5246 if (str == aConvertOSTypes[u].pcszOld)
5247 {
5248 str = aConvertOSTypes[u].pcszNew;
5249 break;
5250 }
5251 }
5252}
5253
5254/**
5255 * Called from the constructor to actually read in the \<Machine\> element
5256 * of a machine config file.
5257 * @param elmMachine
5258 */
5259void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
5260{
5261 Utf8Str strUUID;
5262 if ( elmMachine.getAttributeValue("uuid", strUUID)
5263 && elmMachine.getAttributeValue("name", machineUserData.strName))
5264 {
5265 parseUUID(uuid, strUUID, &elmMachine);
5266
5267 elmMachine.getAttributeValue("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
5268 elmMachine.getAttributeValue("nameSync", machineUserData.fNameSync);
5269
5270 Utf8Str str;
5271 elmMachine.getAttributeValue("Description", machineUserData.strDescription);
5272 elmMachine.getAttributeValue("OSType", machineUserData.strOsType);
5273 if (m->sv < SettingsVersion_v1_5)
5274 convertOldOSType_pre1_5(machineUserData.strOsType);
5275
5276 elmMachine.getAttributeValuePath("stateFile", strStateFile);
5277
5278 if (elmMachine.getAttributeValue("currentSnapshot", str))
5279 parseUUID(uuidCurrentSnapshot, str, &elmMachine);
5280
5281 elmMachine.getAttributeValuePath("snapshotFolder", machineUserData.strSnapshotFolder);
5282
5283 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
5284 fCurrentStateModified = true;
5285 if (elmMachine.getAttributeValue("lastStateChange", str))
5286 parseTimestamp(timeLastStateChange, str, &elmMachine);
5287 // constructor has called RTTimeNow(&timeLastStateChange) before
5288 if (elmMachine.getAttributeValue("aborted", fAborted))
5289 fAborted = true;
5290
5291 elmMachine.getAttributeValue("processPriority", machineUserData.strVMPriority);
5292
5293 str.setNull();
5294 elmMachine.getAttributeValue("icon", str);
5295 parseBase64(machineUserData.ovIcon, str, &elmMachine);
5296
5297 // parse Hardware before the other elements because other things depend on it
5298 const xml::ElementNode *pelmHardware;
5299 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
5300 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
5301 readHardware(*pelmHardware, hardwareMachine);
5302
5303 xml::NodesLoop nlRootChildren(elmMachine);
5304 const xml::ElementNode *pelmMachineChild;
5305 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
5306 {
5307 if (pelmMachineChild->nameEquals("ExtraData"))
5308 readExtraData(*pelmMachineChild,
5309 mapExtraDataItems);
5310 else if ( (m->sv < SettingsVersion_v1_7)
5311 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
5312 )
5313 readHardDiskAttachments_pre1_7(*pelmMachineChild, hardwareMachine.storage);
5314 else if ( (m->sv >= SettingsVersion_v1_7)
5315 && (pelmMachineChild->nameEquals("StorageControllers"))
5316 )
5317 readStorageControllers(*pelmMachineChild, hardwareMachine.storage);
5318 else if (pelmMachineChild->nameEquals("Snapshot"))
5319 {
5320 if (uuidCurrentSnapshot.isZero())
5321 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing"));
5322 bool foundCurrentSnapshot = false;
5323 Snapshot snap;
5324 // this will recurse into child snapshots, if necessary
5325 foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, 1, *pelmMachineChild, snap);
5326 if (!foundCurrentSnapshot)
5327 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute"));
5328 llFirstSnapshot.push_back(snap);
5329 }
5330 else if (pelmMachineChild->nameEquals("Description"))
5331 machineUserData.strDescription = pelmMachineChild->getValue();
5332 else if (pelmMachineChild->nameEquals("Teleporter"))
5333 readTeleporter(pelmMachineChild, &machineUserData);
5334 else if (pelmMachineChild->nameEquals("FaultTolerance"))
5335 {
5336 Utf8Str strFaultToleranceSate;
5337 if (pelmMachineChild->getAttributeValue("state", strFaultToleranceSate))
5338 {
5339 if (strFaultToleranceSate == "master")
5340 machineUserData.enmFaultToleranceState = FaultToleranceState_Master;
5341 else
5342 if (strFaultToleranceSate == "standby")
5343 machineUserData.enmFaultToleranceState = FaultToleranceState_Standby;
5344 else
5345 machineUserData.enmFaultToleranceState = FaultToleranceState_Inactive;
5346 }
5347 pelmMachineChild->getAttributeValue("port", machineUserData.uFaultTolerancePort);
5348 pelmMachineChild->getAttributeValue("address", machineUserData.strFaultToleranceAddress);
5349 pelmMachineChild->getAttributeValue("interval", machineUserData.uFaultToleranceInterval);
5350 pelmMachineChild->getAttributeValue("password", machineUserData.strFaultTolerancePassword);
5351 }
5352 else if (pelmMachineChild->nameEquals("MediaRegistry"))
5353 readMediaRegistry(*pelmMachineChild, mediaRegistry);
5354 else if (pelmMachineChild->nameEquals("Debugging"))
5355 readDebugging(pelmMachineChild, &debugging);
5356 else if (pelmMachineChild->nameEquals("Autostart"))
5357 readAutostart(pelmMachineChild, &autostart);
5358 else if (pelmMachineChild->nameEquals("Groups"))
5359 readGroups(pelmMachineChild, &machineUserData.llGroups);
5360 }
5361
5362 if (m->sv < SettingsVersion_v1_9)
5363 // go through Hardware once more to repair the settings controller structures
5364 // with data from old DVDDrive and FloppyDrive elements
5365 readDVDAndFloppies_pre1_9(*pelmHardware, hardwareMachine.storage);
5366 }
5367 else
5368 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
5369}
5370
5371/**
5372 * Creates a \<Hardware\> node under elmParent and then writes out the XML
5373 * keys under that. Called for both the \<Machine\> node and for snapshots.
5374 * @param elmParent
5375 * @param hw
5376 * @param fl
5377 * @param pllElementsWithUuidAttributes
5378 */
5379void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
5380 const Hardware &hw,
5381 uint32_t fl,
5382 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
5383{
5384 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
5385
5386 if ( m->sv >= SettingsVersion_v1_4
5387 && (m->sv < SettingsVersion_v1_7 ? hw.strVersion != "1" : hw.strVersion != "2"))
5388 pelmHardware->setAttribute("version", hw.strVersion);
5389
5390 if ((m->sv >= SettingsVersion_v1_9)
5391 && !hw.uuid.isZero()
5392 && hw.uuid.isValid()
5393 )
5394 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
5395
5396 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
5397
5398 if (!hw.fHardwareVirt)
5399 pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
5400 if (!hw.fNestedPaging)
5401 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
5402 if (!hw.fVPID)
5403 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
5404 if (!hw.fUnrestrictedExecution)
5405 pelmCPU->createChild("HardwareVirtExUX")->setAttribute("enabled", hw.fUnrestrictedExecution);
5406 // PAE has too crazy default handling, must always save this setting.
5407 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
5408 if (m->sv >= SettingsVersion_v1_16)
5409 {
5410 if (hw.fIBPBOnVMEntry || hw.fIBPBOnVMExit)
5411 {
5412 xml::ElementNode *pelmChild = pelmCPU->createChild("IBPBOn");
5413 if (hw.fIBPBOnVMExit)
5414 pelmChild->setAttribute("vmexit", hw.fIBPBOnVMExit);
5415 if (hw.fIBPBOnVMEntry)
5416 pelmChild->setAttribute("vmentry", hw.fIBPBOnVMEntry);
5417 }
5418 }
5419 if (m->sv >= SettingsVersion_v1_16 && hw.fSpecCtrl)
5420 pelmCPU->createChild("SpecCtrl")->setAttribute("enabled", hw.fSpecCtrl);
5421 if (m->sv >= SettingsVersion_v1_16 && hw.fSpecCtrlByHost)
5422 pelmCPU->createChild("SpecCtrlByHost")->setAttribute("enabled", hw.fSpecCtrlByHost);
5423 if (m->sv >= SettingsVersion_v1_17 && hw.fNestedHWVirt)
5424 pelmCPU->createChild("NestedHWVirt")->setAttribute("enabled", hw.fNestedHWVirt);
5425
5426 if (m->sv >= SettingsVersion_v1_14 && hw.enmLongMode != Hardware::LongMode_Legacy)
5427 {
5428 // LongMode has too crazy default handling, must always save this setting.
5429 pelmCPU->createChild("LongMode")->setAttribute("enabled", hw.enmLongMode == Hardware::LongMode_Enabled);
5430 }
5431
5432 if (hw.fTripleFaultReset)
5433 pelmCPU->createChild("TripleFaultReset")->setAttribute("enabled", hw.fTripleFaultReset);
5434 if (m->sv >= SettingsVersion_v1_14)
5435 {
5436 if (hw.fX2APIC)
5437 pelmCPU->createChild("X2APIC")->setAttribute("enabled", hw.fX2APIC);
5438 else if (!hw.fAPIC)
5439 pelmCPU->createChild("APIC")->setAttribute("enabled", hw.fAPIC);
5440 }
5441 if (hw.cCPUs > 1)
5442 pelmCPU->setAttribute("count", hw.cCPUs);
5443 if (hw.ulCpuExecutionCap != 100)
5444 pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap);
5445 if (hw.uCpuIdPortabilityLevel != 0)
5446 pelmCPU->setAttribute("CpuIdPortabilityLevel", hw.uCpuIdPortabilityLevel);
5447 if (!hw.strCpuProfile.equals("host") && hw.strCpuProfile.isNotEmpty())
5448 pelmCPU->setAttribute("CpuProfile", hw.strCpuProfile);
5449
5450 // HardwareVirtExLargePages has too crazy default handling, must always save this setting.
5451 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
5452
5453 if (m->sv >= SettingsVersion_v1_9)
5454 {
5455 if (hw.fHardwareVirtForce)
5456 pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce);
5457 }
5458
5459 if (m->sv >= SettingsVersion_v1_9 && hw.fUseNativeApi)
5460 pelmCPU->createChild("HardwareVirtExUseNativeApi")->setAttribute("enabled", hw.fUseNativeApi);
5461
5462 if (m->sv >= SettingsVersion_v1_10)
5463 {
5464 if (hw.fCpuHotPlug)
5465 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
5466
5467 xml::ElementNode *pelmCpuTree = NULL;
5468 for (CpuList::const_iterator it = hw.llCpus.begin();
5469 it != hw.llCpus.end();
5470 ++it)
5471 {
5472 const Cpu &cpu = *it;
5473
5474 if (pelmCpuTree == NULL)
5475 pelmCpuTree = pelmCPU->createChild("CpuTree");
5476
5477 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
5478 pelmCpu->setAttribute("id", cpu.ulId);
5479 }
5480 }
5481
5482 xml::ElementNode *pelmCpuIdTree = NULL;
5483 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
5484 it != hw.llCpuIdLeafs.end();
5485 ++it)
5486 {
5487 const CpuIdLeaf &leaf = *it;
5488
5489 if (pelmCpuIdTree == NULL)
5490 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
5491
5492 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
5493 pelmCpuIdLeaf->setAttribute("id", leaf.idx);
5494 if (leaf.idxSub != 0)
5495 pelmCpuIdLeaf->setAttribute("subleaf", leaf.idxSub);
5496 pelmCpuIdLeaf->setAttribute("eax", leaf.uEax);
5497 pelmCpuIdLeaf->setAttribute("ebx", leaf.uEbx);
5498 pelmCpuIdLeaf->setAttribute("ecx", leaf.uEcx);
5499 pelmCpuIdLeaf->setAttribute("edx", leaf.uEdx);
5500 }
5501
5502 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
5503 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
5504 if (m->sv >= SettingsVersion_v1_10)
5505 {
5506 if (hw.fPageFusionEnabled)
5507 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
5508 }
5509
5510 if ( (m->sv >= SettingsVersion_v1_9)
5511 && (hw.firmwareType >= FirmwareType_EFI)
5512 )
5513 {
5514 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
5515 const char *pcszFirmware;
5516
5517 switch (hw.firmwareType)
5518 {
5519 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
5520 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
5521 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
5522 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
5523 default: pcszFirmware = "None"; break;
5524 }
5525 pelmFirmware->setAttribute("type", pcszFirmware);
5526 }
5527
5528 if ( m->sv >= SettingsVersion_v1_10
5529 && ( hw.pointingHIDType != PointingHIDType_PS2Mouse
5530 || hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard))
5531 {
5532 xml::ElementNode *pelmHID = pelmHardware->createChild("HID");
5533 const char *pcszHID;
5534
5535 if (hw.pointingHIDType != PointingHIDType_PS2Mouse)
5536 {
5537 switch (hw.pointingHIDType)
5538 {
5539 case PointingHIDType_USBMouse: pcszHID = "USBMouse"; break;
5540 case PointingHIDType_USBTablet: pcszHID = "USBTablet"; break;
5541 case PointingHIDType_PS2Mouse: pcszHID = "PS2Mouse"; break;
5542 case PointingHIDType_ComboMouse: pcszHID = "ComboMouse"; break;
5543 case PointingHIDType_USBMultiTouch: pcszHID = "USBMultiTouch";break;
5544 case PointingHIDType_None: pcszHID = "None"; break;
5545 default: Assert(false); pcszHID = "PS2Mouse"; break;
5546 }
5547 pelmHID->setAttribute("Pointing", pcszHID);
5548 }
5549
5550 if (hw.keyboardHIDType != KeyboardHIDType_PS2Keyboard)
5551 {
5552 switch (hw.keyboardHIDType)
5553 {
5554 case KeyboardHIDType_USBKeyboard: pcszHID = "USBKeyboard"; break;
5555 case KeyboardHIDType_PS2Keyboard: pcszHID = "PS2Keyboard"; break;
5556 case KeyboardHIDType_ComboKeyboard: pcszHID = "ComboKeyboard"; break;
5557 case KeyboardHIDType_None: pcszHID = "None"; break;
5558 default: Assert(false); pcszHID = "PS2Keyboard"; break;
5559 }
5560 pelmHID->setAttribute("Keyboard", pcszHID);
5561 }
5562 }
5563
5564 if ( (m->sv >= SettingsVersion_v1_10)
5565 && hw.fHPETEnabled
5566 )
5567 {
5568 xml::ElementNode *pelmHPET = pelmHardware->createChild("HPET");
5569 pelmHPET->setAttribute("enabled", hw.fHPETEnabled);
5570 }
5571
5572 if ( (m->sv >= SettingsVersion_v1_11)
5573 )
5574 {
5575 if (hw.chipsetType != ChipsetType_PIIX3)
5576 {
5577 xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset");
5578 const char *pcszChipset;
5579
5580 switch (hw.chipsetType)
5581 {
5582 case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break;
5583 case ChipsetType_ICH9: pcszChipset = "ICH9"; break;
5584 default: Assert(false); pcszChipset = "PIIX3"; break;
5585 }
5586 pelmChipset->setAttribute("type", pcszChipset);
5587 }
5588 }
5589
5590 if ( (m->sv >= SettingsVersion_v1_15)
5591 && !hw.areParavirtDefaultSettings(m->sv)
5592 )
5593 {
5594 const char *pcszParavirtProvider;
5595 switch (hw.paravirtProvider)
5596 {
5597 case ParavirtProvider_None: pcszParavirtProvider = "None"; break;
5598 case ParavirtProvider_Default: pcszParavirtProvider = "Default"; break;
5599 case ParavirtProvider_Legacy: pcszParavirtProvider = "Legacy"; break;
5600 case ParavirtProvider_Minimal: pcszParavirtProvider = "Minimal"; break;
5601 case ParavirtProvider_HyperV: pcszParavirtProvider = "HyperV"; break;
5602 case ParavirtProvider_KVM: pcszParavirtProvider = "KVM"; break;
5603 default: Assert(false); pcszParavirtProvider = "None"; break;
5604 }
5605
5606 xml::ElementNode *pelmParavirt = pelmHardware->createChild("Paravirt");
5607 pelmParavirt->setAttribute("provider", pcszParavirtProvider);
5608
5609 if ( m->sv >= SettingsVersion_v1_16
5610 && hw.strParavirtDebug.isNotEmpty())
5611 pelmParavirt->setAttribute("debug", hw.strParavirtDebug);
5612 }
5613
5614 if (!hw.areBootOrderDefaultSettings())
5615 {
5616 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
5617 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
5618 it != hw.mapBootOrder.end();
5619 ++it)
5620 {
5621 uint32_t i = it->first;
5622 DeviceType_T type = it->second;
5623 const char *pcszDevice;
5624
5625 switch (type)
5626 {
5627 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
5628 case DeviceType_DVD: pcszDevice = "DVD"; break;
5629 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
5630 case DeviceType_Network: pcszDevice = "Network"; break;
5631 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
5632 }
5633
5634 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
5635 pelmOrder->setAttribute("position",
5636 i + 1); // XML is 1-based but internal data is 0-based
5637 pelmOrder->setAttribute("device", pcszDevice);
5638 }
5639 }
5640
5641 if (!hw.areDisplayDefaultSettings())
5642 {
5643 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
5644 if (hw.graphicsControllerType != GraphicsControllerType_VBoxVGA)
5645 {
5646 const char *pcszGraphics;
5647 switch (hw.graphicsControllerType)
5648 {
5649 case GraphicsControllerType_VBoxVGA: pcszGraphics = "VBoxVGA"; break;
5650 case GraphicsControllerType_VMSVGA: pcszGraphics = "VMSVGA"; break;
5651 case GraphicsControllerType_VBoxSVGA: pcszGraphics = "VBoxSVGA"; break;
5652 default: /*case GraphicsControllerType_Null:*/ pcszGraphics = "None"; break;
5653 }
5654 pelmDisplay->setAttribute("controller", pcszGraphics);
5655 }
5656 if (hw.ulVRAMSizeMB != 8)
5657 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
5658 if (hw.cMonitors > 1)
5659 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
5660 if (hw.fAccelerate3D)
5661 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
5662
5663 if (m->sv >= SettingsVersion_v1_8)
5664 {
5665 if (hw.fAccelerate2DVideo)
5666 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
5667 }
5668 }
5669
5670 if (m->sv >= SettingsVersion_v1_14 && !hw.areVideoCaptureDefaultSettings())
5671 {
5672 xml::ElementNode *pelmVideoCapture = pelmHardware->createChild("VideoCapture");
5673 if (hw.fVideoCaptureEnabled)
5674 pelmVideoCapture->setAttribute("enabled", hw.fVideoCaptureEnabled);
5675 if (hw.u64VideoCaptureScreens != UINT64_C(0xffffffffffffffff))
5676 pelmVideoCapture->setAttribute("screens", hw.u64VideoCaptureScreens);
5677 if (!hw.strVideoCaptureFile.isEmpty())
5678 pelmVideoCapture->setAttributePath("file", hw.strVideoCaptureFile);
5679 if (hw.ulVideoCaptureHorzRes != 1024 || hw.ulVideoCaptureVertRes != 768)
5680 {
5681 pelmVideoCapture->setAttribute("horzRes", hw.ulVideoCaptureHorzRes);
5682 pelmVideoCapture->setAttribute("vertRes", hw.ulVideoCaptureVertRes);
5683 }
5684 if (hw.ulVideoCaptureRate != 512)
5685 pelmVideoCapture->setAttribute("rate", hw.ulVideoCaptureRate);
5686 if (hw.ulVideoCaptureFPS)
5687 pelmVideoCapture->setAttribute("fps", hw.ulVideoCaptureFPS);
5688 if (hw.ulVideoCaptureMaxTime)
5689 pelmVideoCapture->setAttribute("maxTime", hw.ulVideoCaptureMaxTime);
5690 if (hw.ulVideoCaptureMaxSize)
5691 pelmVideoCapture->setAttribute("maxSize", hw.ulVideoCaptureMaxSize);
5692 if (!hw.strVideoCaptureOptions.isEmpty())
5693 pelmVideoCapture->setAttributePath("options", hw.strVideoCaptureOptions);
5694 }
5695
5696 if (!hw.vrdeSettings.areDefaultSettings(m->sv))
5697 {
5698 xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay");
5699 if (m->sv < SettingsVersion_v1_16 ? !hw.vrdeSettings.fEnabled : hw.vrdeSettings.fEnabled)
5700 pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled);
5701 if (m->sv < SettingsVersion_v1_11)
5702 {
5703 /* In VBox 4.0 these attributes are replaced with "Properties". */
5704 Utf8Str strPort;
5705 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports");
5706 if (it != hw.vrdeSettings.mapProperties.end())
5707 strPort = it->second;
5708 if (!strPort.length())
5709 strPort = "3389";
5710 pelmVRDE->setAttribute("port", strPort);
5711
5712 Utf8Str strAddress;
5713 it = hw.vrdeSettings.mapProperties.find("TCP/Address");
5714 if (it != hw.vrdeSettings.mapProperties.end())
5715 strAddress = it->second;
5716 if (strAddress.length())
5717 pelmVRDE->setAttribute("netAddress", strAddress);
5718 }
5719 if (hw.vrdeSettings.authType != AuthType_Null)
5720 {
5721 const char *pcszAuthType;
5722 switch (hw.vrdeSettings.authType)
5723 {
5724 case AuthType_Guest: pcszAuthType = "Guest"; break;
5725 case AuthType_External: pcszAuthType = "External"; break;
5726 default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break;
5727 }
5728 pelmVRDE->setAttribute("authType", pcszAuthType);
5729 }
5730
5731 if (hw.vrdeSettings.ulAuthTimeout != 0 && hw.vrdeSettings.ulAuthTimeout != 5000)
5732 pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout);
5733 if (hw.vrdeSettings.fAllowMultiConnection)
5734 pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
5735 if (hw.vrdeSettings.fReuseSingleConnection)
5736 pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
5737
5738 if (m->sv == SettingsVersion_v1_10)
5739 {
5740 xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel");
5741
5742 /* In 4.0 videochannel settings were replaced with properties, so look at properties. */
5743 Utf8Str str;
5744 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
5745 if (it != hw.vrdeSettings.mapProperties.end())
5746 str = it->second;
5747 bool fVideoChannel = RTStrICmp(str.c_str(), "true") == 0
5748 || RTStrCmp(str.c_str(), "1") == 0;
5749 pelmVideoChannel->setAttribute("enabled", fVideoChannel);
5750
5751 it = hw.vrdeSettings.mapProperties.find("VideoChannel/Quality");
5752 if (it != hw.vrdeSettings.mapProperties.end())
5753 str = it->second;
5754 uint32_t ulVideoChannelQuality = RTStrToUInt32(str.c_str()); /* This returns 0 on invalid string which is ok. */
5755 if (ulVideoChannelQuality == 0)
5756 ulVideoChannelQuality = 75;
5757 else
5758 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
5759 pelmVideoChannel->setAttribute("quality", ulVideoChannelQuality);
5760 }
5761 if (m->sv >= SettingsVersion_v1_11)
5762 {
5763 if (hw.vrdeSettings.strAuthLibrary.length())
5764 pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary);
5765 if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty())
5766 pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
5767 if (hw.vrdeSettings.mapProperties.size() > 0)
5768 {
5769 xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties");
5770 for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin();
5771 it != hw.vrdeSettings.mapProperties.end();
5772 ++it)
5773 {
5774 const Utf8Str &strName = it->first;
5775 const Utf8Str &strValue = it->second;
5776 xml::ElementNode *pelm = pelmProperties->createChild("Property");
5777 pelm->setAttribute("name", strName);
5778 pelm->setAttribute("value", strValue);
5779 }
5780 }
5781 }
5782 }
5783
5784 if (!hw.biosSettings.areDefaultSettings())
5785 {
5786 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
5787 if (!hw.biosSettings.fACPIEnabled)
5788 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
5789 if (hw.biosSettings.fIOAPICEnabled)
5790 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
5791 if (hw.biosSettings.apicMode != APICMode_APIC)
5792 {
5793 const char *pcszAPIC;
5794 switch (hw.biosSettings.apicMode)
5795 {
5796 case APICMode_Disabled:
5797 pcszAPIC = "Disabled";
5798 break;
5799 case APICMode_APIC:
5800 default:
5801 pcszAPIC = "APIC";
5802 break;
5803 case APICMode_X2APIC:
5804 pcszAPIC = "X2APIC";
5805 break;
5806 }
5807 pelmBIOS->createChild("APIC")->setAttribute("mode", pcszAPIC);
5808 }
5809
5810 if ( !hw.biosSettings.fLogoFadeIn
5811 || !hw.biosSettings.fLogoFadeOut
5812 || hw.biosSettings.ulLogoDisplayTime
5813 || !hw.biosSettings.strLogoImagePath.isEmpty())
5814 {
5815 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
5816 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
5817 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
5818 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
5819 if (!hw.biosSettings.strLogoImagePath.isEmpty())
5820 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
5821 }
5822
5823 if (hw.biosSettings.biosBootMenuMode != BIOSBootMenuMode_MessageAndMenu)
5824 {
5825 const char *pcszBootMenu;
5826 switch (hw.biosSettings.biosBootMenuMode)
5827 {
5828 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
5829 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
5830 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
5831 }
5832 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
5833 }
5834 if (hw.biosSettings.llTimeOffset)
5835 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
5836 if (hw.biosSettings.fPXEDebugEnabled)
5837 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
5838 }
5839
5840 if (m->sv < SettingsVersion_v1_9)
5841 {
5842 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
5843 // run thru the storage controllers to see if we have a DVD or floppy drives
5844 size_t cDVDs = 0;
5845 size_t cFloppies = 0;
5846
5847 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
5848 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
5849
5850 for (StorageControllersList::const_iterator it = hw.storage.llStorageControllers.begin();
5851 it != hw.storage.llStorageControllers.end();
5852 ++it)
5853 {
5854 const StorageController &sctl = *it;
5855 // in old settings format, the DVD drive could only have been under the IDE controller
5856 if (sctl.storageBus == StorageBus_IDE)
5857 {
5858 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
5859 it2 != sctl.llAttachedDevices.end();
5860 ++it2)
5861 {
5862 const AttachedDevice &att = *it2;
5863 if (att.deviceType == DeviceType_DVD)
5864 {
5865 if (cDVDs > 0)
5866 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
5867
5868 ++cDVDs;
5869
5870 pelmDVD->setAttribute("passthrough", att.fPassThrough);
5871 if (att.fTempEject)
5872 pelmDVD->setAttribute("tempeject", att.fTempEject);
5873
5874 if (!att.uuid.isZero() && att.uuid.isValid())
5875 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
5876 else if (att.strHostDriveSrc.length())
5877 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
5878 }
5879 }
5880 }
5881 else if (sctl.storageBus == StorageBus_Floppy)
5882 {
5883 size_t cFloppiesHere = sctl.llAttachedDevices.size();
5884 if (cFloppiesHere > 1)
5885 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
5886 if (cFloppiesHere)
5887 {
5888 const AttachedDevice &att = sctl.llAttachedDevices.front();
5889 pelmFloppy->setAttribute("enabled", true);
5890
5891 if (!att.uuid.isZero() && att.uuid.isValid())
5892 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
5893 else if (att.strHostDriveSrc.length())
5894 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
5895 }
5896
5897 cFloppies += cFloppiesHere;
5898 }
5899 }
5900
5901 if (cFloppies == 0)
5902 pelmFloppy->setAttribute("enabled", false);
5903 else if (cFloppies > 1)
5904 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
5905 }
5906
5907 if (m->sv < SettingsVersion_v1_14)
5908 {
5909 bool fOhciEnabled = false;
5910 bool fEhciEnabled = false;
5911 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
5912
5913 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
5914 it != hw.usbSettings.llUSBControllers.end();
5915 ++it)
5916 {
5917 const USBController &ctrl = *it;
5918
5919 switch (ctrl.enmType)
5920 {
5921 case USBControllerType_OHCI:
5922 fOhciEnabled = true;
5923 break;
5924 case USBControllerType_EHCI:
5925 fEhciEnabled = true;
5926 break;
5927 default:
5928 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
5929 }
5930 }
5931
5932 pelmUSB->setAttribute("enabled", fOhciEnabled);
5933 pelmUSB->setAttribute("enabledEhci", fEhciEnabled);
5934
5935 buildUSBDeviceFilters(*pelmUSB, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
5936 }
5937 else
5938 {
5939 if ( hw.usbSettings.llUSBControllers.size()
5940 || hw.usbSettings.llDeviceFilters.size())
5941 {
5942 xml::ElementNode *pelmUSB = pelmHardware->createChild("USB");
5943 if (hw.usbSettings.llUSBControllers.size())
5944 {
5945 xml::ElementNode *pelmCtrls = pelmUSB->createChild("Controllers");
5946
5947 for (USBControllerList::const_iterator it = hw.usbSettings.llUSBControllers.begin();
5948 it != hw.usbSettings.llUSBControllers.end();
5949 ++it)
5950 {
5951 const USBController &ctrl = *it;
5952 com::Utf8Str strType;
5953 xml::ElementNode *pelmCtrl = pelmCtrls->createChild("Controller");
5954
5955 switch (ctrl.enmType)
5956 {
5957 case USBControllerType_OHCI:
5958 strType = "OHCI";
5959 break;
5960 case USBControllerType_EHCI:
5961 strType = "EHCI";
5962 break;
5963 case USBControllerType_XHCI:
5964 strType = "XHCI";
5965 break;
5966 default:
5967 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
5968 }
5969
5970 pelmCtrl->setAttribute("name", ctrl.strName);
5971 pelmCtrl->setAttribute("type", strType);
5972 }
5973 }
5974
5975 if (hw.usbSettings.llDeviceFilters.size())
5976 {
5977 xml::ElementNode *pelmFilters = pelmUSB->createChild("DeviceFilters");
5978 buildUSBDeviceFilters(*pelmFilters, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
5979 }
5980 }
5981 }
5982
5983 if ( hw.llNetworkAdapters.size()
5984 && !hw.areAllNetworkAdaptersDefaultSettings(m->sv))
5985 {
5986 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
5987 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
5988 it != hw.llNetworkAdapters.end();
5989 ++it)
5990 {
5991 const NetworkAdapter &nic = *it;
5992
5993 if (!nic.areDefaultSettings(m->sv))
5994 {
5995 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
5996 pelmAdapter->setAttribute("slot", nic.ulSlot);
5997 if (nic.fEnabled)
5998 pelmAdapter->setAttribute("enabled", nic.fEnabled);
5999 if (!nic.strMACAddress.isEmpty())
6000 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
6001 if ( (m->sv >= SettingsVersion_v1_16 && !nic.fCableConnected)
6002 || (m->sv < SettingsVersion_v1_16 && nic.fCableConnected))
6003 pelmAdapter->setAttribute("cable", nic.fCableConnected);
6004 if (nic.ulLineSpeed)
6005 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
6006 if (nic.ulBootPriority != 0)
6007 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
6008 if (nic.fTraceEnabled)
6009 {
6010 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
6011 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
6012 }
6013 if (nic.strBandwidthGroup.isNotEmpty())
6014 pelmAdapter->setAttribute("bandwidthGroup", nic.strBandwidthGroup);
6015
6016 const char *pszPolicy;
6017 switch (nic.enmPromiscModePolicy)
6018 {
6019 case NetworkAdapterPromiscModePolicy_Deny: pszPolicy = NULL; break;
6020 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPolicy = "AllowNetwork"; break;
6021 case NetworkAdapterPromiscModePolicy_AllowAll: pszPolicy = "AllowAll"; break;
6022 default: pszPolicy = NULL; AssertFailed(); break;
6023 }
6024 if (pszPolicy)
6025 pelmAdapter->setAttribute("promiscuousModePolicy", pszPolicy);
6026
6027 if ( (m->sv >= SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C973)
6028 || (m->sv < SettingsVersion_v1_16 && nic.type != NetworkAdapterType_Am79C970A))
6029 {
6030 const char *pcszType;
6031 switch (nic.type)
6032 {
6033 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
6034 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
6035 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
6036 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
6037 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
6038 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
6039 }
6040 pelmAdapter->setAttribute("type", pcszType);
6041 }
6042
6043 xml::ElementNode *pelmNAT;
6044 if (m->sv < SettingsVersion_v1_10)
6045 {
6046 switch (nic.mode)
6047 {
6048 case NetworkAttachmentType_NAT:
6049 pelmNAT = pelmAdapter->createChild("NAT");
6050 if (nic.nat.strNetwork.length())
6051 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6052 break;
6053
6054 case NetworkAttachmentType_Bridged:
6055 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
6056 break;
6057
6058 case NetworkAttachmentType_Internal:
6059 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
6060 break;
6061
6062 case NetworkAttachmentType_HostOnly:
6063 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
6064 break;
6065
6066 default: /*case NetworkAttachmentType_Null:*/
6067 break;
6068 }
6069 }
6070 else
6071 {
6072 /* m->sv >= SettingsVersion_v1_10 */
6073 if (!nic.areDisabledDefaultSettings())
6074 {
6075 xml::ElementNode *pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
6076 if (nic.mode != NetworkAttachmentType_NAT)
6077 buildNetworkXML(NetworkAttachmentType_NAT, false, *pelmDisabledNode, nic);
6078 if (nic.mode != NetworkAttachmentType_Bridged)
6079 buildNetworkXML(NetworkAttachmentType_Bridged, false, *pelmDisabledNode, nic);
6080 if (nic.mode != NetworkAttachmentType_Internal)
6081 buildNetworkXML(NetworkAttachmentType_Internal, false, *pelmDisabledNode, nic);
6082 if (nic.mode != NetworkAttachmentType_HostOnly)
6083 buildNetworkXML(NetworkAttachmentType_HostOnly, false, *pelmDisabledNode, nic);
6084 if (nic.mode != NetworkAttachmentType_Generic)
6085 buildNetworkXML(NetworkAttachmentType_Generic, false, *pelmDisabledNode, nic);
6086 if (nic.mode != NetworkAttachmentType_NATNetwork)
6087 buildNetworkXML(NetworkAttachmentType_NATNetwork, false, *pelmDisabledNode, nic);
6088 }
6089 buildNetworkXML(nic.mode, true, *pelmAdapter, nic);
6090 }
6091 }
6092 }
6093 }
6094
6095 if (hw.llSerialPorts.size())
6096 {
6097 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
6098 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
6099 it != hw.llSerialPorts.end();
6100 ++it)
6101 {
6102 const SerialPort &port = *it;
6103 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6104 pelmPort->setAttribute("slot", port.ulSlot);
6105 pelmPort->setAttribute("enabled", port.fEnabled);
6106 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6107 pelmPort->setAttribute("IRQ", port.ulIRQ);
6108
6109 const char *pcszHostMode;
6110 switch (port.portMode)
6111 {
6112 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
6113 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
6114 case PortMode_TCP: pcszHostMode = "TCP"; break;
6115 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
6116 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
6117 }
6118 switch (port.portMode)
6119 {
6120 case PortMode_TCP:
6121 case PortMode_HostPipe:
6122 pelmPort->setAttribute("server", port.fServer);
6123 RT_FALL_THRU();
6124 case PortMode_HostDevice:
6125 case PortMode_RawFile:
6126 pelmPort->setAttribute("path", port.strPath);
6127 break;
6128
6129 default:
6130 break;
6131 }
6132 pelmPort->setAttribute("hostMode", pcszHostMode);
6133
6134 if ( m->sv >= SettingsVersion_v1_17
6135 && port.uartType != UartType_U16550A)
6136 {
6137 const char *pcszUartType;
6138
6139 switch (port.uartType)
6140 {
6141 case UartType_U16450: pcszUartType = "16450"; break;
6142 case UartType_U16550A: pcszUartType = "16550A"; break;
6143 case UartType_U16750: pcszUartType = "16750"; break;
6144 default: pcszUartType = "16550A"; break;
6145 }
6146 pelmPort->setAttribute("uartType", pcszUartType);
6147 }
6148 }
6149 }
6150
6151 if (hw.llParallelPorts.size())
6152 {
6153 xml::ElementNode *pelmPorts = pelmHardware->createChild("LPT");
6154 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
6155 it != hw.llParallelPorts.end();
6156 ++it)
6157 {
6158 const ParallelPort &port = *it;
6159 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
6160 pelmPort->setAttribute("slot", port.ulSlot);
6161 pelmPort->setAttribute("enabled", port.fEnabled);
6162 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
6163 pelmPort->setAttribute("IRQ", port.ulIRQ);
6164 if (port.strPath.length())
6165 pelmPort->setAttribute("path", port.strPath);
6166 }
6167 }
6168
6169 /* Always write the AudioAdapter config, intentionally not checking if
6170 * the settings are at the default, because that would be problematic
6171 * for the configured host driver type, which would automatically change
6172 * if the default host driver is detected differently. */
6173 {
6174 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
6175
6176 const char *pcszController;
6177 switch (hw.audioAdapter.controllerType)
6178 {
6179 case AudioControllerType_SB16:
6180 pcszController = "SB16";
6181 break;
6182 case AudioControllerType_HDA:
6183 if (m->sv >= SettingsVersion_v1_11)
6184 {
6185 pcszController = "HDA";
6186 break;
6187 }
6188 RT_FALL_THRU();
6189 case AudioControllerType_AC97:
6190 default:
6191 pcszController = NULL;
6192 break;
6193 }
6194 if (pcszController)
6195 pelmAudio->setAttribute("controller", pcszController);
6196
6197 const char *pcszCodec;
6198 switch (hw.audioAdapter.codecType)
6199 {
6200 /* Only write out the setting for non-default AC'97 codec
6201 * and leave the rest alone.
6202 */
6203#if 0
6204 case AudioCodecType_SB16:
6205 pcszCodec = "SB16";
6206 break;
6207 case AudioCodecType_STAC9221:
6208 pcszCodec = "STAC9221";
6209 break;
6210 case AudioCodecType_STAC9700:
6211 pcszCodec = "STAC9700";
6212 break;
6213#endif
6214 case AudioCodecType_AD1980:
6215 pcszCodec = "AD1980";
6216 break;
6217 default:
6218 /* Don't write out anything if unknown. */
6219 pcszCodec = NULL;
6220 }
6221 if (pcszCodec)
6222 pelmAudio->setAttribute("codec", pcszCodec);
6223
6224 const char *pcszDriver;
6225 switch (hw.audioAdapter.driverType)
6226 {
6227 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
6228 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
6229 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
6230 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
6231 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
6232 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
6233 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
6234 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
6235 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
6236 }
6237 /* Deliberately have the audio driver explicitly in the config file,
6238 * otherwise an unwritten default driver triggers auto-detection. */
6239 pelmAudio->setAttribute("driver", pcszDriver);
6240
6241 if (hw.audioAdapter.fEnabled || m->sv < SettingsVersion_v1_16)
6242 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
6243
6244 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledIn)
6245 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledIn))
6246 pelmAudio->setAttribute("enabledIn", hw.audioAdapter.fEnabledIn);
6247
6248 if ( (m->sv <= SettingsVersion_v1_16 && !hw.audioAdapter.fEnabledOut)
6249 || (m->sv > SettingsVersion_v1_16 && hw.audioAdapter.fEnabledOut))
6250 pelmAudio->setAttribute("enabledOut", hw.audioAdapter.fEnabledOut);
6251
6252 if (m->sv >= SettingsVersion_v1_15 && hw.audioAdapter.properties.size() > 0)
6253 {
6254 for (StringsMap::const_iterator it = hw.audioAdapter.properties.begin();
6255 it != hw.audioAdapter.properties.end();
6256 ++it)
6257 {
6258 const Utf8Str &strName = it->first;
6259 const Utf8Str &strValue = it->second;
6260 xml::ElementNode *pelm = pelmAudio->createChild("Property");
6261 pelm->setAttribute("name", strName);
6262 pelm->setAttribute("value", strValue);
6263 }
6264 }
6265 }
6266
6267 if (m->sv >= SettingsVersion_v1_10 && machineUserData.fRTCUseUTC)
6268 {
6269 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
6270 pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local");
6271 }
6272
6273 if (hw.llSharedFolders.size())
6274 {
6275 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
6276 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
6277 it != hw.llSharedFolders.end();
6278 ++it)
6279 {
6280 const SharedFolder &sf = *it;
6281 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
6282 pelmThis->setAttribute("name", sf.strName);
6283 pelmThis->setAttribute("hostPath", sf.strHostPath);
6284 pelmThis->setAttribute("writable", sf.fWritable);
6285 pelmThis->setAttribute("autoMount", sf.fAutoMount);
6286 }
6287 }
6288
6289 if (hw.clipboardMode != ClipboardMode_Disabled)
6290 {
6291 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
6292 const char *pcszClip;
6293 switch (hw.clipboardMode)
6294 {
6295 default: /*case ClipboardMode_Disabled:*/ pcszClip = "Disabled"; break;
6296 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
6297 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
6298 case ClipboardMode_Bidirectional: pcszClip = "Bidirectional"; break;
6299 }
6300 pelmClip->setAttribute("mode", pcszClip);
6301 }
6302
6303 if (hw.dndMode != DnDMode_Disabled)
6304 {
6305 xml::ElementNode *pelmDragAndDrop = pelmHardware->createChild("DragAndDrop");
6306 const char *pcszDragAndDrop;
6307 switch (hw.dndMode)
6308 {
6309 default: /*case DnDMode_Disabled:*/ pcszDragAndDrop = "Disabled"; break;
6310 case DnDMode_HostToGuest: pcszDragAndDrop = "HostToGuest"; break;
6311 case DnDMode_GuestToHost: pcszDragAndDrop = "GuestToHost"; break;
6312 case DnDMode_Bidirectional: pcszDragAndDrop = "Bidirectional"; break;
6313 }
6314 pelmDragAndDrop->setAttribute("mode", pcszDragAndDrop);
6315 }
6316
6317 if ( m->sv >= SettingsVersion_v1_10
6318 && !hw.ioSettings.areDefaultSettings())
6319 {
6320 xml::ElementNode *pelmIO = pelmHardware->createChild("IO");
6321 xml::ElementNode *pelmIOCache;
6322
6323 if (!hw.ioSettings.areDefaultSettings())
6324 {
6325 pelmIOCache = pelmIO->createChild("IoCache");
6326 if (!hw.ioSettings.fIOCacheEnabled)
6327 pelmIOCache->setAttribute("enabled", hw.ioSettings.fIOCacheEnabled);
6328 if (hw.ioSettings.ulIOCacheSize != 5)
6329 pelmIOCache->setAttribute("size", hw.ioSettings.ulIOCacheSize);
6330 }
6331
6332 if ( m->sv >= SettingsVersion_v1_11
6333 && hw.ioSettings.llBandwidthGroups.size())
6334 {
6335 xml::ElementNode *pelmBandwidthGroups = pelmIO->createChild("BandwidthGroups");
6336 for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin();
6337 it != hw.ioSettings.llBandwidthGroups.end();
6338 ++it)
6339 {
6340 const BandwidthGroup &gr = *it;
6341 const char *pcszType;
6342 xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup");
6343 pelmThis->setAttribute("name", gr.strName);
6344 switch (gr.enmType)
6345 {
6346 case BandwidthGroupType_Network: pcszType = "Network"; break;
6347 default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break;
6348 }
6349 pelmThis->setAttribute("type", pcszType);
6350 if (m->sv >= SettingsVersion_v1_13)
6351 pelmThis->setAttribute("maxBytesPerSec", gr.cMaxBytesPerSec);
6352 else
6353 pelmThis->setAttribute("maxMbPerSec", gr.cMaxBytesPerSec / _1M);
6354 }
6355 }
6356 }
6357
6358 if ( m->sv >= SettingsVersion_v1_12
6359 && hw.pciAttachments.size())
6360 {
6361 xml::ElementNode *pelmPCI = pelmHardware->createChild("HostPci");
6362 xml::ElementNode *pelmPCIDevices = pelmPCI->createChild("Devices");
6363
6364 for (HostPCIDeviceAttachmentList::const_iterator it = hw.pciAttachments.begin();
6365 it != hw.pciAttachments.end();
6366 ++it)
6367 {
6368 const HostPCIDeviceAttachment &hpda = *it;
6369
6370 xml::ElementNode *pelmThis = pelmPCIDevices->createChild("Device");
6371
6372 pelmThis->setAttribute("host", hpda.uHostAddress);
6373 pelmThis->setAttribute("guest", hpda.uGuestAddress);
6374 pelmThis->setAttribute("name", hpda.strDeviceName);
6375 }
6376 }
6377
6378 if ( m->sv >= SettingsVersion_v1_12
6379 && hw.fEmulatedUSBCardReader)
6380 {
6381 xml::ElementNode *pelmEmulatedUSB = pelmHardware->createChild("EmulatedUSB");
6382
6383 xml::ElementNode *pelmCardReader = pelmEmulatedUSB->createChild("CardReader");
6384 pelmCardReader->setAttribute("enabled", hw.fEmulatedUSBCardReader);
6385 }
6386
6387 if ( m->sv >= SettingsVersion_v1_14
6388 && !hw.strDefaultFrontend.isEmpty())
6389 {
6390 xml::ElementNode *pelmFrontend = pelmHardware->createChild("Frontend");
6391 xml::ElementNode *pelmDefault = pelmFrontend->createChild("Default");
6392 pelmDefault->setAttribute("type", hw.strDefaultFrontend);
6393 }
6394
6395 if (hw.ulMemoryBalloonSize)
6396 {
6397 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
6398 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
6399 }
6400
6401 if (hw.llGuestProperties.size())
6402 {
6403 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
6404 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
6405 it != hw.llGuestProperties.end();
6406 ++it)
6407 {
6408 const GuestProperty &prop = *it;
6409 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
6410 pelmProp->setAttribute("name", prop.strName);
6411 pelmProp->setAttribute("value", prop.strValue);
6412 pelmProp->setAttribute("timestamp", prop.timestamp);
6413 pelmProp->setAttribute("flags", prop.strFlags);
6414 }
6415 }
6416
6417 /** @todo In the future (6.0?) place the storage controllers under \<Hardware\>, because
6418 * this is where it always should've been. What else than hardware are they? */
6419 xml::ElementNode &elmStorageParent = (m->sv > SettingsVersion_Future) ? *pelmHardware : elmParent;
6420 buildStorageControllersXML(elmStorageParent,
6421 hw.storage,
6422 !!(fl & BuildMachineXML_SkipRemovableMedia),
6423 pllElementsWithUuidAttributes);
6424}
6425
6426/**
6427 * Fill a \<Network\> node. Only relevant for XML version >= v1_10.
6428 * @param mode
6429 * @param fEnabled
6430 * @param elmParent
6431 * @param nic
6432 */
6433void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
6434 bool fEnabled,
6435 xml::ElementNode &elmParent,
6436 const NetworkAdapter &nic)
6437{
6438 switch (mode)
6439 {
6440 case NetworkAttachmentType_NAT:
6441 // For the currently active network attachment type we have to
6442 // generate the tag, otherwise the attachment type is lost.
6443 if (fEnabled || !nic.nat.areDefaultSettings())
6444 {
6445 xml::ElementNode *pelmNAT = elmParent.createChild("NAT");
6446
6447 if (!nic.nat.areDefaultSettings())
6448 {
6449 if (nic.nat.strNetwork.length())
6450 pelmNAT->setAttribute("network", nic.nat.strNetwork);
6451 if (nic.nat.strBindIP.length())
6452 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
6453 if (nic.nat.u32Mtu)
6454 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
6455 if (nic.nat.u32SockRcv)
6456 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
6457 if (nic.nat.u32SockSnd)
6458 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
6459 if (nic.nat.u32TcpRcv)
6460 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
6461 if (nic.nat.u32TcpSnd)
6462 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
6463 if (!nic.nat.areDNSDefaultSettings())
6464 {
6465 xml::ElementNode *pelmDNS = pelmNAT->createChild("DNS");
6466 if (!nic.nat.fDNSPassDomain)
6467 pelmDNS->setAttribute("pass-domain", nic.nat.fDNSPassDomain);
6468 if (nic.nat.fDNSProxy)
6469 pelmDNS->setAttribute("use-proxy", nic.nat.fDNSProxy);
6470 if (nic.nat.fDNSUseHostResolver)
6471 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDNSUseHostResolver);
6472 }
6473
6474 if (!nic.nat.areAliasDefaultSettings())
6475 {
6476 xml::ElementNode *pelmAlias = pelmNAT->createChild("Alias");
6477 if (nic.nat.fAliasLog)
6478 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
6479 if (nic.nat.fAliasProxyOnly)
6480 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
6481 if (nic.nat.fAliasUseSamePorts)
6482 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
6483 }
6484
6485 if (!nic.nat.areTFTPDefaultSettings())
6486 {
6487 xml::ElementNode *pelmTFTP;
6488 pelmTFTP = pelmNAT->createChild("TFTP");
6489 if (nic.nat.strTFTPPrefix.length())
6490 pelmTFTP->setAttribute("prefix", nic.nat.strTFTPPrefix);
6491 if (nic.nat.strTFTPBootFile.length())
6492 pelmTFTP->setAttribute("boot-file", nic.nat.strTFTPBootFile);
6493 if (nic.nat.strTFTPNextServer.length())
6494 pelmTFTP->setAttribute("next-server", nic.nat.strTFTPNextServer);
6495 }
6496 buildNATForwardRulesMap(*pelmNAT, nic.nat.mapRules);
6497 }
6498 }
6499 break;
6500
6501 case NetworkAttachmentType_Bridged:
6502 // For the currently active network attachment type we have to
6503 // generate the tag, otherwise the attachment type is lost.
6504 if (fEnabled || !nic.strBridgedName.isEmpty())
6505 {
6506 xml::ElementNode *pelmMode = elmParent.createChild("BridgedInterface");
6507 if (!nic.strBridgedName.isEmpty())
6508 pelmMode->setAttribute("name", nic.strBridgedName);
6509 }
6510 break;
6511
6512 case NetworkAttachmentType_Internal:
6513 // For the currently active network attachment type we have to
6514 // generate the tag, otherwise the attachment type is lost.
6515 if (fEnabled || !nic.strInternalNetworkName.isEmpty())
6516 {
6517 xml::ElementNode *pelmMode = elmParent.createChild("InternalNetwork");
6518 if (!nic.strInternalNetworkName.isEmpty())
6519 pelmMode->setAttribute("name", nic.strInternalNetworkName);
6520 }
6521 break;
6522
6523 case NetworkAttachmentType_HostOnly:
6524 // For the currently active network attachment type we have to
6525 // generate the tag, otherwise the attachment type is lost.
6526 if (fEnabled || !nic.strHostOnlyName.isEmpty())
6527 {
6528 xml::ElementNode *pelmMode = elmParent.createChild("HostOnlyInterface");
6529 if (!nic.strHostOnlyName.isEmpty())
6530 pelmMode->setAttribute("name", nic.strHostOnlyName);
6531 }
6532 break;
6533
6534 case NetworkAttachmentType_Generic:
6535 // For the currently active network attachment type we have to
6536 // generate the tag, otherwise the attachment type is lost.
6537 if (fEnabled || !nic.areGenericDriverDefaultSettings())
6538 {
6539 xml::ElementNode *pelmMode = elmParent.createChild("GenericInterface");
6540 if (!nic.areGenericDriverDefaultSettings())
6541 {
6542 pelmMode->setAttribute("driver", nic.strGenericDriver);
6543 for (StringsMap::const_iterator it = nic.genericProperties.begin();
6544 it != nic.genericProperties.end();
6545 ++it)
6546 {
6547 xml::ElementNode *pelmProp = pelmMode->createChild("Property");
6548 pelmProp->setAttribute("name", it->first);
6549 pelmProp->setAttribute("value", it->second);
6550 }
6551 }
6552 }
6553 break;
6554
6555 case NetworkAttachmentType_NATNetwork:
6556 // For the currently active network attachment type we have to
6557 // generate the tag, otherwise the attachment type is lost.
6558 if (fEnabled || !nic.strNATNetworkName.isEmpty())
6559 {
6560 xml::ElementNode *pelmMode = elmParent.createChild("NATNetwork");
6561 if (!nic.strNATNetworkName.isEmpty())
6562 pelmMode->setAttribute("name", nic.strNATNetworkName);
6563 }
6564 break;
6565
6566 default: /*case NetworkAttachmentType_Null:*/
6567 break;
6568 }
6569}
6570
6571/**
6572 * Creates a \<StorageControllers\> node under elmParent and then writes out the XML
6573 * keys under that. Called for both the \<Machine\> node and for snapshots.
6574 * @param elmParent
6575 * @param st
6576 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
6577 * an empty drive is always written instead. This is for the OVF export case.
6578 * This parameter is ignored unless the settings version is at least v1.9, which
6579 * is always the case when this gets called for OVF export.
6580 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
6581 * pointers to which we will append all elements that we created here that contain
6582 * UUID attributes. This allows the OVF export code to quickly replace the internal
6583 * media UUIDs with the UUIDs of the media that were exported.
6584 */
6585void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
6586 const Storage &st,
6587 bool fSkipRemovableMedia,
6588 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
6589{
6590 if (!st.llStorageControllers.size())
6591 return;
6592 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
6593
6594 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
6595 it != st.llStorageControllers.end();
6596 ++it)
6597 {
6598 const StorageController &sc = *it;
6599
6600 if ( (m->sv < SettingsVersion_v1_9)
6601 && (sc.controllerType == StorageControllerType_I82078)
6602 )
6603 // floppy controller already got written into <Hardware>/<FloppyController> in buildHardwareXML()
6604 // for pre-1.9 settings
6605 continue;
6606
6607 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
6608 com::Utf8Str name = sc.strName;
6609 if (m->sv < SettingsVersion_v1_8)
6610 {
6611 // pre-1.8 settings use shorter controller names, they are
6612 // expanded when reading the settings
6613 if (name == "IDE Controller")
6614 name = "IDE";
6615 else if (name == "SATA Controller")
6616 name = "SATA";
6617 else if (name == "SCSI Controller")
6618 name = "SCSI";
6619 }
6620 pelmController->setAttribute("name", sc.strName);
6621
6622 const char *pcszType;
6623 switch (sc.controllerType)
6624 {
6625 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
6626 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
6627 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
6628 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
6629 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
6630 case StorageControllerType_I82078: pcszType = "I82078"; break;
6631 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
6632 case StorageControllerType_USB: pcszType = "USB"; break;
6633 case StorageControllerType_NVMe: pcszType = "NVMe"; break;
6634 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
6635 }
6636 pelmController->setAttribute("type", pcszType);
6637
6638 pelmController->setAttribute("PortCount", sc.ulPortCount);
6639
6640 if (m->sv >= SettingsVersion_v1_9)
6641 if (sc.ulInstance)
6642 pelmController->setAttribute("Instance", sc.ulInstance);
6643
6644 if (m->sv >= SettingsVersion_v1_10)
6645 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
6646
6647 if (m->sv >= SettingsVersion_v1_11)
6648 pelmController->setAttribute("Bootable", sc.fBootable);
6649
6650 if (sc.controllerType == StorageControllerType_IntelAhci)
6651 {
6652 pelmController->setAttribute("IDE0MasterEmulationPort", 0);
6653 pelmController->setAttribute("IDE0SlaveEmulationPort", 1);
6654 pelmController->setAttribute("IDE1MasterEmulationPort", 2);
6655 pelmController->setAttribute("IDE1SlaveEmulationPort", 3);
6656 }
6657
6658 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
6659 it2 != sc.llAttachedDevices.end();
6660 ++it2)
6661 {
6662 const AttachedDevice &att = *it2;
6663
6664 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
6665 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
6666 // the floppy controller at the top of the loop
6667 if ( att.deviceType == DeviceType_DVD
6668 && m->sv < SettingsVersion_v1_9
6669 )
6670 continue;
6671
6672 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
6673
6674 pcszType = NULL;
6675
6676 switch (att.deviceType)
6677 {
6678 case DeviceType_HardDisk:
6679 pcszType = "HardDisk";
6680 if (att.fNonRotational)
6681 pelmDevice->setAttribute("nonrotational", att.fNonRotational);
6682 if (att.fDiscard)
6683 pelmDevice->setAttribute("discard", att.fDiscard);
6684 break;
6685
6686 case DeviceType_DVD:
6687 pcszType = "DVD";
6688 pelmDevice->setAttribute("passthrough", att.fPassThrough);
6689 if (att.fTempEject)
6690 pelmDevice->setAttribute("tempeject", att.fTempEject);
6691 break;
6692
6693 case DeviceType_Floppy:
6694 pcszType = "Floppy";
6695 break;
6696
6697 default: break; /* Shut up MSC. */
6698 }
6699
6700 pelmDevice->setAttribute("type", pcszType);
6701
6702 if (m->sv >= SettingsVersion_v1_15)
6703 pelmDevice->setAttribute("hotpluggable", att.fHotPluggable);
6704
6705 pelmDevice->setAttribute("port", att.lPort);
6706 pelmDevice->setAttribute("device", att.lDevice);
6707
6708 if (att.strBwGroup.length())
6709 pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup);
6710
6711 // attached image, if any
6712 if (!att.uuid.isZero()
6713 && att.uuid.isValid()
6714 && (att.deviceType == DeviceType_HardDisk
6715 || !fSkipRemovableMedia
6716 )
6717 )
6718 {
6719 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
6720 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
6721
6722 // if caller wants a list of UUID elements, give it to them
6723 if (pllElementsWithUuidAttributes)
6724 pllElementsWithUuidAttributes->push_back(pelmImage);
6725 }
6726 else if ( (m->sv >= SettingsVersion_v1_9)
6727 && (att.strHostDriveSrc.length())
6728 )
6729 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
6730 }
6731 }
6732}
6733
6734/**
6735 * Creates a \<Debugging\> node under elmParent and then writes out the XML
6736 * keys under that. Called for both the \<Machine\> node and for snapshots.
6737 *
6738 * @param pElmParent Pointer to the parent element.
6739 * @param pDbg Pointer to the debugging settings.
6740 */
6741void MachineConfigFile::buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg)
6742{
6743 if (m->sv < SettingsVersion_v1_13 || pDbg->areDefaultSettings())
6744 return;
6745
6746 xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");
6747 xml::ElementNode *pElmTracing = pElmDebugging->createChild("Tracing");
6748 pElmTracing->setAttribute("enabled", pDbg->fTracingEnabled);
6749 pElmTracing->setAttribute("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
6750 pElmTracing->setAttribute("config", pDbg->strTracingConfig);
6751}
6752
6753/**
6754 * Creates a \<Autostart\> node under elmParent and then writes out the XML
6755 * keys under that. Called for both the \<Machine\> node and for snapshots.
6756 *
6757 * @param pElmParent Pointer to the parent element.
6758 * @param pAutostart Pointer to the autostart settings.
6759 */
6760void MachineConfigFile::buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart)
6761{
6762 const char *pcszAutostop = NULL;
6763
6764 if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())
6765 return;
6766
6767 xml::ElementNode *pElmAutostart = pElmParent->createChild("Autostart");
6768 pElmAutostart->setAttribute("enabled", pAutostart->fAutostartEnabled);
6769 pElmAutostart->setAttribute("delay", pAutostart->uAutostartDelay);
6770
6771 switch (pAutostart->enmAutostopType)
6772 {
6773 case AutostopType_Disabled: pcszAutostop = "Disabled"; break;
6774 case AutostopType_SaveState: pcszAutostop = "SaveState"; break;
6775 case AutostopType_PowerOff: pcszAutostop = "PowerOff"; break;
6776 case AutostopType_AcpiShutdown: pcszAutostop = "AcpiShutdown"; break;
6777 default: Assert(false); pcszAutostop = "Disabled"; break;
6778 }
6779 pElmAutostart->setAttribute("autostop", pcszAutostop);
6780}
6781
6782/**
6783 * Creates a \<Groups\> node under elmParent and then writes out the XML
6784 * keys under that. Called for the \<Machine\> node only.
6785 *
6786 * @param pElmParent Pointer to the parent element.
6787 * @param pllGroups Pointer to the groups list.
6788 */
6789void MachineConfigFile::buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups)
6790{
6791 if ( m->sv < SettingsVersion_v1_13 || pllGroups->size() == 0
6792 || (pllGroups->size() == 1 && pllGroups->front() == "/"))
6793 return;
6794
6795 xml::ElementNode *pElmGroups = pElmParent->createChild("Groups");
6796 for (StringsList::const_iterator it = pllGroups->begin();
6797 it != pllGroups->end();
6798 ++it)
6799 {
6800 const Utf8Str &group = *it;
6801 xml::ElementNode *pElmGroup = pElmGroups->createChild("Group");
6802 pElmGroup->setAttribute("name", group);
6803 }
6804}
6805
6806/**
6807 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
6808 * for the root snapshot of a machine, if present; elmParent then points to the \<Snapshots\> node under the
6809 * \<Machine\> node to which \<Snapshot\> must be added. This may then recurse for child snapshots.
6810 *
6811 * @param depth
6812 * @param elmParent
6813 * @param snap
6814 */
6815void MachineConfigFile::buildSnapshotXML(uint32_t depth,
6816 xml::ElementNode &elmParent,
6817 const Snapshot &snap)
6818{
6819 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
6820 throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
6821
6822 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
6823
6824 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
6825 pelmSnapshot->setAttribute("name", snap.strName);
6826 pelmSnapshot->setAttribute("timeStamp", stringifyTimestamp(snap.timestamp));
6827
6828 if (snap.strStateFile.length())
6829 pelmSnapshot->setAttributePath("stateFile", snap.strStateFile);
6830
6831 if (snap.strDescription.length())
6832 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
6833
6834 // We only skip removable media for OVF, but OVF never includes snapshots.
6835 buildHardwareXML(*pelmSnapshot, snap.hardware, 0 /* fl */, NULL /* pllElementsWithUuidAttributes */);
6836 buildDebuggingXML(pelmSnapshot, &snap.debugging);
6837 buildAutostartXML(pelmSnapshot, &snap.autostart);
6838 // note: Groups exist only for Machine, not for Snapshot
6839
6840 if (snap.llChildSnapshots.size())
6841 {
6842 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
6843 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
6844 it != snap.llChildSnapshots.end();
6845 ++it)
6846 {
6847 const Snapshot &child = *it;
6848 buildSnapshotXML(depth + 1, *pelmChildren, child);
6849 }
6850 }
6851}
6852
6853/**
6854 * Builds the XML DOM tree for the machine config under the given XML element.
6855 *
6856 * This has been separated out from write() so it can be called from elsewhere,
6857 * such as the OVF code, to build machine XML in an existing XML tree.
6858 *
6859 * As a result, this gets called from two locations:
6860 *
6861 * -- MachineConfigFile::write();
6862 *
6863 * -- Appliance::buildXMLForOneVirtualSystem()
6864 *
6865 * In fl, the following flag bits are recognized:
6866 *
6867 * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will
6868 * be written, if present. This is not set when called from OVF because OVF
6869 * has its own variant of a media registry. This flag is ignored unless the
6870 * settings version is at least v1.11 (VirtualBox 4.0).
6871 *
6872 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
6873 * of the machine and write out \<Snapshot\> and possibly more snapshots under
6874 * that, if snapshots are present. Otherwise all snapshots are suppressed
6875 * (when called from OVF).
6876 *
6877 * -- BuildMachineXML_WriteVBoxVersionAttribute: If set, add a settingsVersion
6878 * attribute to the machine tag with the vbox settings version. This is for
6879 * the OVF export case in which we don't have the settings version set in
6880 * the root element.
6881 *
6882 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
6883 * (DVDs, floppies) are silently skipped. This is for the OVF export case
6884 * until we support copying ISO and RAW media as well. This flag is ignored
6885 * unless the settings version is at least v1.9, which is always the case
6886 * when this gets called for OVF export.
6887 *
6888 * -- BuildMachineXML_SuppressSavedState: If set, the Machine/stateFile
6889 * attribute is never set. This is also for the OVF export case because we
6890 * cannot save states with OVF.
6891 *
6892 * @param elmMachine XML \<Machine\> element to add attributes and elements to.
6893 * @param fl Flags.
6894 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
6895 * see buildStorageControllersXML() for details.
6896 */
6897void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
6898 uint32_t fl,
6899 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
6900{
6901 if (fl & BuildMachineXML_WriteVBoxVersionAttribute)
6902 {
6903 // add settings version attribute to machine element
6904 setVersionAttribute(elmMachine);
6905 LogRel(("Exporting settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
6906 }
6907
6908 elmMachine.setAttribute("uuid", uuid.toStringCurly());
6909 elmMachine.setAttribute("name", machineUserData.strName);
6910 if (machineUserData.fDirectoryIncludesUUID)
6911 elmMachine.setAttribute("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
6912 if (!machineUserData.fNameSync)
6913 elmMachine.setAttribute("nameSync", machineUserData.fNameSync);
6914 if (machineUserData.strDescription.length())
6915 elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
6916 elmMachine.setAttribute("OSType", machineUserData.strOsType);
6917 if ( strStateFile.length()
6918 && !(fl & BuildMachineXML_SuppressSavedState)
6919 )
6920 elmMachine.setAttributePath("stateFile", strStateFile);
6921
6922 if ((fl & BuildMachineXML_IncludeSnapshots)
6923 && !uuidCurrentSnapshot.isZero()
6924 && uuidCurrentSnapshot.isValid())
6925 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
6926
6927 if (machineUserData.strSnapshotFolder.length())
6928 elmMachine.setAttributePath("snapshotFolder", machineUserData.strSnapshotFolder);
6929 if (!fCurrentStateModified)
6930 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
6931 elmMachine.setAttribute("lastStateChange", stringifyTimestamp(timeLastStateChange));
6932 if (fAborted)
6933 elmMachine.setAttribute("aborted", fAborted);
6934 if (machineUserData.strVMPriority.length())
6935 elmMachine.setAttribute("processPriority", machineUserData.strVMPriority);
6936 // Please keep the icon last so that one doesn't have to check if there
6937 // is anything in the line after this very long attribute in the XML.
6938 if (machineUserData.ovIcon.size())
6939 {
6940 Utf8Str strIcon;
6941 toBase64(strIcon, machineUserData.ovIcon);
6942 elmMachine.setAttribute("icon", strIcon);
6943 }
6944 if ( m->sv >= SettingsVersion_v1_9
6945 && ( machineUserData.fTeleporterEnabled
6946 || machineUserData.uTeleporterPort
6947 || !machineUserData.strTeleporterAddress.isEmpty()
6948 || !machineUserData.strTeleporterPassword.isEmpty()
6949 )
6950 )
6951 {
6952 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
6953 pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled);
6954 pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort);
6955 pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress);
6956 pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword);
6957 }
6958
6959 if ( m->sv >= SettingsVersion_v1_11
6960 && ( machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
6961 || machineUserData.uFaultTolerancePort
6962 || machineUserData.uFaultToleranceInterval
6963 || !machineUserData.strFaultToleranceAddress.isEmpty()
6964 )
6965 )
6966 {
6967 xml::ElementNode *pelmFaultTolerance = elmMachine.createChild("FaultTolerance");
6968 switch (machineUserData.enmFaultToleranceState)
6969 {
6970 case FaultToleranceState_Inactive:
6971 pelmFaultTolerance->setAttribute("state", "inactive");
6972 break;
6973 case FaultToleranceState_Master:
6974 pelmFaultTolerance->setAttribute("state", "master");
6975 break;
6976 case FaultToleranceState_Standby:
6977 pelmFaultTolerance->setAttribute("state", "standby");
6978 break;
6979#ifdef VBOX_WITH_XPCOM_CPP_ENUM_HACK
6980 case FaultToleranceState_32BitHack: /* (compiler warnings) */
6981 AssertFailedBreak();
6982#endif
6983 }
6984
6985 pelmFaultTolerance->setAttribute("port", machineUserData.uFaultTolerancePort);
6986 pelmFaultTolerance->setAttribute("address", machineUserData.strFaultToleranceAddress);
6987 pelmFaultTolerance->setAttribute("interval", machineUserData.uFaultToleranceInterval);
6988 pelmFaultTolerance->setAttribute("password", machineUserData.strFaultTolerancePassword);
6989 }
6990
6991 if ( (fl & BuildMachineXML_MediaRegistry)
6992 && (m->sv >= SettingsVersion_v1_11)
6993 )
6994 buildMediaRegistry(elmMachine, mediaRegistry);
6995
6996 buildExtraData(elmMachine, mapExtraDataItems);
6997
6998 if ( (fl & BuildMachineXML_IncludeSnapshots)
6999 && llFirstSnapshot.size())
7000 buildSnapshotXML(1, elmMachine, llFirstSnapshot.front());
7001
7002 buildHardwareXML(elmMachine, hardwareMachine, fl, pllElementsWithUuidAttributes);
7003 buildDebuggingXML(&elmMachine, &debugging);
7004 buildAutostartXML(&elmMachine, &autostart);
7005 buildGroupsXML(&elmMachine, &machineUserData.llGroups);
7006}
7007
7008/**
7009 * Returns true only if the given AudioDriverType is supported on
7010 * the current host platform. For example, this would return false
7011 * for AudioDriverType_DirectSound when compiled on a Linux host.
7012 * @param drv AudioDriverType_* enum to test.
7013 * @return true only if the current host supports that driver.
7014 */
7015/*static*/
7016bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
7017{
7018 switch (drv)
7019 {
7020 case AudioDriverType_Null:
7021#ifdef RT_OS_WINDOWS
7022 case AudioDriverType_DirectSound:
7023#endif
7024#ifdef VBOX_WITH_AUDIO_OSS
7025 case AudioDriverType_OSS:
7026#endif
7027#ifdef VBOX_WITH_AUDIO_ALSA
7028 case AudioDriverType_ALSA:
7029#endif
7030#ifdef VBOX_WITH_AUDIO_PULSE
7031 case AudioDriverType_Pulse:
7032#endif
7033#ifdef RT_OS_DARWIN
7034 case AudioDriverType_CoreAudio:
7035#endif
7036#ifdef RT_OS_OS2
7037 case AudioDriverType_MMPM:
7038#endif
7039 return true;
7040 default: break; /* Shut up MSC. */
7041 }
7042
7043 return false;
7044}
7045
7046/**
7047 * Returns the AudioDriverType_* which should be used by default on this
7048 * host platform. On Linux, this will check at runtime whether PulseAudio
7049 * or ALSA are actually supported on the first call.
7050 *
7051 * @return Default audio driver type for this host platform.
7052 */
7053/*static*/
7054AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
7055{
7056#if defined(RT_OS_WINDOWS)
7057 return AudioDriverType_DirectSound;
7058
7059#elif defined(RT_OS_LINUX)
7060 /* On Linux, we need to check at runtime what's actually supported. */
7061 static RTCLockMtx s_mtx;
7062 static AudioDriverType_T s_enmLinuxDriver = AudioDriverType_Null;
7063 RTCLock lock(s_mtx);
7064 if (s_enmLinuxDriver == AudioDriverType_Null)
7065 {
7066# ifdef VBOX_WITH_AUDIO_PULSE
7067 /* Check for the pulse library & that the pulse audio daemon is running. */
7068 if (RTProcIsRunningByName("pulseaudio") &&
7069 RTLdrIsLoadable("libpulse.so.0"))
7070 s_enmLinuxDriver = AudioDriverType_Pulse;
7071 else
7072# endif /* VBOX_WITH_AUDIO_PULSE */
7073# ifdef VBOX_WITH_AUDIO_ALSA
7074 /* Check if we can load the ALSA library */
7075 if (RTLdrIsLoadable("libasound.so.2"))
7076 s_enmLinuxDriver = AudioDriverType_ALSA;
7077 else
7078# endif /* VBOX_WITH_AUDIO_ALSA */
7079 s_enmLinuxDriver = AudioDriverType_OSS;
7080 }
7081 return s_enmLinuxDriver;
7082
7083#elif defined(RT_OS_DARWIN)
7084 return AudioDriverType_CoreAudio;
7085
7086#elif defined(RT_OS_OS2)
7087 return AudioDriverType_MMPM;
7088
7089#else /* All other platforms. */
7090# ifdef VBOX_WITH_AUDIO_OSS
7091 return AudioDriverType_OSS;
7092# else
7093 /* Return NULL driver as a fallback if nothing of the above is available. */
7094 return AudioDriverType_Null;
7095# endif
7096#endif
7097}
7098
7099/**
7100 * Called from write() before calling ConfigFileBase::createStubDocument().
7101 * This adjusts the settings version in m->sv if incompatible settings require
7102 * a settings bump, whereas otherwise we try to preserve the settings version
7103 * to avoid breaking compatibility with older versions.
7104 *
7105 * We do the checks in here in reverse order: newest first, oldest last, so
7106 * that we avoid unnecessary checks since some of these are expensive.
7107 */
7108void MachineConfigFile::bumpSettingsVersionIfNeeded()
7109{
7110 if (m->sv < SettingsVersion_v1_17)
7111 {
7112 // VirtualBox 6.0 adds nested hardware virtualization, using native API (NEM).
7113 if ( hardwareMachine.fNestedHWVirt
7114 || hardwareMachine.fUseNativeApi)
7115 {
7116 m->sv = SettingsVersion_v1_17;
7117 return;
7118 }
7119
7120 /*
7121 * Check if any serial port uses a non 16550A serial port.
7122 */
7123 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
7124 it != hardwareMachine.llSerialPorts.end();
7125 ++it)
7126 {
7127 const SerialPort &port = *it;
7128 if (port.uartType != UartType_U16550A)
7129 {
7130 m->sv = SettingsVersion_v1_17;
7131 return;
7132 }
7133 }
7134 }
7135
7136 if (m->sv < SettingsVersion_v1_16)
7137 {
7138 // VirtualBox 5.1 adds a NVMe storage controller, paravirt debug
7139 // options, cpu profile, APIC settings (CPU capability and BIOS).
7140
7141 if ( hardwareMachine.strParavirtDebug.isNotEmpty()
7142 || (!hardwareMachine.strCpuProfile.equals("host") && hardwareMachine.strCpuProfile.isNotEmpty())
7143 || hardwareMachine.biosSettings.apicMode != APICMode_APIC
7144 || !hardwareMachine.fAPIC
7145 || hardwareMachine.fX2APIC
7146 || hardwareMachine.fIBPBOnVMExit
7147 || hardwareMachine.fIBPBOnVMEntry
7148 || hardwareMachine.fSpecCtrl
7149 || hardwareMachine.fSpecCtrlByHost)
7150 {
7151 m->sv = SettingsVersion_v1_16;
7152 return;
7153 }
7154
7155 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7156 it != hardwareMachine.storage.llStorageControllers.end();
7157 ++it)
7158 {
7159 const StorageController &sctl = *it;
7160
7161 if (sctl.controllerType == StorageControllerType_NVMe)
7162 {
7163 m->sv = SettingsVersion_v1_16;
7164 return;
7165 }
7166 }
7167
7168 for (CpuIdLeafsList::const_iterator it = hardwareMachine.llCpuIdLeafs.begin();
7169 it != hardwareMachine.llCpuIdLeafs.end();
7170 ++it)
7171 if (it->idxSub != 0)
7172 {
7173 m->sv = SettingsVersion_v1_16;
7174 return;
7175 }
7176 }
7177
7178 if (m->sv < SettingsVersion_v1_15)
7179 {
7180 // VirtualBox 5.0 adds paravirt providers, explicit AHCI port hotplug
7181 // setting, USB storage controller, xHCI, serial port TCP backend
7182 // and VM process priority.
7183
7184 /*
7185 * Check simple configuration bits first, loopy stuff afterwards.
7186 */
7187 if ( hardwareMachine.paravirtProvider != ParavirtProvider_Legacy
7188 || hardwareMachine.uCpuIdPortabilityLevel != 0
7189 || machineUserData.strVMPriority.length())
7190 {
7191 m->sv = SettingsVersion_v1_15;
7192 return;
7193 }
7194
7195 /*
7196 * Check whether the hotpluggable flag of all storage devices differs
7197 * from the default for old settings.
7198 * AHCI ports are hotpluggable by default every other device is not.
7199 * Also check if there are USB storage controllers.
7200 */
7201 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7202 it != hardwareMachine.storage.llStorageControllers.end();
7203 ++it)
7204 {
7205 const StorageController &sctl = *it;
7206
7207 if (sctl.controllerType == StorageControllerType_USB)
7208 {
7209 m->sv = SettingsVersion_v1_15;
7210 return;
7211 }
7212
7213 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
7214 it2 != sctl.llAttachedDevices.end();
7215 ++it2)
7216 {
7217 const AttachedDevice &att = *it2;
7218
7219 if ( ( att.fHotPluggable
7220 && sctl.controllerType != StorageControllerType_IntelAhci)
7221 || ( !att.fHotPluggable
7222 && sctl.controllerType == StorageControllerType_IntelAhci))
7223 {
7224 m->sv = SettingsVersion_v1_15;
7225 return;
7226 }
7227 }
7228 }
7229
7230 /*
7231 * Check if there is an xHCI (USB3) USB controller.
7232 */
7233 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
7234 it != hardwareMachine.usbSettings.llUSBControllers.end();
7235 ++it)
7236 {
7237 const USBController &ctrl = *it;
7238 if (ctrl.enmType == USBControllerType_XHCI)
7239 {
7240 m->sv = SettingsVersion_v1_15;
7241 return;
7242 }
7243 }
7244
7245 /*
7246 * Check if any serial port uses the TCP backend.
7247 */
7248 for (SerialPortsList::const_iterator it = hardwareMachine.llSerialPorts.begin();
7249 it != hardwareMachine.llSerialPorts.end();
7250 ++it)
7251 {
7252 const SerialPort &port = *it;
7253 if (port.portMode == PortMode_TCP)
7254 {
7255 m->sv = SettingsVersion_v1_15;
7256 return;
7257 }
7258 }
7259 }
7260
7261 if (m->sv < SettingsVersion_v1_14)
7262 {
7263 // VirtualBox 4.3 adds default frontend setting, graphics controller
7264 // setting, explicit long mode setting, video capturing and NAT networking.
7265 if ( !hardwareMachine.strDefaultFrontend.isEmpty()
7266 || hardwareMachine.graphicsControllerType != GraphicsControllerType_VBoxVGA
7267 || hardwareMachine.enmLongMode != Hardware::LongMode_Legacy
7268 || machineUserData.ovIcon.size() > 0
7269 || hardwareMachine.fVideoCaptureEnabled)
7270 {
7271 m->sv = SettingsVersion_v1_14;
7272 return;
7273 }
7274 NetworkAdaptersList::const_iterator netit;
7275 for (netit = hardwareMachine.llNetworkAdapters.begin();
7276 netit != hardwareMachine.llNetworkAdapters.end();
7277 ++netit)
7278 {
7279 if (netit->mode == NetworkAttachmentType_NATNetwork)
7280 {
7281 m->sv = SettingsVersion_v1_14;
7282 break;
7283 }
7284 }
7285 }
7286
7287 if (m->sv < SettingsVersion_v1_14)
7288 {
7289 unsigned cOhciCtrls = 0;
7290 unsigned cEhciCtrls = 0;
7291 bool fNonStdName = false;
7292
7293 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
7294 it != hardwareMachine.usbSettings.llUSBControllers.end();
7295 ++it)
7296 {
7297 const USBController &ctrl = *it;
7298
7299 switch (ctrl.enmType)
7300 {
7301 case USBControllerType_OHCI:
7302 cOhciCtrls++;
7303 if (ctrl.strName != "OHCI")
7304 fNonStdName = true;
7305 break;
7306 case USBControllerType_EHCI:
7307 cEhciCtrls++;
7308 if (ctrl.strName != "EHCI")
7309 fNonStdName = true;
7310 break;
7311 default:
7312 /* Anything unknown forces a bump. */
7313 fNonStdName = true;
7314 }
7315
7316 /* Skip checking other controllers if the settings bump is necessary. */
7317 if (cOhciCtrls > 1 || cEhciCtrls > 1 || fNonStdName)
7318 {
7319 m->sv = SettingsVersion_v1_14;
7320 break;
7321 }
7322 }
7323 }
7324
7325 if (m->sv < SettingsVersion_v1_13)
7326 {
7327 // VirtualBox 4.2 adds tracing, autostart, UUID in directory and groups.
7328 if ( !debugging.areDefaultSettings()
7329 || !autostart.areDefaultSettings()
7330 || machineUserData.fDirectoryIncludesUUID
7331 || machineUserData.llGroups.size() > 1
7332 || machineUserData.llGroups.front() != "/")
7333 m->sv = SettingsVersion_v1_13;
7334 }
7335
7336 if (m->sv < SettingsVersion_v1_13)
7337 {
7338 // VirtualBox 4.2 changes the units for bandwidth group limits.
7339 for (BandwidthGroupList::const_iterator it = hardwareMachine.ioSettings.llBandwidthGroups.begin();
7340 it != hardwareMachine.ioSettings.llBandwidthGroups.end();
7341 ++it)
7342 {
7343 const BandwidthGroup &gr = *it;
7344 if (gr.cMaxBytesPerSec % _1M)
7345 {
7346 // Bump version if a limit cannot be expressed in megabytes
7347 m->sv = SettingsVersion_v1_13;
7348 break;
7349 }
7350 }
7351 }
7352
7353 if (m->sv < SettingsVersion_v1_12)
7354 {
7355 // VirtualBox 4.1 adds PCI passthrough and emulated USB Smart Card reader
7356 if ( hardwareMachine.pciAttachments.size()
7357 || hardwareMachine.fEmulatedUSBCardReader)
7358 m->sv = SettingsVersion_v1_12;
7359 }
7360
7361 if (m->sv < SettingsVersion_v1_12)
7362 {
7363 // VirtualBox 4.1 adds a promiscuous mode policy to the network
7364 // adapters and a generic network driver transport.
7365 NetworkAdaptersList::const_iterator netit;
7366 for (netit = hardwareMachine.llNetworkAdapters.begin();
7367 netit != hardwareMachine.llNetworkAdapters.end();
7368 ++netit)
7369 {
7370 if ( netit->enmPromiscModePolicy != NetworkAdapterPromiscModePolicy_Deny
7371 || netit->mode == NetworkAttachmentType_Generic
7372 || !netit->areGenericDriverDefaultSettings()
7373 )
7374 {
7375 m->sv = SettingsVersion_v1_12;
7376 break;
7377 }
7378 }
7379 }
7380
7381 if (m->sv < SettingsVersion_v1_11)
7382 {
7383 // VirtualBox 4.0 adds HD audio, CPU priorities, fault tolerance,
7384 // per-machine media registries, VRDE, JRockitVE, bandwidth groups,
7385 // ICH9 chipset
7386 if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
7387 || hardwareMachine.ulCpuExecutionCap != 100
7388 || machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
7389 || machineUserData.uFaultTolerancePort
7390 || machineUserData.uFaultToleranceInterval
7391 || !machineUserData.strFaultToleranceAddress.isEmpty()
7392 || mediaRegistry.llHardDisks.size()
7393 || mediaRegistry.llDvdImages.size()
7394 || mediaRegistry.llFloppyImages.size()
7395 || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty()
7396 || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty()
7397 || machineUserData.strOsType == "JRockitVE"
7398 || hardwareMachine.ioSettings.llBandwidthGroups.size()
7399 || hardwareMachine.chipsetType == ChipsetType_ICH9
7400 )
7401 m->sv = SettingsVersion_v1_11;
7402 }
7403
7404 if (m->sv < SettingsVersion_v1_10)
7405 {
7406 /* If the properties contain elements other than "TCP/Ports" and "TCP/Address",
7407 * then increase the version to at least VBox 3.2, which can have video channel properties.
7408 */
7409 unsigned cOldProperties = 0;
7410
7411 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
7412 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7413 cOldProperties++;
7414 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
7415 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7416 cOldProperties++;
7417
7418 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
7419 m->sv = SettingsVersion_v1_10;
7420 }
7421
7422 if (m->sv < SettingsVersion_v1_11)
7423 {
7424 /* If the properties contain elements other than "TCP/Ports", "TCP/Address",
7425 * "VideoChannel/Enabled" and "VideoChannel/Quality" then increase the version to VBox 4.0.
7426 */
7427 unsigned cOldProperties = 0;
7428
7429 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
7430 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7431 cOldProperties++;
7432 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
7433 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7434 cOldProperties++;
7435 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
7436 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7437 cOldProperties++;
7438 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Quality");
7439 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
7440 cOldProperties++;
7441
7442 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
7443 m->sv = SettingsVersion_v1_11;
7444 }
7445
7446 // settings version 1.9 is required if there is not exactly one DVD
7447 // or more than one floppy drive present or the DVD is not at the secondary
7448 // master; this check is a bit more complicated
7449 //
7450 // settings version 1.10 is required if the host cache should be disabled
7451 //
7452 // settings version 1.11 is required for bandwidth limits and if more than
7453 // one controller of each type is present.
7454 if (m->sv < SettingsVersion_v1_11)
7455 {
7456 // count attached DVDs and floppies (only if < v1.9)
7457 size_t cDVDs = 0;
7458 size_t cFloppies = 0;
7459
7460 // count storage controllers (if < v1.11)
7461 size_t cSata = 0;
7462 size_t cScsiLsi = 0;
7463 size_t cScsiBuslogic = 0;
7464 size_t cSas = 0;
7465 size_t cIde = 0;
7466 size_t cFloppy = 0;
7467
7468 // need to run thru all the storage controllers and attached devices to figure this out
7469 for (StorageControllersList::const_iterator it = hardwareMachine.storage.llStorageControllers.begin();
7470 it != hardwareMachine.storage.llStorageControllers.end();
7471 ++it)
7472 {
7473 const StorageController &sctl = *it;
7474
7475 // count storage controllers of each type; 1.11 is required if more than one
7476 // controller of one type is present
7477 switch (sctl.storageBus)
7478 {
7479 case StorageBus_IDE:
7480 cIde++;
7481 break;
7482 case StorageBus_SATA:
7483 cSata++;
7484 break;
7485 case StorageBus_SAS:
7486 cSas++;
7487 break;
7488 case StorageBus_SCSI:
7489 if (sctl.controllerType == StorageControllerType_LsiLogic)
7490 cScsiLsi++;
7491 else
7492 cScsiBuslogic++;
7493 break;
7494 case StorageBus_Floppy:
7495 cFloppy++;
7496 break;
7497 default:
7498 // Do nothing
7499 break;
7500 }
7501
7502 if ( cSata > 1
7503 || cScsiLsi > 1
7504 || cScsiBuslogic > 1
7505 || cSas > 1
7506 || cIde > 1
7507 || cFloppy > 1)
7508 {
7509 m->sv = SettingsVersion_v1_11;
7510 break; // abort the loop -- we will not raise the version further
7511 }
7512
7513 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
7514 it2 != sctl.llAttachedDevices.end();
7515 ++it2)
7516 {
7517 const AttachedDevice &att = *it2;
7518
7519 // Bandwidth limitations are new in VirtualBox 4.0 (1.11)
7520 if (m->sv < SettingsVersion_v1_11)
7521 {
7522 if (att.strBwGroup.length() != 0)
7523 {
7524 m->sv = SettingsVersion_v1_11;
7525 break; // abort the loop -- we will not raise the version further
7526 }
7527 }
7528
7529 // disabling the host IO cache requires settings version 1.10
7530 if ( (m->sv < SettingsVersion_v1_10)
7531 && (!sctl.fUseHostIOCache)
7532 )
7533 m->sv = SettingsVersion_v1_10;
7534
7535 // we can only write the StorageController/@Instance attribute with v1.9
7536 if ( (m->sv < SettingsVersion_v1_9)
7537 && (sctl.ulInstance != 0)
7538 )
7539 m->sv = SettingsVersion_v1_9;
7540
7541 if (m->sv < SettingsVersion_v1_9)
7542 {
7543 if (att.deviceType == DeviceType_DVD)
7544 {
7545 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
7546 || (att.lPort != 1) // DVDs not at secondary master?
7547 || (att.lDevice != 0)
7548 )
7549 m->sv = SettingsVersion_v1_9;
7550
7551 ++cDVDs;
7552 }
7553 else if (att.deviceType == DeviceType_Floppy)
7554 ++cFloppies;
7555 }
7556 }
7557
7558 if (m->sv >= SettingsVersion_v1_11)
7559 break; // abort the loop -- we will not raise the version further
7560 }
7561
7562 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
7563 // so any deviation from that will require settings version 1.9
7564 if ( (m->sv < SettingsVersion_v1_9)
7565 && ( (cDVDs != 1)
7566 || (cFloppies > 1)
7567 )
7568 )
7569 m->sv = SettingsVersion_v1_9;
7570 }
7571
7572 // VirtualBox 3.2: Check for non default I/O settings
7573 if (m->sv < SettingsVersion_v1_10)
7574 {
7575 if ( (hardwareMachine.ioSettings.fIOCacheEnabled != true)
7576 || (hardwareMachine.ioSettings.ulIOCacheSize != 5)
7577 // and page fusion
7578 || (hardwareMachine.fPageFusionEnabled)
7579 // and CPU hotplug, RTC timezone control, HID type and HPET
7580 || machineUserData.fRTCUseUTC
7581 || hardwareMachine.fCpuHotPlug
7582 || hardwareMachine.pointingHIDType != PointingHIDType_PS2Mouse
7583 || hardwareMachine.keyboardHIDType != KeyboardHIDType_PS2Keyboard
7584 || hardwareMachine.fHPETEnabled
7585 )
7586 m->sv = SettingsVersion_v1_10;
7587 }
7588
7589 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main
7590 // VirtualBox 4.0 adds network bandwitdth
7591 if (m->sv < SettingsVersion_v1_11)
7592 {
7593 NetworkAdaptersList::const_iterator netit;
7594 for (netit = hardwareMachine.llNetworkAdapters.begin();
7595 netit != hardwareMachine.llNetworkAdapters.end();
7596 ++netit)
7597 {
7598 if ( (m->sv < SettingsVersion_v1_12)
7599 && (netit->strBandwidthGroup.isNotEmpty())
7600 )
7601 {
7602 /* New in VirtualBox 4.1 */
7603 m->sv = SettingsVersion_v1_12;
7604 break;
7605 }
7606 else if ( (m->sv < SettingsVersion_v1_10)
7607 && (netit->fEnabled)
7608 && (netit->mode == NetworkAttachmentType_NAT)
7609 && ( netit->nat.u32Mtu != 0
7610 || netit->nat.u32SockRcv != 0
7611 || netit->nat.u32SockSnd != 0
7612 || netit->nat.u32TcpRcv != 0
7613 || netit->nat.u32TcpSnd != 0
7614 || !netit->nat.fDNSPassDomain
7615 || netit->nat.fDNSProxy
7616 || netit->nat.fDNSUseHostResolver
7617 || netit->nat.fAliasLog
7618 || netit->nat.fAliasProxyOnly
7619 || netit->nat.fAliasUseSamePorts
7620 || netit->nat.strTFTPPrefix.length()
7621 || netit->nat.strTFTPBootFile.length()
7622 || netit->nat.strTFTPNextServer.length()
7623 || netit->nat.mapRules.size()
7624 )
7625 )
7626 {
7627 m->sv = SettingsVersion_v1_10;
7628 // no break because we still might need v1.11 above
7629 }
7630 else if ( (m->sv < SettingsVersion_v1_10)
7631 && (netit->fEnabled)
7632 && (netit->ulBootPriority != 0)
7633 )
7634 {
7635 m->sv = SettingsVersion_v1_10;
7636 // no break because we still might need v1.11 above
7637 }
7638 }
7639 }
7640
7641 // all the following require settings version 1.9
7642 if ( (m->sv < SettingsVersion_v1_9)
7643 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
7644 || machineUserData.fTeleporterEnabled
7645 || machineUserData.uTeleporterPort
7646 || !machineUserData.strTeleporterAddress.isEmpty()
7647 || !machineUserData.strTeleporterPassword.isEmpty()
7648 || (!hardwareMachine.uuid.isZero() && hardwareMachine.uuid.isValid())
7649 )
7650 )
7651 m->sv = SettingsVersion_v1_9;
7652
7653 // "accelerate 2d video" requires settings version 1.8
7654 if ( (m->sv < SettingsVersion_v1_8)
7655 && (hardwareMachine.fAccelerate2DVideo)
7656 )
7657 m->sv = SettingsVersion_v1_8;
7658
7659 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
7660 if ( m->sv < SettingsVersion_v1_4
7661 && hardwareMachine.strVersion != "1"
7662 )
7663 m->sv = SettingsVersion_v1_4;
7664}
7665
7666/**
7667 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
7668 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
7669 * in particular if the file cannot be written.
7670 */
7671void MachineConfigFile::write(const com::Utf8Str &strFilename)
7672{
7673 try
7674 {
7675 // createStubDocument() sets the settings version to at least 1.7; however,
7676 // we might need to enfore a later settings version if incompatible settings
7677 // are present:
7678 bumpSettingsVersionIfNeeded();
7679
7680 m->strFilename = strFilename;
7681 specialBackupIfFirstBump();
7682 createStubDocument();
7683
7684 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
7685 buildMachineXML(*pelmMachine,
7686 MachineConfigFile::BuildMachineXML_IncludeSnapshots
7687 | MachineConfigFile::BuildMachineXML_MediaRegistry,
7688 // but not BuildMachineXML_WriteVBoxVersionAttribute
7689 NULL); /* pllElementsWithUuidAttributes */
7690
7691 // now go write the XML
7692 xml::XmlFileWriter writer(*m->pDoc);
7693 writer.write(m->strFilename.c_str(), true /*fSafe*/);
7694
7695 m->fFileExists = true;
7696 clearDocument();
7697 }
7698 catch (...)
7699 {
7700 clearDocument();
7701 throw;
7702 }
7703}
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