VirtualBox

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

Last change on this file since 79778 was 79778, checked in by vboxsync, 5 years ago

Main: s/DHCPOptionEncoding_Legacy/DHCPOptionEncoding_Normal/g as 'Legacy' does not seem a good fit for the more userfriendly value encoding. bugref:9288

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