VirtualBox

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

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

Capturing: Build fix.

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