VirtualBox

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

Last change on this file since 86692 was 85769, checked in by vboxsync, 4 years ago

Main: bugref:9618 Added Main/API support for AMD-V Virtualized VMSAVE/VMLOAD hardware virtualization feature.

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

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