VirtualBox

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

Last change on this file since 94727 was 94727, checked in by vboxsync, 3 years ago

Main/Settings: Implement encrypting/decrypting a VM config, bugref:9955 [doxygen fix]

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

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