VirtualBox

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

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

Main,FE/VBoxManage: Allow changing the UART type of the serial ports through the API

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

© 2023 Oracle
ContactPrivacy policyTerms of Use