VirtualBox

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

Last change on this file since 78517 was 78509, checked in by vboxsync, 6 years ago

Main/Machine+StorageController+SystemProperties+Console: Add basic support for virtio-scsi storage controller.

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

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