VirtualBox

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

Last change on this file since 85416 was 85285, checked in by vboxsync, 4 years ago

Main/Settings.cpp: Signed/unsigned conversion issue. bugref:9790

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

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