VirtualBox

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

Last change on this file since 71735 was 71179, checked in by vboxsync, 7 years ago

Fixing few VirtualBox related typos in various places, mostly GUI, GA, Main.

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