VirtualBox

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

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

Devices: bugref:9932 DrvVMNet and host-only network initial implementation

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

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