VirtualBox

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

Last change on this file since 76542 was 76366, checked in by vboxsync, 6 years ago

include/VBox/com/Guid.h: Don't include iprt/err.h for no good reason. bugref:9344

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

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