VirtualBox

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

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

Main/Update check Removed update agent-specific proxy settings. ​bugref:7983

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

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