VirtualBox

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

最後變更 在這個檔案從84626是 84626,由 vboxsync 提交於 5 年 前

Main/Settings.cpp: bugref:9558 ticketref:19527 apply new default of having audio in/out disabled to VBox 5.2 and later (update to r118168)

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

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