VirtualBox

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

Last change on this file since 81953 was 81882, checked in by vboxsync, 6 years ago

Main/xml: 6.1 will save StorageControllers under Hardware for all VMs with version 1.17 (i.e. using features of 6.0) and later. This is already nderstood by 5.2 and 6.0, so there is no problem with backwards compatibility. Additionally some xml validation fixes.

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

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