VirtualBox

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

Last change on this file since 83323 was 83170, checked in by vboxsync, 5 years ago

Build fixes for r136290.

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

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