VirtualBox

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

Last change on this file since 98123 was 98103, checked in by vboxsync, 23 months ago

Copyright year updates by scm.

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