VirtualBox

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

Last change on this file since 95685 was 95662, checked in by vboxsync, 2 years ago

Recording/Main: Fixed migration from older settings (< 1.19). ​bugref:9286

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