VirtualBox

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

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

GuestPropertySvc.h,Main: Changed GuestPropValidateName and GuestPropValidateValue to take size_t instead of uint32_t, eliminating the need for a bunch of (uint32_t) casts. Call setErrorBoth when either of the two fails in Main. bugref:10185

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