VirtualBox

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

Last change on this file since 74431 was 74431, checked in by vboxsync, 6 years ago

Main,GUI,VBoxManage: Provide API for proxy settings that picks up the current GUI settings and converts it to SystemProperties XML attributes. Adjusted GUI to use it. Updated VBoxMangage system properties handling accordingly. bugref:9249

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