VirtualBox

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

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

Main/Machine+BIOSSettings: simplify NVRAM handling, do everything in backwards c
ompatible way
Frontends/VBoxManage: adapt to API change

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

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette