VirtualBox

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

最後變更 在這個檔案從90297是 88345,由 vboxsync 提交於 4 年 前

Intel IOMMU: bugref:9967 Main, VBoxManage: Handle Intel IOMMU in places where it's missing. Without VBOX_WITH_IOMMU_INTEL define, it's considered not implemented.

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

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