VirtualBox

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

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

Main/FE: Added new audio driver type "default" to make it possible to move VMs (appliances) between different platforms without the need of changing the audio driver explicitly. When the "default" driver is selected, the best audio backend option for a platform will be used.

While at it, also added the "WAS" (Windows Audio Session) driver type for which we only had a hack so far. This now can be explicitly selected, also by the frontends. bugref:10051

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