VirtualBox

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

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

Recording: Renaming APIs ICapture* -> IRecord* and other terminology to better distinguish from features like mouse capturing and stuff.

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