VirtualBox

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

Last change on this file since 54657 was 54314, checked in by vboxsync, 10 years ago

Main/DHCPServerImpl: for DHCP options keep not just the text
representation, but also encoding of that text representation.

When XML settings are read, interpret old format, without explicit
"encoding" attribute, as legacy encoding where we are expected to know
the actual format of the option from the option code itself. When
writing legacy options, write them in old format.

This is in preparation for using "de:ad:be:ef" hex-encoded option
values for non-standard options which format we can't know a priory.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 245.4 KB
Line 
1/* $Id: Settings.cpp 54314 2015-02-19 21:32:18Z 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
56/*
57 * Copyright (C) 2007-2014 Oracle Corporation
58 *
59 * This file is part of VirtualBox Open Source Edition (OSE), as
60 * available from http://www.virtualbox.org. This file is free software;
61 * you can redistribute it and/or modify it under the terms of the GNU
62 * General Public License (GPL) as published by the Free Software
63 * Foundation, in version 2 as it comes in the "COPYING" file of the
64 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
65 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
66 */
67
68#include "VBox/com/string.h"
69#include "VBox/settings.h"
70#include <iprt/cpp/xml.h>
71#include <iprt/stream.h>
72#include <iprt/ctype.h>
73#include <iprt/file.h>
74#include <iprt/process.h>
75#include <iprt/ldr.h>
76#include <iprt/cpp/lock.h>
77
78// generated header
79#include "SchemaDefs.h"
80
81#include "Logging.h"
82#include "HashedPw.h"
83
84using namespace com;
85using namespace settings;
86
87////////////////////////////////////////////////////////////////////////////////
88//
89// Defines
90//
91////////////////////////////////////////////////////////////////////////////////
92
93/** VirtualBox XML settings namespace */
94#define VBOX_XML_NAMESPACE "http://www.innotek.de/VirtualBox-settings"
95
96/** VirtualBox XML settings version number substring ("x.y") */
97#define VBOX_XML_VERSION "1.12"
98
99/** VirtualBox XML settings version platform substring */
100#if defined (RT_OS_DARWIN)
101# define VBOX_XML_PLATFORM "macosx"
102#elif defined (RT_OS_FREEBSD)
103# define VBOX_XML_PLATFORM "freebsd"
104#elif defined (RT_OS_LINUX)
105# define VBOX_XML_PLATFORM "linux"
106#elif defined (RT_OS_NETBSD)
107# define VBOX_XML_PLATFORM "netbsd"
108#elif defined (RT_OS_OPENBSD)
109# define VBOX_XML_PLATFORM "openbsd"
110#elif defined (RT_OS_OS2)
111# define VBOX_XML_PLATFORM "os2"
112#elif defined (RT_OS_SOLARIS)
113# define VBOX_XML_PLATFORM "solaris"
114#elif defined (RT_OS_WINDOWS)
115# define VBOX_XML_PLATFORM "windows"
116#else
117# error Unsupported platform!
118#endif
119
120/** VirtualBox XML settings full version string ("x.y-platform") */
121#define VBOX_XML_VERSION_FULL VBOX_XML_VERSION "-" VBOX_XML_PLATFORM
122
123////////////////////////////////////////////////////////////////////////////////
124//
125// Internal data
126//
127////////////////////////////////////////////////////////////////////////////////
128
129/**
130 * Opaque data structore for ConfigFileBase (only declared
131 * in header, defined only here).
132 */
133
134struct ConfigFileBase::Data
135{
136 Data()
137 : pDoc(NULL),
138 pelmRoot(NULL),
139 sv(SettingsVersion_Null),
140 svRead(SettingsVersion_Null)
141 {}
142
143 ~Data()
144 {
145 cleanup();
146 }
147
148 RTCString strFilename;
149 bool fFileExists;
150
151 xml::Document *pDoc;
152 xml::ElementNode *pelmRoot;
153
154 com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux"
155 SettingsVersion_T sv; // e.g. SettingsVersion_v1_7
156
157 SettingsVersion_T svRead; // settings version that the original file had when it was read,
158 // or SettingsVersion_Null if none
159
160 void copyFrom(const Data &d)
161 {
162 strFilename = d.strFilename;
163 fFileExists = d.fFileExists;
164 strSettingsVersionFull = d.strSettingsVersionFull;
165 sv = d.sv;
166 svRead = d.svRead;
167 }
168
169 void cleanup()
170 {
171 if (pDoc)
172 {
173 delete pDoc;
174 pDoc = NULL;
175 pelmRoot = NULL;
176 }
177 }
178};
179
180/**
181 * Private exception class (not in the header file) that makes
182 * throwing xml::LogicError instances easier. That class is public
183 * and should be caught by client code.
184 */
185class settings::ConfigFileError : public xml::LogicError
186{
187public:
188 ConfigFileError(const ConfigFileBase *file,
189 const xml::Node *pNode,
190 const char *pcszFormat, ...)
191 : xml::LogicError()
192 {
193 va_list args;
194 va_start(args, pcszFormat);
195 Utf8Str strWhat(pcszFormat, args);
196 va_end(args);
197
198 Utf8Str strLine;
199 if (pNode)
200 strLine = Utf8StrFmt(" (line %RU32)", pNode->getLineNumber());
201
202 const char *pcsz = strLine.c_str();
203 Utf8StrFmt str(N_("Error in %s%s -- %s"),
204 file->m->strFilename.c_str(),
205 (pcsz) ? pcsz : "",
206 strWhat.c_str());
207
208 setWhat(str.c_str());
209 }
210};
211
212////////////////////////////////////////////////////////////////////////////////
213//
214// MediaRegistry
215//
216////////////////////////////////////////////////////////////////////////////////
217
218bool Medium::operator==(const Medium &m) const
219{
220 return (uuid == m.uuid)
221 && (strLocation == m.strLocation)
222 && (strDescription == m.strDescription)
223 && (strFormat == m.strFormat)
224 && (fAutoReset == m.fAutoReset)
225 && (properties == m.properties)
226 && (hdType == m.hdType)
227 && (llChildren== m.llChildren); // this is deep and recurses
228}
229
230bool MediaRegistry::operator==(const MediaRegistry &m) const
231{
232 return llHardDisks == m.llHardDisks
233 && llDvdImages == m.llDvdImages
234 && llFloppyImages == m.llFloppyImages;
235}
236
237////////////////////////////////////////////////////////////////////////////////
238//
239// ConfigFileBase
240//
241////////////////////////////////////////////////////////////////////////////////
242
243/**
244 * Constructor. Allocates the XML internals, parses the XML file if
245 * pstrFilename is != NULL and reads the settings version from it.
246 * @param strFilename
247 */
248ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
249 : m(new Data)
250{
251 Utf8Str strMajor;
252 Utf8Str strMinor;
253
254 m->fFileExists = false;
255
256 if (pstrFilename)
257 {
258 // reading existing settings file:
259 m->strFilename = *pstrFilename;
260
261 xml::XmlFileParser parser;
262 m->pDoc = new xml::Document;
263 parser.read(*pstrFilename,
264 *m->pDoc);
265
266 m->fFileExists = true;
267
268 m->pelmRoot = m->pDoc->getRootElement();
269 if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
270 throw ConfigFileError(this, NULL, N_("Root element in VirtualBox settings files must be \"VirtualBox\"."));
271
272 if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
273 throw ConfigFileError(this, m->pelmRoot, N_("Required VirtualBox/@version attribute is missing"));
274
275 LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
276
277 // parse settings version; allow future versions but fail if file is older than 1.6
278 m->sv = SettingsVersion_Null;
279 if (m->strSettingsVersionFull.length() > 3)
280 {
281 const char *pcsz = m->strSettingsVersionFull.c_str();
282 char c;
283
284 while ( (c = *pcsz)
285 && RT_C_IS_DIGIT(c)
286 )
287 {
288 strMajor.append(c);
289 ++pcsz;
290 }
291
292 if (*pcsz++ == '.')
293 {
294 while ( (c = *pcsz)
295 && RT_C_IS_DIGIT(c)
296 )
297 {
298 strMinor.append(c);
299 ++pcsz;
300 }
301 }
302
303 uint32_t ulMajor = RTStrToUInt32(strMajor.c_str());
304 uint32_t ulMinor = RTStrToUInt32(strMinor.c_str());
305
306 if (ulMajor == 1)
307 {
308 if (ulMinor == 3)
309 m->sv = SettingsVersion_v1_3;
310 else if (ulMinor == 4)
311 m->sv = SettingsVersion_v1_4;
312 else if (ulMinor == 5)
313 m->sv = SettingsVersion_v1_5;
314 else if (ulMinor == 6)
315 m->sv = SettingsVersion_v1_6;
316 else if (ulMinor == 7)
317 m->sv = SettingsVersion_v1_7;
318 else if (ulMinor == 8)
319 m->sv = SettingsVersion_v1_8;
320 else if (ulMinor == 9)
321 m->sv = SettingsVersion_v1_9;
322 else if (ulMinor == 10)
323 m->sv = SettingsVersion_v1_10;
324 else if (ulMinor == 11)
325 m->sv = SettingsVersion_v1_11;
326 else if (ulMinor == 12)
327 m->sv = SettingsVersion_v1_12;
328 else if (ulMinor == 13)
329 m->sv = SettingsVersion_v1_13;
330 else if (ulMinor == 14)
331 m->sv = SettingsVersion_v1_14;
332 else if (ulMinor == 15)
333 m->sv = SettingsVersion_v1_15;
334 else if (ulMinor > 15)
335 m->sv = SettingsVersion_Future;
336 }
337 else if (ulMajor > 1)
338 m->sv = SettingsVersion_Future;
339
340 Log(("Parsed settings version %d.%d to enum value %d\n", ulMajor, ulMinor, m->sv));
341 }
342
343 if (m->sv == SettingsVersion_Null)
344 throw ConfigFileError(this, m->pelmRoot, N_("Cannot handle settings version '%s'"), m->strSettingsVersionFull.c_str());
345
346 // remember the settings version we read in case it gets upgraded later,
347 // so we know when to make backups
348 m->svRead = m->sv;
349 }
350 else
351 {
352 // creating new settings file:
353 m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
354 m->sv = SettingsVersion_v1_12;
355 }
356}
357
358ConfigFileBase::ConfigFileBase(const ConfigFileBase &other)
359 : m(new Data)
360{
361 copyBaseFrom(other);
362 m->strFilename = "";
363 m->fFileExists = false;
364}
365
366/**
367 * Clean up.
368 */
369ConfigFileBase::~ConfigFileBase()
370{
371 if (m)
372 {
373 delete m;
374 m = NULL;
375 }
376}
377
378/**
379 * Helper function that parses a UUID in string form into
380 * a com::Guid item. Accepts UUIDs both with and without
381 * "{}" brackets. Throws on errors.
382 * @param guid
383 * @param strUUID
384 */
385void ConfigFileBase::parseUUID(Guid &guid,
386 const Utf8Str &strUUID) const
387{
388 guid = strUUID.c_str();
389 if (guid.isZero())
390 throw ConfigFileError(this, NULL, N_("UUID \"%s\" has zero format"), strUUID.c_str());
391 else if (!guid.isValid())
392 throw ConfigFileError(this, NULL, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
393}
394
395/**
396 * Parses the given string in str and attempts to treat it as an ISO
397 * date/time stamp to put into timestamp. Throws on errors.
398 * @param timestamp
399 * @param str
400 */
401void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
402 const com::Utf8Str &str) const
403{
404 const char *pcsz = str.c_str();
405 // yyyy-mm-ddThh:mm:ss
406 // "2009-07-10T11:54:03Z"
407 // 01234567890123456789
408 // 1
409 if (str.length() > 19)
410 {
411 // timezone must either be unspecified or 'Z' for UTC
412 if ( (pcsz[19])
413 && (pcsz[19] != 'Z')
414 )
415 throw ConfigFileError(this, NULL, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
416
417 int32_t yyyy;
418 uint32_t mm, dd, hh, min, secs;
419 if ( (pcsz[4] == '-')
420 && (pcsz[7] == '-')
421 && (pcsz[10] == 'T')
422 && (pcsz[13] == ':')
423 && (pcsz[16] == ':')
424 )
425 {
426 int rc;
427 if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
428 // could theoretically be negative but let's assume that nobody
429 // created virtual machines before the Christian era
430 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
431 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
432 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
433 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
434 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
435 )
436 {
437 RTTIME time =
438 {
439 yyyy,
440 (uint8_t)mm,
441 0,
442 0,
443 (uint8_t)dd,
444 (uint8_t)hh,
445 (uint8_t)min,
446 (uint8_t)secs,
447 0,
448 RTTIME_FLAGS_TYPE_UTC,
449 0
450 };
451 if (RTTimeNormalize(&time))
452 if (RTTimeImplode(&timestamp, &time))
453 return;
454 }
455
456 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
457 }
458
459 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
460 }
461}
462
463/**
464 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
465 * @param stamp
466 * @return
467 */
468com::Utf8Str ConfigFileBase::makeString(const RTTIMESPEC &stamp)
469{
470 RTTIME time;
471 if (!RTTimeExplode(&time, &stamp))
472 throw ConfigFileError(this, NULL, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
473
474 return Utf8StrFmt("%04u-%02u-%02uT%02u:%02u:%02uZ",
475 time.i32Year, time.u8Month, time.u8MonthDay,
476 time.u8Hour, time.u8Minute, time.u8Second);
477}
478
479/**
480 * Helper method to read in an ExtraData subtree and stores its contents
481 * in the given map of extradata items. Used for both main and machine
482 * extradata (MainConfigFile and MachineConfigFile).
483 * @param elmExtraData
484 * @param map
485 */
486void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
487 StringsMap &map)
488{
489 xml::NodesLoop nlLevel4(elmExtraData);
490 const xml::ElementNode *pelmExtraDataItem;
491 while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
492 {
493 if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
494 {
495 // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
496 Utf8Str strName, strValue;
497 if ( pelmExtraDataItem->getAttributeValue("name", strName)
498 && pelmExtraDataItem->getAttributeValue("value", strValue) )
499 map[strName] = strValue;
500 else
501 throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing"));
502 }
503 }
504}
505
506/**
507 * Reads <USBDeviceFilter> entries from under the given elmDeviceFilters node and
508 * stores them in the given linklist. This is in ConfigFileBase because it's used
509 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
510 * filters).
511 * @param elmDeviceFilters
512 * @param ll
513 */
514void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
515 USBDeviceFiltersList &ll)
516{
517 xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
518 const xml::ElementNode *pelmLevel4Child;
519 while ((pelmLevel4Child = nl1.forAllNodes()))
520 {
521 USBDeviceFilter flt;
522 flt.action = USBDeviceFilterAction_Ignore;
523 Utf8Str strAction;
524 if ( pelmLevel4Child->getAttributeValue("name", flt.strName)
525 && pelmLevel4Child->getAttributeValue("active", flt.fActive))
526 {
527 if (!pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId))
528 pelmLevel4Child->getAttributeValue("vendorid", flt.strVendorId); // used before 1.3
529 if (!pelmLevel4Child->getAttributeValue("productId", flt.strProductId))
530 pelmLevel4Child->getAttributeValue("productid", flt.strProductId); // used before 1.3
531 pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
532 pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
533 pelmLevel4Child->getAttributeValue("product", flt.strProduct);
534 if (!pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber))
535 pelmLevel4Child->getAttributeValue("serialnumber", flt.strSerialNumber); // used before 1.3
536 pelmLevel4Child->getAttributeValue("port", flt.strPort);
537
538 // the next 2 are irrelevant for host USB objects
539 pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
540 pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
541
542 // action is only used with host USB objects
543 if (pelmLevel4Child->getAttributeValue("action", strAction))
544 {
545 if (strAction == "Ignore")
546 flt.action = USBDeviceFilterAction_Ignore;
547 else if (strAction == "Hold")
548 flt.action = USBDeviceFilterAction_Hold;
549 else
550 throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str());
551 }
552
553 ll.push_back(flt);
554 }
555 }
556}
557
558/**
559 * Reads a media registry entry from the main VirtualBox.xml file.
560 *
561 * Whereas the current media registry code is fairly straightforward, it was quite a mess
562 * with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements
563 * in the media registry were much more inconsistent, and different elements were used
564 * depending on the type of device and image.
565 *
566 * @param t
567 * @param elmMedium
568 * @param llMedia
569 */
570void ConfigFileBase::readMedium(MediaType t,
571 const xml::ElementNode &elmMedium, // HardDisk node if root; if recursing,
572 // child HardDisk node or DiffHardDisk node for pre-1.4
573 MediaList &llMedia) // list to append medium to (root disk or child list)
574{
575 // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
576 settings::Medium med;
577 Utf8Str strUUID;
578 if (!elmMedium.getAttributeValue("uuid", strUUID))
579 throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName());
580
581 parseUUID(med.uuid, strUUID);
582
583 bool fNeedsLocation = true;
584
585 if (t == HardDisk)
586 {
587 if (m->sv < SettingsVersion_v1_4)
588 {
589 // here the system is:
590 // <HardDisk uuid="{....}" type="normal">
591 // <VirtualDiskImage filePath="/path/to/xxx.vdi"/>
592 // </HardDisk>
593
594 fNeedsLocation = false;
595 bool fNeedsFilePath = true;
596 const xml::ElementNode *pelmImage;
597 if ((pelmImage = elmMedium.findChildElement("VirtualDiskImage")))
598 med.strFormat = "VDI";
599 else if ((pelmImage = elmMedium.findChildElement("VMDKImage")))
600 med.strFormat = "VMDK";
601 else if ((pelmImage = elmMedium.findChildElement("VHDImage")))
602 med.strFormat = "VHD";
603 else if ((pelmImage = elmMedium.findChildElement("ISCSIHardDisk")))
604 {
605 med.strFormat = "iSCSI";
606
607 fNeedsFilePath = false;
608 // location is special here: current settings specify an "iscsi://user@server:port/target/lun"
609 // string for the location and also have several disk properties for these, whereas this used
610 // to be hidden in several sub-elements before 1.4, so compose a location string and set up
611 // the properties:
612 med.strLocation = "iscsi://";
613 Utf8Str strUser, strServer, strPort, strTarget, strLun;
614 if (pelmImage->getAttributeValue("userName", strUser))
615 {
616 med.strLocation.append(strUser);
617 med.strLocation.append("@");
618 }
619 Utf8Str strServerAndPort;
620 if (pelmImage->getAttributeValue("server", strServer))
621 {
622 strServerAndPort = strServer;
623 }
624 if (pelmImage->getAttributeValue("port", strPort))
625 {
626 if (strServerAndPort.length())
627 strServerAndPort.append(":");
628 strServerAndPort.append(strPort);
629 }
630 med.strLocation.append(strServerAndPort);
631 if (pelmImage->getAttributeValue("target", strTarget))
632 {
633 med.strLocation.append("/");
634 med.strLocation.append(strTarget);
635 }
636 if (pelmImage->getAttributeValue("lun", strLun))
637 {
638 med.strLocation.append("/");
639 med.strLocation.append(strLun);
640 }
641
642 if (strServer.length() && strPort.length())
643 med.properties["TargetAddress"] = strServerAndPort;
644 if (strTarget.length())
645 med.properties["TargetName"] = strTarget;
646 if (strUser.length())
647 med.properties["InitiatorUsername"] = strUser;
648 Utf8Str strPassword;
649 if (pelmImage->getAttributeValue("password", strPassword))
650 med.properties["InitiatorSecret"] = strPassword;
651 if (strLun.length())
652 med.properties["LUN"] = strLun;
653 }
654 else if ((pelmImage = elmMedium.findChildElement("CustomHardDisk")))
655 {
656 fNeedsFilePath = false;
657 fNeedsLocation = true;
658 // also requires @format attribute, which will be queried below
659 }
660 else
661 throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName());
662
663 if (fNeedsFilePath)
664 {
665 if (!(pelmImage->getAttributeValuePath("filePath", med.strLocation)))
666 throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName());
667 }
668 }
669
670 if (med.strFormat.isEmpty()) // not set with 1.4 format above, or 1.4 Custom format?
671 if (!elmMedium.getAttributeValue("format", med.strFormat))
672 throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName());
673
674 if (!elmMedium.getAttributeValue("autoReset", med.fAutoReset))
675 med.fAutoReset = false;
676
677 Utf8Str strType;
678 if (elmMedium.getAttributeValue("type", strType))
679 {
680 // pre-1.4 used lower case, so make this case-insensitive
681 strType.toUpper();
682 if (strType == "NORMAL")
683 med.hdType = MediumType_Normal;
684 else if (strType == "IMMUTABLE")
685 med.hdType = MediumType_Immutable;
686 else if (strType == "WRITETHROUGH")
687 med.hdType = MediumType_Writethrough;
688 else if (strType == "SHAREABLE")
689 med.hdType = MediumType_Shareable;
690 else if (strType == "READONLY")
691 med.hdType = MediumType_Readonly;
692 else if (strType == "MULTIATTACH")
693 med.hdType = MediumType_MultiAttach;
694 else
695 throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable, Writethrough, Shareable, Readonly or MultiAttach"));
696 }
697 }
698 else
699 {
700 if (m->sv < SettingsVersion_v1_4)
701 {
702 // DVD and floppy images before 1.4 had "src" attribute instead of "location"
703 if (!elmMedium.getAttributeValue("src", med.strLocation))
704 throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName());
705
706 fNeedsLocation = false;
707 }
708
709 if (!elmMedium.getAttributeValue("format", med.strFormat))
710 {
711 // DVD and floppy images before 1.11 had no format attribute. assign the default.
712 med.strFormat = "RAW";
713 }
714
715 if (t == DVDImage)
716 med.hdType = MediumType_Readonly;
717 else if (t == FloppyImage)
718 med.hdType = MediumType_Writethrough;
719 }
720
721 if (fNeedsLocation)
722 // current files and 1.4 CustomHardDisk elements must have a location attribute
723 if (!elmMedium.getAttributeValue("location", med.strLocation))
724 throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName());
725
726 elmMedium.getAttributeValue("Description", med.strDescription); // optional
727
728 // recurse to handle children
729 xml::NodesLoop nl2(elmMedium);
730 const xml::ElementNode *pelmHDChild;
731 while ((pelmHDChild = nl2.forAllNodes()))
732 {
733 if ( t == HardDisk
734 && ( pelmHDChild->nameEquals("HardDisk")
735 || ( (m->sv < SettingsVersion_v1_4)
736 && (pelmHDChild->nameEquals("DiffHardDisk"))
737 )
738 )
739 )
740 // recurse with this element and push the child onto our current children list
741 readMedium(t,
742 *pelmHDChild,
743 med.llChildren);
744 else if (pelmHDChild->nameEquals("Property"))
745 {
746 Utf8Str strPropName, strPropValue;
747 if ( pelmHDChild->getAttributeValue("name", strPropName)
748 && pelmHDChild->getAttributeValue("value", strPropValue) )
749 med.properties[strPropName] = strPropValue;
750 else
751 throw ConfigFileError(this, pelmHDChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
752 }
753 }
754
755 llMedia.push_back(med);
756}
757
758/**
759 * Reads in the entire <MediaRegistry> chunk and stores its media in the lists
760 * of the given MediaRegistry structure.
761 *
762 * This is used in both MainConfigFile and MachineConfigFile since starting with
763 * VirtualBox 4.0, we can have media registries in both.
764 *
765 * For pre-1.4 files, this gets called with the <DiskRegistry> chunk instead.
766 *
767 * @param elmMediaRegistry
768 */
769void ConfigFileBase::readMediaRegistry(const xml::ElementNode &elmMediaRegistry,
770 MediaRegistry &mr)
771{
772 xml::NodesLoop nl1(elmMediaRegistry);
773 const xml::ElementNode *pelmChild1;
774 while ((pelmChild1 = nl1.forAllNodes()))
775 {
776 MediaType t = Error;
777 if (pelmChild1->nameEquals("HardDisks"))
778 t = HardDisk;
779 else if (pelmChild1->nameEquals("DVDImages"))
780 t = DVDImage;
781 else if (pelmChild1->nameEquals("FloppyImages"))
782 t = FloppyImage;
783 else
784 continue;
785
786 xml::NodesLoop nl2(*pelmChild1);
787 const xml::ElementNode *pelmMedium;
788 while ((pelmMedium = nl2.forAllNodes()))
789 {
790 if ( t == HardDisk
791 && (pelmMedium->nameEquals("HardDisk"))
792 )
793 readMedium(t,
794 *pelmMedium,
795 mr.llHardDisks); // list to append hard disk data to: the root list
796 else if ( t == DVDImage
797 && (pelmMedium->nameEquals("Image"))
798 )
799 readMedium(t,
800 *pelmMedium,
801 mr.llDvdImages); // list to append dvd images to: the root list
802 else if ( t == FloppyImage
803 && (pelmMedium->nameEquals("Image"))
804 )
805 readMedium(t,
806 *pelmMedium,
807 mr.llFloppyImages); // list to append floppy images to: the root list
808 }
809 }
810}
811
812/**
813 * This is common version for reading NAT port forward rule in per-_machine's_adapter_ and
814 * per-network approaches.
815 * Note: this function doesn't in fill given list from xml::ElementNodesList, because there is conflicting
816 * declaration in ovmfreader.h.
817 */
818void ConfigFileBase::readNATForwardRuleList(const xml::ElementNode &elmParent, NATRuleList &llRules)
819{
820 xml::ElementNodesList plstRules;
821 elmParent.getChildElements(plstRules, "Forwarding");
822 for (xml::ElementNodesList::iterator pf = plstRules.begin(); pf != plstRules.end(); ++pf)
823 {
824 NATRule rule;
825 uint32_t port = 0;
826 (*pf)->getAttributeValue("name", rule.strName);
827 (*pf)->getAttributeValue("proto", (uint32_t&)rule.proto);
828 (*pf)->getAttributeValue("hostip", rule.strHostIP);
829 (*pf)->getAttributeValue("hostport", port);
830 rule.u16HostPort = port;
831 (*pf)->getAttributeValue("guestip", rule.strGuestIP);
832 (*pf)->getAttributeValue("guestport", port);
833 rule.u16GuestPort = port;
834 llRules.push_back(rule);
835 }
836}
837
838void ConfigFileBase::readNATLoopbacks(const xml::ElementNode &elmParent, NATLoopbackOffsetList &llLoopbacks)
839{
840 xml::ElementNodesList plstLoopbacks;
841 elmParent.getChildElements(plstLoopbacks, "Loopback4");
842 for (xml::ElementNodesList::iterator lo = plstLoopbacks.begin();
843 lo != plstLoopbacks.end(); ++lo)
844 {
845 NATHostLoopbackOffset loopback;
846 (*lo)->getAttributeValue("address", loopback.strLoopbackHostAddress);
847 (*lo)->getAttributeValue("offset", (uint32_t&)loopback.u32Offset);
848 llLoopbacks.push_back(loopback);
849 }
850}
851
852
853/**
854 * Adds a "version" attribute to the given XML element with the
855 * VirtualBox settings version (e.g. "1.10-linux"). Used by
856 * the XML format for the root element and by the OVF export
857 * for the vbox:Machine element.
858 * @param elm
859 */
860void ConfigFileBase::setVersionAttribute(xml::ElementNode &elm)
861{
862 const char *pcszVersion = NULL;
863 switch (m->sv)
864 {
865 case SettingsVersion_v1_8:
866 pcszVersion = "1.8";
867 break;
868
869 case SettingsVersion_v1_9:
870 pcszVersion = "1.9";
871 break;
872
873 case SettingsVersion_v1_10:
874 pcszVersion = "1.10";
875 break;
876
877 case SettingsVersion_v1_11:
878 pcszVersion = "1.11";
879 break;
880
881 case SettingsVersion_v1_12:
882 pcszVersion = "1.12";
883 break;
884
885 case SettingsVersion_v1_13:
886 pcszVersion = "1.13";
887 break;
888
889 case SettingsVersion_v1_14:
890 pcszVersion = "1.14";
891 break;
892
893 case SettingsVersion_v1_15:
894 pcszVersion = "1.15";
895 break;
896
897 default:
898 // catch human error: the assertion below will trigger in debug
899 // or dbgopt builds, so hopefully this will get noticed sooner in
900 // the future, because it's easy to forget top update something.
901 AssertMsg(m->sv <= SettingsVersion_v1_7, ("Settings.cpp: unexpected settings version %d, unhandled future version?\n", m->sv));
902 // silently upgrade if this is less than 1.7 because that's the oldest we can write
903 if (m->sv <= SettingsVersion_v1_7)
904 {
905 pcszVersion = "1.7";
906 m->sv = SettingsVersion_v1_7;
907 }
908 else
909 {
910 // This is reached for SettingsVersion_Future and forgotten
911 // settings version after SettingsVersion_v1_7, which should
912 // not happen (see assertion above). Set the version to the
913 // latest known version, to minimize loss of information, but
914 // as we can't predict the future we have to use some format
915 // we know, and latest should be the best choice. Note that
916 // for "forgotten settings" this may not be the best choice,
917 // but as it's an omission of someone who changed this file
918 // it's the only generic possibility.
919 pcszVersion = "1.15";
920 m->sv = SettingsVersion_v1_15;
921 }
922 break;
923 }
924
925 elm.setAttribute("version", Utf8StrFmt("%s-%s",
926 pcszVersion,
927 VBOX_XML_PLATFORM)); // e.g. "linux"
928}
929
930/**
931 * Creates a new stub xml::Document in the m->pDoc member with the
932 * root "VirtualBox" element set up. This is used by both
933 * MainConfigFile and MachineConfigFile at the beginning of writing
934 * out their XML.
935 *
936 * Before calling this, it is the responsibility of the caller to
937 * set the "sv" member to the required settings version that is to
938 * be written. For newly created files, the settings version will be
939 * the latest (1.12); for files read in from disk earlier, it will be
940 * the settings version indicated in the file. However, this method
941 * will silently make sure that the settings version is always
942 * at least 1.7 and change it if necessary, since there is no write
943 * support for earlier settings versions.
944 */
945void ConfigFileBase::createStubDocument()
946{
947 Assert(m->pDoc == NULL);
948 m->pDoc = new xml::Document;
949
950 m->pelmRoot = m->pDoc->createRootElement("VirtualBox",
951 "\n"
952 "** DO NOT EDIT THIS FILE.\n"
953 "** If you make changes to this file while any VirtualBox related application\n"
954 "** is running, your changes will be overwritten later, without taking effect.\n"
955 "** Use VBoxManage or the VirtualBox Manager GUI to make changes.\n"
956);
957 m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
958
959 // add settings version attribute to root element
960 setVersionAttribute(*m->pelmRoot);
961
962 // since this gets called before the XML document is actually written out,
963 // this is where we must check whether we're upgrading the settings version
964 // and need to make a backup, so the user can go back to an earlier
965 // VirtualBox version and recover his old settings files.
966 if ( (m->svRead != SettingsVersion_Null) // old file exists?
967 && (m->svRead < m->sv) // we're upgrading?
968 )
969 {
970 // compose new filename: strip off trailing ".xml"/".vbox"
971 Utf8Str strFilenameNew;
972 Utf8Str strExt = ".xml";
973 if (m->strFilename.endsWith(".xml"))
974 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 4);
975 else if (m->strFilename.endsWith(".vbox"))
976 {
977 strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 5);
978 strExt = ".vbox";
979 }
980
981 // and append something like "-1.3-linux.xml"
982 strFilenameNew.append("-");
983 strFilenameNew.append(m->strSettingsVersionFull); // e.g. "1.3-linux"
984 strFilenameNew.append(strExt); // .xml for main config, .vbox for machine config
985
986 RTFileMove(m->strFilename.c_str(),
987 strFilenameNew.c_str(),
988 0); // no RTFILEMOVE_FLAGS_REPLACE
989
990 // do this only once
991 m->svRead = SettingsVersion_Null;
992 }
993}
994
995/**
996 * Creates an <ExtraData> node under the given parent element with
997 * <ExtraDataItem> childern according to the contents of the given
998 * map.
999 *
1000 * This is in ConfigFileBase because it's used in both MainConfigFile
1001 * and MachineConfigFile, which both can have extradata.
1002 *
1003 * @param elmParent
1004 * @param me
1005 */
1006void ConfigFileBase::buildExtraData(xml::ElementNode &elmParent,
1007 const StringsMap &me)
1008{
1009 if (me.size())
1010 {
1011 xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
1012 for (StringsMap::const_iterator it = me.begin();
1013 it != me.end();
1014 ++it)
1015 {
1016 const Utf8Str &strName = it->first;
1017 const Utf8Str &strValue = it->second;
1018 xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
1019 pelmThis->setAttribute("name", strName);
1020 pelmThis->setAttribute("value", strValue);
1021 }
1022 }
1023}
1024
1025/**
1026 * Creates <DeviceFilter> nodes under the given parent element according to
1027 * the contents of the given USBDeviceFiltersList. This is in ConfigFileBase
1028 * because it's used in both MainConfigFile (for host filters) and
1029 * MachineConfigFile (for machine filters).
1030 *
1031 * If fHostMode is true, this means that we're supposed to write filters
1032 * for the IHost interface (respect "action", omit "strRemote" and
1033 * "ulMaskedInterfaces" in struct USBDeviceFilter).
1034 *
1035 * @param elmParent
1036 * @param ll
1037 * @param fHostMode
1038 */
1039void ConfigFileBase::buildUSBDeviceFilters(xml::ElementNode &elmParent,
1040 const USBDeviceFiltersList &ll,
1041 bool fHostMode)
1042{
1043 for (USBDeviceFiltersList::const_iterator it = ll.begin();
1044 it != ll.end();
1045 ++it)
1046 {
1047 const USBDeviceFilter &flt = *it;
1048 xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
1049 pelmFilter->setAttribute("name", flt.strName);
1050 pelmFilter->setAttribute("active", flt.fActive);
1051 if (flt.strVendorId.length())
1052 pelmFilter->setAttribute("vendorId", flt.strVendorId);
1053 if (flt.strProductId.length())
1054 pelmFilter->setAttribute("productId", flt.strProductId);
1055 if (flt.strRevision.length())
1056 pelmFilter->setAttribute("revision", flt.strRevision);
1057 if (flt.strManufacturer.length())
1058 pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
1059 if (flt.strProduct.length())
1060 pelmFilter->setAttribute("product", flt.strProduct);
1061 if (flt.strSerialNumber.length())
1062 pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
1063 if (flt.strPort.length())
1064 pelmFilter->setAttribute("port", flt.strPort);
1065
1066 if (fHostMode)
1067 {
1068 const char *pcsz =
1069 (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
1070 : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
1071 pelmFilter->setAttribute("action", pcsz);
1072 }
1073 else
1074 {
1075 if (flt.strRemote.length())
1076 pelmFilter->setAttribute("remote", flt.strRemote);
1077 if (flt.ulMaskedInterfaces)
1078 pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
1079 }
1080 }
1081}
1082
1083/**
1084 * Creates a single <HardDisk> element for the given Medium structure
1085 * and recurses to write the child hard disks underneath. Called from
1086 * MainConfigFile::write().
1087 *
1088 * @param elmMedium
1089 * @param m
1090 * @param level
1091 */
1092void ConfigFileBase::buildMedium(xml::ElementNode &elmMedium,
1093 DeviceType_T devType,
1094 const Medium &mdm,
1095 uint32_t level) // 0 for "root" call, incremented with each recursion
1096{
1097 xml::ElementNode *pelmMedium;
1098
1099 if (devType == DeviceType_HardDisk)
1100 pelmMedium = elmMedium.createChild("HardDisk");
1101 else
1102 pelmMedium = elmMedium.createChild("Image");
1103
1104 pelmMedium->setAttribute("uuid", mdm.uuid.toStringCurly());
1105
1106 pelmMedium->setAttributePath("location", mdm.strLocation);
1107
1108 if (devType == DeviceType_HardDisk || RTStrICmp(mdm.strFormat.c_str(), "RAW"))
1109 pelmMedium->setAttribute("format", mdm.strFormat);
1110 if ( devType == DeviceType_HardDisk
1111 && mdm.fAutoReset)
1112 pelmMedium->setAttribute("autoReset", mdm.fAutoReset);
1113 if (mdm.strDescription.length())
1114 pelmMedium->setAttribute("Description", mdm.strDescription);
1115
1116 for (StringsMap::const_iterator it = mdm.properties.begin();
1117 it != mdm.properties.end();
1118 ++it)
1119 {
1120 xml::ElementNode *pelmProp = pelmMedium->createChild("Property");
1121 pelmProp->setAttribute("name", it->first);
1122 pelmProp->setAttribute("value", it->second);
1123 }
1124
1125 // only for base hard disks, save the type
1126 if (level == 0)
1127 {
1128 // no need to save the usual DVD/floppy medium types
1129 if ( ( devType != DeviceType_DVD
1130 || ( mdm.hdType != MediumType_Writethrough // shouldn't happen
1131 && mdm.hdType != MediumType_Readonly))
1132 && ( devType != DeviceType_Floppy
1133 || mdm.hdType != MediumType_Writethrough))
1134 {
1135 const char *pcszType =
1136 mdm.hdType == MediumType_Normal ? "Normal" :
1137 mdm.hdType == MediumType_Immutable ? "Immutable" :
1138 mdm.hdType == MediumType_Writethrough ? "Writethrough" :
1139 mdm.hdType == MediumType_Shareable ? "Shareable" :
1140 mdm.hdType == MediumType_Readonly ? "Readonly" :
1141 mdm.hdType == MediumType_MultiAttach ? "MultiAttach" :
1142 "INVALID";
1143 pelmMedium->setAttribute("type", pcszType);
1144 }
1145 }
1146
1147 for (MediaList::const_iterator it = mdm.llChildren.begin();
1148 it != mdm.llChildren.end();
1149 ++it)
1150 {
1151 // recurse for children
1152 buildMedium(*pelmMedium, // parent
1153 devType, // device type
1154 *it, // settings::Medium
1155 ++level); // recursion level
1156 }
1157}
1158
1159/**
1160 * Creates a <MediaRegistry> node under the given parent and writes out all
1161 * hard disks and DVD and floppy images from the lists in the given MediaRegistry
1162 * structure under it.
1163 *
1164 * This is used in both MainConfigFile and MachineConfigFile since starting with
1165 * VirtualBox 4.0, we can have media registries in both.
1166 *
1167 * @param elmParent
1168 * @param mr
1169 */
1170void ConfigFileBase::buildMediaRegistry(xml::ElementNode &elmParent,
1171 const MediaRegistry &mr)
1172{
1173 xml::ElementNode *pelmMediaRegistry = elmParent.createChild("MediaRegistry");
1174
1175 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
1176 for (MediaList::const_iterator it = mr.llHardDisks.begin();
1177 it != mr.llHardDisks.end();
1178 ++it)
1179 {
1180 buildMedium(*pelmHardDisks, DeviceType_HardDisk, *it, 0);
1181 }
1182
1183 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
1184 for (MediaList::const_iterator it = mr.llDvdImages.begin();
1185 it != mr.llDvdImages.end();
1186 ++it)
1187 {
1188 buildMedium(*pelmDVDImages, DeviceType_DVD, *it, 0);
1189 }
1190
1191 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
1192 for (MediaList::const_iterator it = mr.llFloppyImages.begin();
1193 it != mr.llFloppyImages.end();
1194 ++it)
1195 {
1196 buildMedium(*pelmFloppyImages, DeviceType_Floppy, *it, 0);
1197 }
1198}
1199
1200/**
1201 * Serialize NAT port-forwarding rules in parent container.
1202 * Note: it's responsibility of caller to create parent of the list tag.
1203 * because this method used for serializing per-_mahine's_adapter_ and per-network approaches.
1204 */
1205void ConfigFileBase::buildNATForwardRuleList(xml::ElementNode &elmParent, const NATRuleList &natRuleList)
1206{
1207 for (NATRuleList::const_iterator r = natRuleList.begin();
1208 r != natRuleList.end(); ++r)
1209 {
1210 xml::ElementNode *pelmPF;
1211 pelmPF = elmParent.createChild("Forwarding");
1212 if ((*r).strName.length())
1213 pelmPF->setAttribute("name", (*r).strName);
1214 pelmPF->setAttribute("proto", (*r).proto);
1215 if ((*r).strHostIP.length())
1216 pelmPF->setAttribute("hostip", (*r).strHostIP);
1217 if ((*r).u16HostPort)
1218 pelmPF->setAttribute("hostport", (*r).u16HostPort);
1219 if ((*r).strGuestIP.length())
1220 pelmPF->setAttribute("guestip", (*r).strGuestIP);
1221 if ((*r).u16GuestPort)
1222 pelmPF->setAttribute("guestport", (*r).u16GuestPort);
1223 }
1224}
1225
1226
1227void ConfigFileBase::buildNATLoopbacks(xml::ElementNode &elmParent, const NATLoopbackOffsetList &natLoopbackOffsetList)
1228{
1229 for (NATLoopbackOffsetList::const_iterator lo = natLoopbackOffsetList.begin();
1230 lo != natLoopbackOffsetList.end(); ++lo)
1231 {
1232 xml::ElementNode *pelmLo;
1233 pelmLo = elmParent.createChild("Loopback4");
1234 pelmLo->setAttribute("address", (*lo).strLoopbackHostAddress);
1235 pelmLo->setAttribute("offset", (*lo).u32Offset);
1236 }
1237}
1238
1239/**
1240 * Cleans up memory allocated by the internal XML parser. To be called by
1241 * descendant classes when they're done analyzing the DOM tree to discard it.
1242 */
1243void ConfigFileBase::clearDocument()
1244{
1245 m->cleanup();
1246}
1247
1248/**
1249 * Returns true only if the underlying config file exists on disk;
1250 * either because the file has been loaded from disk, or it's been written
1251 * to disk, or both.
1252 * @return
1253 */
1254bool ConfigFileBase::fileExists()
1255{
1256 return m->fFileExists;
1257}
1258
1259/**
1260 * Copies the base variables from another instance. Used by Machine::saveSettings
1261 * so that the settings version does not get lost when a copy of the Machine settings
1262 * file is made to see if settings have actually changed.
1263 * @param b
1264 */
1265void ConfigFileBase::copyBaseFrom(const ConfigFileBase &b)
1266{
1267 m->copyFrom(*b.m);
1268}
1269
1270////////////////////////////////////////////////////////////////////////////////
1271//
1272// Structures shared between Machine XML and VirtualBox.xml
1273//
1274////////////////////////////////////////////////////////////////////////////////
1275
1276/**
1277 * Comparison operator. This gets called from MachineConfigFile::operator==,
1278 * which in turn gets called from Machine::saveSettings to figure out whether
1279 * machine settings have really changed and thus need to be written out to disk.
1280 */
1281bool USBDeviceFilter::operator==(const USBDeviceFilter &u) const
1282{
1283 return ( (this == &u)
1284 || ( (strName == u.strName)
1285 && (fActive == u.fActive)
1286 && (strVendorId == u.strVendorId)
1287 && (strProductId == u.strProductId)
1288 && (strRevision == u.strRevision)
1289 && (strManufacturer == u.strManufacturer)
1290 && (strProduct == u.strProduct)
1291 && (strSerialNumber == u.strSerialNumber)
1292 && (strPort == u.strPort)
1293 && (action == u.action)
1294 && (strRemote == u.strRemote)
1295 && (ulMaskedInterfaces == u.ulMaskedInterfaces)
1296 )
1297 );
1298}
1299
1300////////////////////////////////////////////////////////////////////////////////
1301//
1302// MainConfigFile
1303//
1304////////////////////////////////////////////////////////////////////////////////
1305
1306/**
1307 * Reads one <MachineEntry> from the main VirtualBox.xml file.
1308 * @param elmMachineRegistry
1309 */
1310void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
1311{
1312 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
1313 xml::NodesLoop nl1(elmMachineRegistry);
1314 const xml::ElementNode *pelmChild1;
1315 while ((pelmChild1 = nl1.forAllNodes()))
1316 {
1317 if (pelmChild1->nameEquals("MachineEntry"))
1318 {
1319 MachineRegistryEntry mre;
1320 Utf8Str strUUID;
1321 if ( pelmChild1->getAttributeValue("uuid", strUUID)
1322 && pelmChild1->getAttributeValue("src", mre.strSettingsFile) )
1323 {
1324 parseUUID(mre.uuid, strUUID);
1325 llMachines.push_back(mre);
1326 }
1327 else
1328 throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing"));
1329 }
1330 }
1331}
1332
1333/**
1334 * Reads in the <DHCPServers> chunk.
1335 * @param elmDHCPServers
1336 */
1337void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
1338{
1339 xml::NodesLoop nl1(elmDHCPServers);
1340 const xml::ElementNode *pelmServer;
1341 while ((pelmServer = nl1.forAllNodes()))
1342 {
1343 if (pelmServer->nameEquals("DHCPServer"))
1344 {
1345 DHCPServer srv;
1346 if ( pelmServer->getAttributeValue("networkName", srv.strNetworkName)
1347 && pelmServer->getAttributeValue("IPAddress", srv.strIPAddress)
1348 && pelmServer->getAttributeValue("networkMask", srv.GlobalDhcpOptions[DhcpOpt_SubnetMask].text)
1349 && pelmServer->getAttributeValue("lowerIP", srv.strIPLower)
1350 && pelmServer->getAttributeValue("upperIP", srv.strIPUpper)
1351 && pelmServer->getAttributeValue("enabled", srv.fEnabled) )
1352 {
1353 xml::NodesLoop nlOptions(*pelmServer, "Options");
1354 const xml::ElementNode *options;
1355 /* XXX: Options are in 1:1 relation to DHCPServer */
1356
1357 while ((options = nlOptions.forAllNodes()))
1358 {
1359 readDhcpOptions(srv.GlobalDhcpOptions, *options);
1360 } /* end of forall("Options") */
1361 xml::NodesLoop nlConfig(*pelmServer, "Config");
1362 const xml::ElementNode *cfg;
1363 while ((cfg = nlConfig.forAllNodes()))
1364 {
1365 com::Utf8Str strVmName;
1366 uint32_t u32Slot;
1367 cfg->getAttributeValue("vm-name", strVmName);
1368 cfg->getAttributeValue("slot", u32Slot);
1369 readDhcpOptions(srv.VmSlot2OptionsM[VmNameSlotKey(strVmName, u32Slot)], *cfg);
1370 }
1371 llDhcpServers.push_back(srv);
1372 }
1373 else
1374 throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing"));
1375 }
1376 }
1377}
1378
1379void MainConfigFile::readDhcpOptions(DhcpOptionMap& map,
1380 const xml::ElementNode& options)
1381{
1382 xml::NodesLoop nl2(options, "Option");
1383 const xml::ElementNode *opt;
1384 while ((opt = nl2.forAllNodes()))
1385 {
1386 DhcpOpt_T OptName;
1387 com::Utf8Str OptText;
1388 int32_t OptEnc = DhcpOptValue::LEGACY;
1389
1390 opt->getAttributeValue("name", (uint32_t&)OptName);
1391
1392 if (OptName == DhcpOpt_SubnetMask)
1393 continue;
1394
1395 opt->getAttributeValue("value", OptText);
1396 opt->getAttributeValue("encoding", OptEnc);
1397
1398 map[OptName] = DhcpOptValue(OptText, (DhcpOptValue::Encoding)OptEnc);
1399 } /* end of forall("Option") */
1400
1401}
1402
1403/**
1404 * Reads in the <NATNetworks> chunk.
1405 * @param elmNATNetworks
1406 */
1407void MainConfigFile::readNATNetworks(const xml::ElementNode &elmNATNetworks)
1408{
1409 xml::NodesLoop nl1(elmNATNetworks);
1410 const xml::ElementNode *pelmNet;
1411 while ((pelmNet = nl1.forAllNodes()))
1412 {
1413 if (pelmNet->nameEquals("NATNetwork"))
1414 {
1415 NATNetwork net;
1416 if ( pelmNet->getAttributeValue("networkName", net.strNetworkName)
1417 && pelmNet->getAttributeValue("enabled", net.fEnabled)
1418 && pelmNet->getAttributeValue("network", net.strNetwork)
1419 && pelmNet->getAttributeValue("ipv6", net.fIPv6)
1420 && pelmNet->getAttributeValue("ipv6prefix", net.strIPv6Prefix)
1421 && pelmNet->getAttributeValue("advertiseDefaultIPv6Route", net.fAdvertiseDefaultIPv6Route)
1422 && pelmNet->getAttributeValue("needDhcp", net.fNeedDhcpServer) )
1423 {
1424 pelmNet->getAttributeValue("loopback6", net.u32HostLoopback6Offset);
1425 const xml::ElementNode *pelmMappings;
1426 if ((pelmMappings = pelmNet->findChildElement("Mappings")))
1427 readNATLoopbacks(*pelmMappings, net.llHostLoopbackOffsetList);
1428
1429 const xml::ElementNode *pelmPortForwardRules4;
1430 if ((pelmPortForwardRules4 = pelmNet->findChildElement("PortForwarding4")))
1431 readNATForwardRuleList(*pelmPortForwardRules4,
1432 net.llPortForwardRules4);
1433
1434 const xml::ElementNode *pelmPortForwardRules6;
1435 if ((pelmPortForwardRules6 = pelmNet->findChildElement("PortForwarding6")))
1436 readNATForwardRuleList(*pelmPortForwardRules6,
1437 net.llPortForwardRules6);
1438
1439 llNATNetworks.push_back(net);
1440 }
1441 else
1442 throw ConfigFileError(this, pelmNet, N_("Required NATNetwork/@networkName, @gateway, @network,@advertiseDefaultIpv6Route , @needDhcp or @enabled attribute is missing"));
1443 }
1444 }
1445}
1446
1447/**
1448 * Constructor.
1449 *
1450 * If pstrFilename is != NULL, this reads the given settings file into the member
1451 * variables and various substructures and lists. Otherwise, the member variables
1452 * are initialized with default values.
1453 *
1454 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1455 * the caller should catch; if this constructor does not throw, then the member
1456 * variables contain meaningful values (either from the file or defaults).
1457 *
1458 * @param strFilename
1459 */
1460MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
1461 : ConfigFileBase(pstrFilename)
1462{
1463 if (pstrFilename)
1464 {
1465 // the ConfigFileBase constructor has loaded the XML file, so now
1466 // we need only analyze what is in there
1467 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1468 const xml::ElementNode *pelmRootChild;
1469 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1470 {
1471 if (pelmRootChild->nameEquals("Global"))
1472 {
1473 xml::NodesLoop nlGlobalChildren(*pelmRootChild);
1474 const xml::ElementNode *pelmGlobalChild;
1475 while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
1476 {
1477 if (pelmGlobalChild->nameEquals("SystemProperties"))
1478 {
1479 pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1480 pelmGlobalChild->getAttributeValue("LoggingLevel", systemProperties.strLoggingLevel);
1481 pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1482 if (!pelmGlobalChild->getAttributeValue("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary))
1483 // pre-1.11 used @remoteDisplayAuthLibrary instead
1484 pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strVRDEAuthLibrary);
1485 pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1486 pelmGlobalChild->getAttributeValue("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
1487 pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.ulLogHistoryCount);
1488 pelmGlobalChild->getAttributeValue("autostartDatabasePath", systemProperties.strAutostartDatabasePath);
1489 pelmGlobalChild->getAttributeValue("defaultFrontend", systemProperties.strDefaultFrontend);
1490 pelmGlobalChild->getAttributeValue("exclusiveHwVirt", systemProperties.fExclusiveHwVirt);
1491 }
1492 else if (pelmGlobalChild->nameEquals("ExtraData"))
1493 readExtraData(*pelmGlobalChild, mapExtraDataItems);
1494 else if (pelmGlobalChild->nameEquals("MachineRegistry"))
1495 readMachineRegistry(*pelmGlobalChild);
1496 else if ( (pelmGlobalChild->nameEquals("MediaRegistry"))
1497 || ( (m->sv < SettingsVersion_v1_4)
1498 && (pelmGlobalChild->nameEquals("DiskRegistry"))
1499 )
1500 )
1501 readMediaRegistry(*pelmGlobalChild, mediaRegistry);
1502 else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
1503 {
1504 xml::NodesLoop nlLevel4(*pelmGlobalChild);
1505 const xml::ElementNode *pelmLevel4Child;
1506 while ((pelmLevel4Child = nlLevel4.forAllNodes()))
1507 {
1508 if (pelmLevel4Child->nameEquals("DHCPServers"))
1509 readDHCPServers(*pelmLevel4Child);
1510 if (pelmLevel4Child->nameEquals("NATNetworks"))
1511 readNATNetworks(*pelmLevel4Child);
1512 }
1513 }
1514 else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
1515 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
1516 }
1517 } // end if (pelmRootChild->nameEquals("Global"))
1518 }
1519
1520 clearDocument();
1521 }
1522
1523 // DHCP servers were introduced with settings version 1.7; if we're loading
1524 // from an older version OR this is a fresh install, then add one DHCP server
1525 // with default settings
1526 if ( (!llDhcpServers.size())
1527 && ( (!pstrFilename) // empty VirtualBox.xml file
1528 || (m->sv < SettingsVersion_v1_7) // upgrading from before 1.7
1529 )
1530 )
1531 {
1532 DHCPServer srv;
1533 srv.strNetworkName =
1534#ifdef RT_OS_WINDOWS
1535 "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
1536#else
1537 "HostInterfaceNetworking-vboxnet0";
1538#endif
1539 srv.strIPAddress = "192.168.56.100";
1540 srv.GlobalDhcpOptions[DhcpOpt_SubnetMask] = DhcpOptValue("255.255.255.0");
1541 srv.strIPLower = "192.168.56.101";
1542 srv.strIPUpper = "192.168.56.254";
1543 srv.fEnabled = true;
1544 llDhcpServers.push_back(srv);
1545 }
1546}
1547
1548void MainConfigFile::bumpSettingsVersionIfNeeded()
1549{
1550 if (m->sv < SettingsVersion_v1_14)
1551 {
1552 // VirtualBox 4.3 adds NAT networks.
1553 if ( !llNATNetworks.empty())
1554 m->sv = SettingsVersion_v1_14;
1555 }
1556}
1557
1558
1559/**
1560 * Called from the IVirtualBox interface to write out VirtualBox.xml. This
1561 * builds an XML DOM tree and writes it out to disk.
1562 */
1563void MainConfigFile::write(const com::Utf8Str strFilename)
1564{
1565 bumpSettingsVersionIfNeeded();
1566
1567 m->strFilename = strFilename;
1568 createStubDocument();
1569
1570 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
1571
1572 buildExtraData(*pelmGlobal, mapExtraDataItems);
1573
1574 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
1575 for (MachinesRegistry::const_iterator it = llMachines.begin();
1576 it != llMachines.end();
1577 ++it)
1578 {
1579 // <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"/>
1580 const MachineRegistryEntry &mre = *it;
1581 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
1582 pelmMachineEntry->setAttribute("uuid", mre.uuid.toStringCurly());
1583 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
1584 }
1585
1586 buildMediaRegistry(*pelmGlobal, mediaRegistry);
1587
1588 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
1589 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
1590 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
1591 it != llDhcpServers.end();
1592 ++it)
1593 {
1594 const DHCPServer &d = *it;
1595 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
1596 DhcpOptConstIterator itOpt;
1597 itOpt = d.GlobalDhcpOptions.find(DhcpOpt_SubnetMask);
1598
1599 pelmThis->setAttribute("networkName", d.strNetworkName);
1600 pelmThis->setAttribute("IPAddress", d.strIPAddress);
1601 if (itOpt != d.GlobalDhcpOptions.end())
1602 pelmThis->setAttribute("networkMask", itOpt->second.text);
1603 pelmThis->setAttribute("lowerIP", d.strIPLower);
1604 pelmThis->setAttribute("upperIP", d.strIPUpper);
1605 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
1606 /* We assume that if there're only 1 element it means that */
1607 size_t cOpt = d.GlobalDhcpOptions.size();
1608 /* We don't want duplicate validation check of networkMask here*/
1609 if ( ( itOpt == d.GlobalDhcpOptions.end()
1610 && cOpt > 0)
1611 || cOpt > 1)
1612 {
1613 xml::ElementNode *pelmOptions = pelmThis->createChild("Options");
1614 for (itOpt = d.GlobalDhcpOptions.begin();
1615 itOpt != d.GlobalDhcpOptions.end();
1616 ++itOpt)
1617 {
1618 if (itOpt->first == DhcpOpt_SubnetMask)
1619 continue;
1620
1621 xml::ElementNode *pelmOpt = pelmOptions->createChild("Option");
1622
1623 if (!pelmOpt)
1624 break;
1625
1626 pelmOpt->setAttribute("name", itOpt->first);
1627 pelmOpt->setAttribute("value", itOpt->second.text);
1628 if (itOpt->second.encoding != DhcpOptValue::LEGACY)
1629 pelmOpt->setAttribute("encoding", (int)itOpt->second.encoding);
1630 }
1631 } /* end of if */
1632
1633 if (d.VmSlot2OptionsM.size() > 0)
1634 {
1635 VmSlot2OptionsConstIterator itVmSlot;
1636 DhcpOptConstIterator itOpt1;
1637 for(itVmSlot = d.VmSlot2OptionsM.begin();
1638 itVmSlot != d.VmSlot2OptionsM.end();
1639 ++itVmSlot)
1640 {
1641 xml::ElementNode *pelmCfg = pelmThis->createChild("Config");
1642 pelmCfg->setAttribute("vm-name", itVmSlot->first.VmName);
1643 pelmCfg->setAttribute("slot", itVmSlot->first.Slot);
1644
1645 for (itOpt1 = itVmSlot->second.begin();
1646 itOpt1 != itVmSlot->second.end();
1647 ++itOpt1)
1648 {
1649 xml::ElementNode *pelmOpt = pelmCfg->createChild("Option");
1650 pelmOpt->setAttribute("name", itOpt1->first);
1651 pelmOpt->setAttribute("value", itOpt1->second.text);
1652 if (itOpt1->second.encoding != DhcpOptValue::LEGACY)
1653 pelmOpt->setAttribute("encoding", (int)itOpt1->second.encoding);
1654 }
1655 }
1656 } /* and of if */
1657
1658 }
1659
1660 xml::ElementNode *pelmNATNetworks;
1661 /* don't create entry if no NAT networks are registered. */
1662 if (!llNATNetworks.empty())
1663 {
1664 pelmNATNetworks = pelmNetserviceRegistry->createChild("NATNetworks");
1665 for (NATNetworksList::const_iterator it = llNATNetworks.begin();
1666 it != llNATNetworks.end();
1667 ++it)
1668 {
1669 const NATNetwork &n = *it;
1670 xml::ElementNode *pelmThis = pelmNATNetworks->createChild("NATNetwork");
1671 pelmThis->setAttribute("networkName", n.strNetworkName);
1672 pelmThis->setAttribute("network", n.strNetwork);
1673 pelmThis->setAttribute("ipv6", n.fIPv6 ? 1 : 0);
1674 pelmThis->setAttribute("ipv6prefix", n.strIPv6Prefix);
1675 pelmThis->setAttribute("advertiseDefaultIPv6Route", (n.fAdvertiseDefaultIPv6Route)? 1 : 0);
1676 pelmThis->setAttribute("needDhcp", (n.fNeedDhcpServer) ? 1 : 0);
1677 pelmThis->setAttribute("enabled", (n.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
1678 if (n.llPortForwardRules4.size())
1679 {
1680 xml::ElementNode *pelmPf4 = pelmThis->createChild("PortForwarding4");
1681 buildNATForwardRuleList(*pelmPf4, n.llPortForwardRules4);
1682 }
1683 if (n.llPortForwardRules6.size())
1684 {
1685 xml::ElementNode *pelmPf6 = pelmThis->createChild("PortForwarding6");
1686 buildNATForwardRuleList(*pelmPf6, n.llPortForwardRules6);
1687 }
1688
1689 if (n.llHostLoopbackOffsetList.size())
1690 {
1691 xml::ElementNode *pelmMappings = pelmThis->createChild("Mappings");
1692 buildNATLoopbacks(*pelmMappings, n.llHostLoopbackOffsetList);
1693
1694 }
1695 }
1696 }
1697
1698
1699 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
1700 if (systemProperties.strDefaultMachineFolder.length())
1701 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1702 if (systemProperties.strLoggingLevel.length())
1703 pelmSysProps->setAttribute("LoggingLevel", systemProperties.strLoggingLevel);
1704 if (systemProperties.strDefaultHardDiskFormat.length())
1705 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1706 if (systemProperties.strVRDEAuthLibrary.length())
1707 pelmSysProps->setAttribute("VRDEAuthLibrary", systemProperties.strVRDEAuthLibrary);
1708 if (systemProperties.strWebServiceAuthLibrary.length())
1709 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1710 if (systemProperties.strDefaultVRDEExtPack.length())
1711 pelmSysProps->setAttribute("defaultVRDEExtPack", systemProperties.strDefaultVRDEExtPack);
1712 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.ulLogHistoryCount);
1713 if (systemProperties.strAutostartDatabasePath.length())
1714 pelmSysProps->setAttribute("autostartDatabasePath", systemProperties.strAutostartDatabasePath);
1715 if (systemProperties.strDefaultFrontend.length())
1716 pelmSysProps->setAttribute("defaultFrontend", systemProperties.strDefaultFrontend);
1717 pelmSysProps->setAttribute("exclusiveHwVirt", systemProperties.fExclusiveHwVirt);
1718
1719 buildUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
1720 host.llUSBDeviceFilters,
1721 true); // fHostMode
1722
1723 // now go write the XML
1724 xml::XmlFileWriter writer(*m->pDoc);
1725 writer.write(m->strFilename.c_str(), true /*fSafe*/);
1726
1727 m->fFileExists = true;
1728
1729 clearDocument();
1730}
1731
1732////////////////////////////////////////////////////////////////////////////////
1733//
1734// Machine XML structures
1735//
1736////////////////////////////////////////////////////////////////////////////////
1737
1738/**
1739 * Comparison operator. This gets called from MachineConfigFile::operator==,
1740 * which in turn gets called from Machine::saveSettings to figure out whether
1741 * machine settings have really changed and thus need to be written out to disk.
1742 */
1743bool VRDESettings::operator==(const VRDESettings& v) const
1744{
1745 return ( (this == &v)
1746 || ( (fEnabled == v.fEnabled)
1747 && (authType == v.authType)
1748 && (ulAuthTimeout == v.ulAuthTimeout)
1749 && (strAuthLibrary == v.strAuthLibrary)
1750 && (fAllowMultiConnection == v.fAllowMultiConnection)
1751 && (fReuseSingleConnection == v.fReuseSingleConnection)
1752 && (strVrdeExtPack == v.strVrdeExtPack)
1753 && (mapProperties == v.mapProperties)
1754 )
1755 );
1756}
1757
1758/**
1759 * Comparison operator. This gets called from MachineConfigFile::operator==,
1760 * which in turn gets called from Machine::saveSettings to figure out whether
1761 * machine settings have really changed and thus need to be written out to disk.
1762 */
1763bool BIOSSettings::operator==(const BIOSSettings &d) const
1764{
1765 return ( (this == &d)
1766 || ( fACPIEnabled == d.fACPIEnabled
1767 && fIOAPICEnabled == d.fIOAPICEnabled
1768 && fLogoFadeIn == d.fLogoFadeIn
1769 && fLogoFadeOut == d.fLogoFadeOut
1770 && ulLogoDisplayTime == d.ulLogoDisplayTime
1771 && strLogoImagePath == d.strLogoImagePath
1772 && biosBootMenuMode == d.biosBootMenuMode
1773 && fPXEDebugEnabled == d.fPXEDebugEnabled
1774 && llTimeOffset == d.llTimeOffset)
1775 );
1776}
1777
1778/**
1779 * Comparison operator. This gets called from MachineConfigFile::operator==,
1780 * which in turn gets called from Machine::saveSettings to figure out whether
1781 * machine settings have really changed and thus need to be written out to disk.
1782 */
1783bool USBController::operator==(const USBController &u) const
1784{
1785 return ( (this == &u)
1786 || ( (strName == u.strName)
1787 && (enmType == u.enmType)
1788 )
1789 );
1790}
1791
1792/**
1793 * Comparison operator. This gets called from MachineConfigFile::operator==,
1794 * which in turn gets called from Machine::saveSettings to figure out whether
1795 * machine settings have really changed and thus need to be written out to disk.
1796 */
1797bool USB::operator==(const USB &u) const
1798{
1799 return ( (this == &u)
1800 || ( (llUSBControllers == u.llUSBControllers)
1801 && (llDeviceFilters == u.llDeviceFilters)
1802 )
1803 );
1804}
1805
1806/**
1807 * Comparison operator. This gets called from MachineConfigFile::operator==,
1808 * which in turn gets called from Machine::saveSettings to figure out whether
1809 * machine settings have really changed and thus need to be written out to disk.
1810 */
1811bool NetworkAdapter::operator==(const NetworkAdapter &n) const
1812{
1813 return ( (this == &n)
1814 || ( (ulSlot == n.ulSlot)
1815 && (type == n.type)
1816 && (fEnabled == n.fEnabled)
1817 && (strMACAddress == n.strMACAddress)
1818 && (fCableConnected == n.fCableConnected)
1819 && (ulLineSpeed == n.ulLineSpeed)
1820 && (enmPromiscModePolicy == n.enmPromiscModePolicy)
1821 && (fTraceEnabled == n.fTraceEnabled)
1822 && (strTraceFile == n.strTraceFile)
1823 && (mode == n.mode)
1824 && (nat == n.nat)
1825 && (strBridgedName == n.strBridgedName)
1826 && (strHostOnlyName == n.strHostOnlyName)
1827 && (strInternalNetworkName == n.strInternalNetworkName)
1828 && (strGenericDriver == n.strGenericDriver)
1829 && (genericProperties == n.genericProperties)
1830 && (ulBootPriority == n.ulBootPriority)
1831 && (strBandwidthGroup == n.strBandwidthGroup)
1832 )
1833 );
1834}
1835
1836/**
1837 * Comparison operator. This gets called from MachineConfigFile::operator==,
1838 * which in turn gets called from Machine::saveSettings to figure out whether
1839 * machine settings have really changed and thus need to be written out to disk.
1840 */
1841bool SerialPort::operator==(const SerialPort &s) const
1842{
1843 return ( (this == &s)
1844 || ( (ulSlot == s.ulSlot)
1845 && (fEnabled == s.fEnabled)
1846 && (ulIOBase == s.ulIOBase)
1847 && (ulIRQ == s.ulIRQ)
1848 && (portMode == s.portMode)
1849 && (strPath == s.strPath)
1850 && (fServer == s.fServer)
1851 )
1852 );
1853}
1854
1855/**
1856 * Comparison operator. This gets called from MachineConfigFile::operator==,
1857 * which in turn gets called from Machine::saveSettings to figure out whether
1858 * machine settings have really changed and thus need to be written out to disk.
1859 */
1860bool ParallelPort::operator==(const ParallelPort &s) const
1861{
1862 return ( (this == &s)
1863 || ( (ulSlot == s.ulSlot)
1864 && (fEnabled == s.fEnabled)
1865 && (ulIOBase == s.ulIOBase)
1866 && (ulIRQ == s.ulIRQ)
1867 && (strPath == s.strPath)
1868 )
1869 );
1870}
1871
1872/**
1873 * Comparison operator. This gets called from MachineConfigFile::operator==,
1874 * which in turn gets called from Machine::saveSettings to figure out whether
1875 * machine settings have really changed and thus need to be written out to disk.
1876 */
1877bool SharedFolder::operator==(const SharedFolder &g) const
1878{
1879 return ( (this == &g)
1880 || ( (strName == g.strName)
1881 && (strHostPath == g.strHostPath)
1882 && (fWritable == g.fWritable)
1883 && (fAutoMount == g.fAutoMount)
1884 )
1885 );
1886}
1887
1888/**
1889 * Comparison operator. This gets called from MachineConfigFile::operator==,
1890 * which in turn gets called from Machine::saveSettings to figure out whether
1891 * machine settings have really changed and thus need to be written out to disk.
1892 */
1893bool GuestProperty::operator==(const GuestProperty &g) const
1894{
1895 return ( (this == &g)
1896 || ( (strName == g.strName)
1897 && (strValue == g.strValue)
1898 && (timestamp == g.timestamp)
1899 && (strFlags == g.strFlags)
1900 )
1901 );
1902}
1903
1904Hardware::Hardware()
1905 : strVersion("1"),
1906 fHardwareVirt(true),
1907 fNestedPaging(true),
1908 fVPID(true),
1909 fUnrestrictedExecution(true),
1910 fHardwareVirtForce(false),
1911 fSyntheticCpu(false),
1912 fTripleFaultReset(false),
1913 fPAE(false),
1914 enmLongMode(HC_ARCH_BITS == 64 ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled),
1915 cCPUs(1),
1916 fCpuHotPlug(false),
1917 fHPETEnabled(false),
1918 ulCpuExecutionCap(100),
1919 ulMemorySizeMB((uint32_t)-1),
1920 graphicsControllerType(GraphicsControllerType_VBoxVGA),
1921 ulVRAMSizeMB(8),
1922 cMonitors(1),
1923 fAccelerate3D(false),
1924 fAccelerate2DVideo(false),
1925 ulVideoCaptureHorzRes(1024),
1926 ulVideoCaptureVertRes(768),
1927 ulVideoCaptureRate(512),
1928 ulVideoCaptureFPS(25),
1929 ulVideoCaptureMaxTime(0),
1930 ulVideoCaptureMaxSize(0),
1931 fVideoCaptureEnabled(false),
1932 u64VideoCaptureScreens(UINT64_C(0xffffffffffffffff)),
1933 strVideoCaptureFile(""),
1934 firmwareType(FirmwareType_BIOS),
1935 pointingHIDType(PointingHIDType_PS2Mouse),
1936 keyboardHIDType(KeyboardHIDType_PS2Keyboard),
1937 chipsetType(ChipsetType_PIIX3),
1938 paravirtProvider(ParavirtProvider_Legacy),
1939 fEmulatedUSBCardReader(false),
1940 clipboardMode(ClipboardMode_Disabled),
1941 dndMode(DnDMode_Disabled),
1942 ulMemoryBalloonSize(0),
1943 fPageFusionEnabled(false)
1944{
1945 mapBootOrder[0] = DeviceType_Floppy;
1946 mapBootOrder[1] = DeviceType_DVD;
1947 mapBootOrder[2] = DeviceType_HardDisk;
1948
1949 /* The default value for PAE depends on the host:
1950 * - 64 bits host -> always true
1951 * - 32 bits host -> true for Windows & Darwin (masked off if the host cpu doesn't support it anyway)
1952 */
1953#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
1954 fPAE = true;
1955#endif
1956
1957 /* The default value of large page supports depends on the host:
1958 * - 64 bits host -> true, unless it's Linux (pending further prediction work due to excessively expensive large page allocations)
1959 * - 32 bits host -> false
1960 */
1961#if HC_ARCH_BITS == 64 && !defined(RT_OS_LINUX)
1962 fLargePages = true;
1963#else
1964 /* Not supported on 32 bits hosts. */
1965 fLargePages = false;
1966#endif
1967}
1968
1969/**
1970 * Comparison operator. This gets called from MachineConfigFile::operator==,
1971 * which in turn gets called from Machine::saveSettings to figure out whether
1972 * machine settings have really changed and thus need to be written out to disk.
1973 */
1974bool Hardware::operator==(const Hardware& h) const
1975{
1976 return ( (this == &h)
1977 || ( (strVersion == h.strVersion)
1978 && (uuid == h.uuid)
1979 && (fHardwareVirt == h.fHardwareVirt)
1980 && (fNestedPaging == h.fNestedPaging)
1981 && (fLargePages == h.fLargePages)
1982 && (fVPID == h.fVPID)
1983 && (fUnrestrictedExecution == h.fUnrestrictedExecution)
1984 && (fHardwareVirtForce == h.fHardwareVirtForce)
1985 && (fSyntheticCpu == h.fSyntheticCpu)
1986 && (fPAE == h.fPAE)
1987 && (enmLongMode == h.enmLongMode)
1988 && (fTripleFaultReset == h.fTripleFaultReset)
1989 && (cCPUs == h.cCPUs)
1990 && (fCpuHotPlug == h.fCpuHotPlug)
1991 && (ulCpuExecutionCap == h.ulCpuExecutionCap)
1992 && (fHPETEnabled == h.fHPETEnabled)
1993 && (llCpus == h.llCpus)
1994 && (llCpuIdLeafs == h.llCpuIdLeafs)
1995 && (ulMemorySizeMB == h.ulMemorySizeMB)
1996 && (mapBootOrder == h.mapBootOrder)
1997 && (graphicsControllerType == h.graphicsControllerType)
1998 && (ulVRAMSizeMB == h.ulVRAMSizeMB)
1999 && (cMonitors == h.cMonitors)
2000 && (fAccelerate3D == h.fAccelerate3D)
2001 && (fAccelerate2DVideo == h.fAccelerate2DVideo)
2002 && (fVideoCaptureEnabled == h.fVideoCaptureEnabled)
2003 && (u64VideoCaptureScreens == h.u64VideoCaptureScreens)
2004 && (strVideoCaptureFile == h.strVideoCaptureFile)
2005 && (ulVideoCaptureHorzRes == h.ulVideoCaptureHorzRes)
2006 && (ulVideoCaptureVertRes == h.ulVideoCaptureVertRes)
2007 && (ulVideoCaptureRate == h.ulVideoCaptureRate)
2008 && (ulVideoCaptureFPS == h.ulVideoCaptureFPS)
2009 && (ulVideoCaptureMaxTime == h.ulVideoCaptureMaxTime)
2010 && (ulVideoCaptureMaxSize == h.ulVideoCaptureMaxTime)
2011 && (firmwareType == h.firmwareType)
2012 && (pointingHIDType == h.pointingHIDType)
2013 && (keyboardHIDType == h.keyboardHIDType)
2014 && (chipsetType == h.chipsetType)
2015 && (paravirtProvider == h.paravirtProvider)
2016 && (fEmulatedUSBCardReader == h.fEmulatedUSBCardReader)
2017 && (vrdeSettings == h.vrdeSettings)
2018 && (biosSettings == h.biosSettings)
2019 && (usbSettings == h.usbSettings)
2020 && (llNetworkAdapters == h.llNetworkAdapters)
2021 && (llSerialPorts == h.llSerialPorts)
2022 && (llParallelPorts == h.llParallelPorts)
2023 && (audioAdapter == h.audioAdapter)
2024 && (llSharedFolders == h.llSharedFolders)
2025 && (clipboardMode == h.clipboardMode)
2026 && (dndMode == h.dndMode)
2027 && (ulMemoryBalloonSize == h.ulMemoryBalloonSize)
2028 && (fPageFusionEnabled == h.fPageFusionEnabled)
2029 && (llGuestProperties == h.llGuestProperties)
2030 && (strNotificationPatterns == h.strNotificationPatterns)
2031 && (ioSettings == h.ioSettings)
2032 && (pciAttachments == h.pciAttachments)
2033 && (strDefaultFrontend == h.strDefaultFrontend)
2034 )
2035 );
2036}
2037
2038/**
2039 * Comparison operator. This gets called from MachineConfigFile::operator==,
2040 * which in turn gets called from Machine::saveSettings to figure out whether
2041 * machine settings have really changed and thus need to be written out to disk.
2042 */
2043bool AttachedDevice::operator==(const AttachedDevice &a) const
2044{
2045 return ( (this == &a)
2046 || ( (deviceType == a.deviceType)
2047 && (fPassThrough == a.fPassThrough)
2048 && (fTempEject == a.fTempEject)
2049 && (fNonRotational == a.fNonRotational)
2050 && (fDiscard == a.fDiscard)
2051 && (fHotPluggable == a.fHotPluggable)
2052 && (lPort == a.lPort)
2053 && (lDevice == a.lDevice)
2054 && (uuid == a.uuid)
2055 && (strHostDriveSrc == a.strHostDriveSrc)
2056 && (strBwGroup == a.strBwGroup)
2057 )
2058 );
2059}
2060
2061/**
2062 * Comparison operator. This gets called from MachineConfigFile::operator==,
2063 * which in turn gets called from Machine::saveSettings to figure out whether
2064 * machine settings have really changed and thus need to be written out to disk.
2065 */
2066bool StorageController::operator==(const StorageController &s) const
2067{
2068 return ( (this == &s)
2069 || ( (strName == s.strName)
2070 && (storageBus == s.storageBus)
2071 && (controllerType == s.controllerType)
2072 && (ulPortCount == s.ulPortCount)
2073 && (ulInstance == s.ulInstance)
2074 && (fUseHostIOCache == s.fUseHostIOCache)
2075 && (lIDE0MasterEmulationPort == s.lIDE0MasterEmulationPort)
2076 && (lIDE0SlaveEmulationPort == s.lIDE0SlaveEmulationPort)
2077 && (lIDE1MasterEmulationPort == s.lIDE1MasterEmulationPort)
2078 && (lIDE1SlaveEmulationPort == s.lIDE1SlaveEmulationPort)
2079 && (llAttachedDevices == s.llAttachedDevices)
2080 )
2081 );
2082}
2083
2084/**
2085 * Comparison operator. This gets called from MachineConfigFile::operator==,
2086 * which in turn gets called from Machine::saveSettings to figure out whether
2087 * machine settings have really changed and thus need to be written out to disk.
2088 */
2089bool Storage::operator==(const Storage &s) const
2090{
2091 return ( (this == &s)
2092 || (llStorageControllers == s.llStorageControllers) // deep compare
2093 );
2094}
2095
2096/**
2097 * Comparison operator. This gets called from MachineConfigFile::operator==,
2098 * which in turn gets called from Machine::saveSettings to figure out whether
2099 * machine settings have really changed and thus need to be written out to disk.
2100 */
2101bool Snapshot::operator==(const Snapshot &s) const
2102{
2103 return ( (this == &s)
2104 || ( (uuid == s.uuid)
2105 && (strName == s.strName)
2106 && (strDescription == s.strDescription)
2107 && (RTTimeSpecIsEqual(&timestamp, &s.timestamp))
2108 && (strStateFile == s.strStateFile)
2109 && (hardware == s.hardware) // deep compare
2110 && (storage == s.storage) // deep compare
2111 && (llChildSnapshots == s.llChildSnapshots) // deep compare
2112 && debugging == s.debugging
2113 && autostart == s.autostart
2114 )
2115 );
2116}
2117
2118/**
2119 * IOSettings constructor.
2120 */
2121IOSettings::IOSettings()
2122{
2123 fIOCacheEnabled = true;
2124 ulIOCacheSize = 5;
2125}
2126
2127////////////////////////////////////////////////////////////////////////////////
2128//
2129// MachineConfigFile
2130//
2131////////////////////////////////////////////////////////////////////////////////
2132
2133/**
2134 * Constructor.
2135 *
2136 * If pstrFilename is != NULL, this reads the given settings file into the member
2137 * variables and various substructures and lists. Otherwise, the member variables
2138 * are initialized with default values.
2139 *
2140 * Throws variants of xml::Error for I/O, XML and logical content errors, which
2141 * the caller should catch; if this constructor does not throw, then the member
2142 * variables contain meaningful values (either from the file or defaults).
2143 *
2144 * @param strFilename
2145 */
2146MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
2147 : ConfigFileBase(pstrFilename),
2148 fCurrentStateModified(true),
2149 fAborted(false)
2150{
2151 RTTimeNow(&timeLastStateChange);
2152
2153 if (pstrFilename)
2154 {
2155 // the ConfigFileBase constructor has loaded the XML file, so now
2156 // we need only analyze what is in there
2157
2158 xml::NodesLoop nlRootChildren(*m->pelmRoot);
2159 const xml::ElementNode *pelmRootChild;
2160 while ((pelmRootChild = nlRootChildren.forAllNodes()))
2161 {
2162 if (pelmRootChild->nameEquals("Machine"))
2163 readMachine(*pelmRootChild);
2164 }
2165
2166 // clean up memory allocated by XML engine
2167 clearDocument();
2168 }
2169}
2170
2171/**
2172 * Public routine which returns true if this machine config file can have its
2173 * own media registry (which is true for settings version v1.11 and higher,
2174 * i.e. files created by VirtualBox 4.0 and higher).
2175 * @return
2176 */
2177bool MachineConfigFile::canHaveOwnMediaRegistry() const
2178{
2179 return (m->sv >= SettingsVersion_v1_11);
2180}
2181
2182/**
2183 * Public routine which allows for importing machine XML from an external DOM tree.
2184 * Use this after having called the constructor with a NULL argument.
2185 *
2186 * This is used by the OVF code if a <vbox:Machine> element has been encountered
2187 * in an OVF VirtualSystem element.
2188 *
2189 * @param elmMachine
2190 */
2191void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine)
2192{
2193 readMachine(elmMachine);
2194}
2195
2196/**
2197 * Comparison operator. This gets called from Machine::saveSettings to figure out
2198 * whether machine settings have really changed and thus need to be written out to disk.
2199 *
2200 * Even though this is called operator==, this does NOT compare all fields; the "equals"
2201 * should be understood as "has the same machine config as". The following fields are
2202 * NOT compared:
2203 * -- settings versions and file names inherited from ConfigFileBase;
2204 * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
2205 *
2206 * The "deep" comparisons marked below will invoke the operator== functions of the
2207 * structs defined in this file, which may in turn go into comparing lists of
2208 * other structures. As a result, invoking this can be expensive, but it's
2209 * less expensive than writing out XML to disk.
2210 */
2211bool MachineConfigFile::operator==(const MachineConfigFile &c) const
2212{
2213 return ( (this == &c)
2214 || ( (uuid == c.uuid)
2215 && (machineUserData == c.machineUserData)
2216 && (strStateFile == c.strStateFile)
2217 && (uuidCurrentSnapshot == c.uuidCurrentSnapshot)
2218 // skip fCurrentStateModified!
2219 && (RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange))
2220 && (fAborted == c.fAborted)
2221 && (hardwareMachine == c.hardwareMachine) // this one's deep
2222 && (storageMachine == c.storageMachine) // this one's deep
2223 && (mediaRegistry == c.mediaRegistry) // this one's deep
2224 // skip mapExtraDataItems! there is no old state available as it's always forced
2225 && (llFirstSnapshot == c.llFirstSnapshot) // this one's deep
2226 )
2227 );
2228}
2229
2230/**
2231 * Called from MachineConfigFile::readHardware() to read cpu information.
2232 * @param elmCpuid
2233 * @param ll
2234 */
2235void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
2236 CpuList &ll)
2237{
2238 xml::NodesLoop nl1(elmCpu, "Cpu");
2239 const xml::ElementNode *pelmCpu;
2240 while ((pelmCpu = nl1.forAllNodes()))
2241 {
2242 Cpu cpu;
2243
2244 if (!pelmCpu->getAttributeValue("id", cpu.ulId))
2245 throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
2246
2247 ll.push_back(cpu);
2248 }
2249}
2250
2251/**
2252 * Called from MachineConfigFile::readHardware() to cpuid information.
2253 * @param elmCpuid
2254 * @param ll
2255 */
2256void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid,
2257 CpuIdLeafsList &ll)
2258{
2259 xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf");
2260 const xml::ElementNode *pelmCpuIdLeaf;
2261 while ((pelmCpuIdLeaf = nl1.forAllNodes()))
2262 {
2263 CpuIdLeaf leaf;
2264
2265 if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.ulId))
2266 throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing"));
2267
2268 pelmCpuIdLeaf->getAttributeValue("eax", leaf.ulEax);
2269 pelmCpuIdLeaf->getAttributeValue("ebx", leaf.ulEbx);
2270 pelmCpuIdLeaf->getAttributeValue("ecx", leaf.ulEcx);
2271 pelmCpuIdLeaf->getAttributeValue("edx", leaf.ulEdx);
2272
2273 ll.push_back(leaf);
2274 }
2275}
2276
2277/**
2278 * Called from MachineConfigFile::readHardware() to network information.
2279 * @param elmNetwork
2280 * @param ll
2281 */
2282void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
2283 NetworkAdaptersList &ll)
2284{
2285 xml::NodesLoop nl1(elmNetwork, "Adapter");
2286 const xml::ElementNode *pelmAdapter;
2287 while ((pelmAdapter = nl1.forAllNodes()))
2288 {
2289 NetworkAdapter nic;
2290
2291 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
2292 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
2293
2294 Utf8Str strTemp;
2295 if (pelmAdapter->getAttributeValue("type", strTemp))
2296 {
2297 if (strTemp == "Am79C970A")
2298 nic.type = NetworkAdapterType_Am79C970A;
2299 else if (strTemp == "Am79C973")
2300 nic.type = NetworkAdapterType_Am79C973;
2301 else if (strTemp == "82540EM")
2302 nic.type = NetworkAdapterType_I82540EM;
2303 else if (strTemp == "82543GC")
2304 nic.type = NetworkAdapterType_I82543GC;
2305 else if (strTemp == "82545EM")
2306 nic.type = NetworkAdapterType_I82545EM;
2307 else if (strTemp == "virtio")
2308 nic.type = NetworkAdapterType_Virtio;
2309 else
2310 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
2311 }
2312
2313 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
2314 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
2315 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
2316 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
2317
2318 if (pelmAdapter->getAttributeValue("promiscuousModePolicy", strTemp))
2319 {
2320 if (strTemp == "Deny")
2321 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_Deny;
2322 else if (strTemp == "AllowNetwork")
2323 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowNetwork;
2324 else if (strTemp == "AllowAll")
2325 nic.enmPromiscModePolicy = NetworkAdapterPromiscModePolicy_AllowAll;
2326 else
2327 throw ConfigFileError(this, pelmAdapter,
2328 N_("Invalid value '%s' in Adapter/@promiscuousModePolicy attribute"), strTemp.c_str());
2329 }
2330
2331 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
2332 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
2333 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
2334 pelmAdapter->getAttributeValue("bandwidthGroup", nic.strBandwidthGroup);
2335
2336 xml::ElementNodesList llNetworkModes;
2337 pelmAdapter->getChildElements(llNetworkModes);
2338 xml::ElementNodesList::iterator it;
2339 /* We should have only active mode descriptor and disabled modes set */
2340 if (llNetworkModes.size() > 2)
2341 {
2342 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
2343 }
2344 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
2345 {
2346 const xml::ElementNode *pelmNode = *it;
2347 if (pelmNode->nameEquals("DisabledModes"))
2348 {
2349 xml::ElementNodesList llDisabledNetworkModes;
2350 xml::ElementNodesList::iterator itDisabled;
2351 pelmNode->getChildElements(llDisabledNetworkModes);
2352 /* run over disabled list and load settings */
2353 for (itDisabled = llDisabledNetworkModes.begin();
2354 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
2355 {
2356 const xml::ElementNode *pelmDisabledNode = *itDisabled;
2357 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
2358 }
2359 }
2360 else
2361 readAttachedNetworkMode(*pelmNode, true, nic);
2362 }
2363 // else: default is NetworkAttachmentType_Null
2364
2365 ll.push_back(nic);
2366 }
2367}
2368
2369void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
2370{
2371 NetworkAttachmentType_T enmAttachmentType = NetworkAttachmentType_Null;
2372
2373 if (elmMode.nameEquals("NAT"))
2374 {
2375 enmAttachmentType = NetworkAttachmentType_NAT;
2376
2377 elmMode.getAttributeValue("network", nic.nat.strNetwork);
2378 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
2379 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
2380 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
2381 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
2382 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
2383 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
2384 const xml::ElementNode *pelmDNS;
2385 if ((pelmDNS = elmMode.findChildElement("DNS")))
2386 {
2387 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDNSPassDomain);
2388 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDNSProxy);
2389 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDNSUseHostResolver);
2390 }
2391 const xml::ElementNode *pelmAlias;
2392 if ((pelmAlias = elmMode.findChildElement("Alias")))
2393 {
2394 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
2395 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
2396 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
2397 }
2398 const xml::ElementNode *pelmTFTP;
2399 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
2400 {
2401 pelmTFTP->getAttributeValue("prefix", nic.nat.strTFTPPrefix);
2402 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTFTPBootFile);
2403 pelmTFTP->getAttributeValue("next-server", nic.nat.strTFTPNextServer);
2404 }
2405
2406 readNATForwardRuleList(elmMode, nic.nat.llRules);
2407 }
2408 else if ( elmMode.nameEquals("HostInterface")
2409 || elmMode.nameEquals("BridgedInterface"))
2410 {
2411 enmAttachmentType = NetworkAttachmentType_Bridged;
2412
2413 elmMode.getAttributeValue("name", nic.strBridgedName); // optional bridged interface name
2414 }
2415 else if (elmMode.nameEquals("InternalNetwork"))
2416 {
2417 enmAttachmentType = NetworkAttachmentType_Internal;
2418
2419 if (!elmMode.getAttributeValue("name", nic.strInternalNetworkName)) // required network name
2420 throw ConfigFileError(this, &elmMode, N_("Required InternalNetwork/@name element is missing"));
2421 }
2422 else if (elmMode.nameEquals("HostOnlyInterface"))
2423 {
2424 enmAttachmentType = NetworkAttachmentType_HostOnly;
2425
2426 if (!elmMode.getAttributeValue("name", nic.strHostOnlyName)) // required network name
2427 throw ConfigFileError(this, &elmMode, N_("Required HostOnlyInterface/@name element is missing"));
2428 }
2429 else if (elmMode.nameEquals("GenericInterface"))
2430 {
2431 enmAttachmentType = NetworkAttachmentType_Generic;
2432
2433 elmMode.getAttributeValue("driver", nic.strGenericDriver); // optional network attachment driver
2434
2435 // get all properties
2436 xml::NodesLoop nl(elmMode);
2437 const xml::ElementNode *pelmModeChild;
2438 while ((pelmModeChild = nl.forAllNodes()))
2439 {
2440 if (pelmModeChild->nameEquals("Property"))
2441 {
2442 Utf8Str strPropName, strPropValue;
2443 if ( pelmModeChild->getAttributeValue("name", strPropName)
2444 && pelmModeChild->getAttributeValue("value", strPropValue) )
2445 nic.genericProperties[strPropName] = strPropValue;
2446 else
2447 throw ConfigFileError(this, pelmModeChild, N_("Required GenericInterface/Property/@name or @value attribute is missing"));
2448 }
2449 }
2450 }
2451 else if (elmMode.nameEquals("NATNetwork"))
2452 {
2453 enmAttachmentType = NetworkAttachmentType_NATNetwork;
2454
2455 if (!elmMode.getAttributeValue("name", nic.strNATNetworkName)) // required network name
2456 throw ConfigFileError(this, &elmMode, N_("Required NATNetwork/@name element is missing"));
2457 }
2458 else if (elmMode.nameEquals("VDE"))
2459 {
2460 enmAttachmentType = NetworkAttachmentType_Generic;
2461
2462 com::Utf8Str strVDEName;
2463 elmMode.getAttributeValue("network", strVDEName); // optional network name
2464 nic.strGenericDriver = "VDE";
2465 nic.genericProperties["network"] = strVDEName;
2466 }
2467
2468 if (fEnabled && enmAttachmentType != NetworkAttachmentType_Null)
2469 nic.mode = enmAttachmentType;
2470}
2471
2472/**
2473 * Called from MachineConfigFile::readHardware() to read serial port information.
2474 * @param elmUART
2475 * @param ll
2476 */
2477void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
2478 SerialPortsList &ll)
2479{
2480 xml::NodesLoop nl1(elmUART, "Port");
2481 const xml::ElementNode *pelmPort;
2482 while ((pelmPort = nl1.forAllNodes()))
2483 {
2484 SerialPort port;
2485 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
2486 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
2487
2488 // slot must be unique
2489 for (SerialPortsList::const_iterator it = ll.begin();
2490 it != ll.end();
2491 ++it)
2492 if ((*it).ulSlot == port.ulSlot)
2493 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
2494
2495 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
2496 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
2497 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
2498 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
2499 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
2500 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
2501
2502 Utf8Str strPortMode;
2503 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
2504 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
2505 if (strPortMode == "RawFile")
2506 port.portMode = PortMode_RawFile;
2507 else if (strPortMode == "HostPipe")
2508 port.portMode = PortMode_HostPipe;
2509 else if (strPortMode == "HostDevice")
2510 port.portMode = PortMode_HostDevice;
2511 else if (strPortMode == "Disconnected")
2512 port.portMode = PortMode_Disconnected;
2513 else
2514 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
2515
2516 pelmPort->getAttributeValue("path", port.strPath);
2517 pelmPort->getAttributeValue("server", port.fServer);
2518
2519 ll.push_back(port);
2520 }
2521}
2522
2523/**
2524 * Called from MachineConfigFile::readHardware() to read parallel port information.
2525 * @param elmLPT
2526 * @param ll
2527 */
2528void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
2529 ParallelPortsList &ll)
2530{
2531 xml::NodesLoop nl1(elmLPT, "Port");
2532 const xml::ElementNode *pelmPort;
2533 while ((pelmPort = nl1.forAllNodes()))
2534 {
2535 ParallelPort port;
2536 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
2537 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
2538
2539 // slot must be unique
2540 for (ParallelPortsList::const_iterator it = ll.begin();
2541 it != ll.end();
2542 ++it)
2543 if ((*it).ulSlot == port.ulSlot)
2544 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
2545
2546 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
2547 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
2548 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
2549 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
2550 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
2551 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
2552
2553 pelmPort->getAttributeValue("path", port.strPath);
2554
2555 ll.push_back(port);
2556 }
2557}
2558
2559/**
2560 * Called from MachineConfigFile::readHardware() to read audio adapter information
2561 * and maybe fix driver information depending on the current host hardware.
2562 *
2563 * @param elmAudioAdapter "AudioAdapter" XML element.
2564 * @param hw
2565 */
2566void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
2567 AudioAdapter &aa)
2568{
2569 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
2570
2571 Utf8Str strTemp;
2572 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
2573 {
2574 if (strTemp == "SB16")
2575 aa.controllerType = AudioControllerType_SB16;
2576 else if (strTemp == "AC97")
2577 aa.controllerType = AudioControllerType_AC97;
2578 else if (strTemp == "HDA")
2579 aa.controllerType = AudioControllerType_HDA;
2580 else
2581 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
2582 }
2583
2584 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
2585 {
2586 // settings before 1.3 used lower case so make sure this is case-insensitive
2587 strTemp.toUpper();
2588 if (strTemp == "NULL")
2589 aa.driverType = AudioDriverType_Null;
2590 else if (strTemp == "WINMM")
2591 aa.driverType = AudioDriverType_WinMM;
2592 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
2593 aa.driverType = AudioDriverType_DirectSound;
2594 else if (strTemp == "SOLAUDIO")
2595 aa.driverType = AudioDriverType_SolAudio;
2596 else if (strTemp == "ALSA")
2597 aa.driverType = AudioDriverType_ALSA;
2598 else if (strTemp == "PULSE")
2599 aa.driverType = AudioDriverType_Pulse;
2600 else if (strTemp == "OSS")
2601 aa.driverType = AudioDriverType_OSS;
2602 else if (strTemp == "COREAUDIO")
2603 aa.driverType = AudioDriverType_CoreAudio;
2604 else if (strTemp == "MMPM")
2605 aa.driverType = AudioDriverType_MMPM;
2606 else
2607 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
2608
2609 // now check if this is actually supported on the current host platform;
2610 // people might be opening a file created on a Windows host, and that
2611 // VM should still start on a Linux host
2612 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
2613 aa.driverType = getHostDefaultAudioDriver();
2614 }
2615}
2616
2617/**
2618 * Called from MachineConfigFile::readHardware() to read guest property information.
2619 * @param elmGuestProperties
2620 * @param hw
2621 */
2622void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
2623 Hardware &hw)
2624{
2625 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
2626 const xml::ElementNode *pelmProp;
2627 while ((pelmProp = nl1.forAllNodes()))
2628 {
2629 GuestProperty prop;
2630 pelmProp->getAttributeValue("name", prop.strName);
2631 pelmProp->getAttributeValue("value", prop.strValue);
2632
2633 pelmProp->getAttributeValue("timestamp", prop.timestamp);
2634 pelmProp->getAttributeValue("flags", prop.strFlags);
2635 hw.llGuestProperties.push_back(prop);
2636 }
2637
2638 elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
2639}
2640
2641/**
2642 * Helper function to read attributes that are common to <SATAController> (pre-1.7)
2643 * and <StorageController>.
2644 * @param elmStorageController
2645 * @param strg
2646 */
2647void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
2648 StorageController &sctl)
2649{
2650 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
2651 elmStorageController.getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
2652 elmStorageController.getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
2653 elmStorageController.getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
2654 elmStorageController.getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
2655
2656 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
2657}
2658
2659/**
2660 * Reads in a <Hardware> block and stores it in the given structure. Used
2661 * both directly from readMachine and from readSnapshot, since snapshots
2662 * have their own hardware sections.
2663 *
2664 * For legacy pre-1.7 settings we also need a storage structure because
2665 * the IDE and SATA controllers used to be defined under <Hardware>.
2666 *
2667 * @param elmHardware
2668 * @param hw
2669 */
2670void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
2671 Hardware &hw,
2672 Storage &strg)
2673{
2674 if (!elmHardware.getAttributeValue("version", hw.strVersion))
2675 {
2676 /* KLUDGE ALERT! For a while during the 3.1 development this was not
2677 written because it was thought to have a default value of "2". For
2678 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
2679 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
2680 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
2681 missing the hardware version, then it probably should be "2" instead
2682 of "1". */
2683 if (m->sv < SettingsVersion_v1_7)
2684 hw.strVersion = "1";
2685 else
2686 hw.strVersion = "2";
2687 }
2688 Utf8Str strUUID;
2689 if (elmHardware.getAttributeValue("uuid", strUUID))
2690 parseUUID(hw.uuid, strUUID);
2691
2692 xml::NodesLoop nl1(elmHardware);
2693 const xml::ElementNode *pelmHwChild;
2694 while ((pelmHwChild = nl1.forAllNodes()))
2695 {
2696 if (pelmHwChild->nameEquals("CPU"))
2697 {
2698 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
2699 {
2700 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
2701 const xml::ElementNode *pelmCPUChild;
2702 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
2703 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
2704 }
2705
2706 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
2707 pelmHwChild->getAttributeValue("executionCap", hw.ulCpuExecutionCap);
2708
2709 const xml::ElementNode *pelmCPUChild;
2710 if (hw.fCpuHotPlug)
2711 {
2712 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
2713 readCpuTree(*pelmCPUChild, hw.llCpus);
2714 }
2715
2716 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
2717 {
2718 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
2719 }
2720 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
2721 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
2722 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
2723 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
2724 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
2725 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
2726 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExUX")))
2727 pelmCPUChild->getAttributeValue("enabled", hw.fUnrestrictedExecution);
2728 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtForce")))
2729 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirtForce);
2730
2731 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
2732 {
2733 /* The default for pre 3.1 was false, so we must respect that. */
2734 if (m->sv < SettingsVersion_v1_9)
2735 hw.fPAE = false;
2736 }
2737 else
2738 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
2739
2740 bool fLongMode;
2741 if ( (pelmCPUChild = pelmHwChild->findChildElement("LongMode"))
2742 && pelmCPUChild->getAttributeValue("enabled", fLongMode) )
2743 hw.enmLongMode = fLongMode ? Hardware::LongMode_Enabled : Hardware::LongMode_Disabled;
2744 else
2745 hw.enmLongMode = Hardware::LongMode_Legacy;
2746
2747 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
2748 pelmCPUChild->getAttributeValue("enabled", hw.fSyntheticCpu);
2749
2750 if ((pelmCPUChild = pelmHwChild->findChildElement("TripleFaultReset")))
2751 pelmCPUChild->getAttributeValue("enabled", hw.fTripleFaultReset);
2752
2753 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
2754 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
2755 }
2756 else if (pelmHwChild->nameEquals("Memory"))
2757 {
2758 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
2759 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
2760 }
2761 else if (pelmHwChild->nameEquals("Firmware"))
2762 {
2763 Utf8Str strFirmwareType;
2764 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
2765 {
2766 if ( (strFirmwareType == "BIOS")
2767 || (strFirmwareType == "1") // some trunk builds used the number here
2768 )
2769 hw.firmwareType = FirmwareType_BIOS;
2770 else if ( (strFirmwareType == "EFI")
2771 || (strFirmwareType == "2") // some trunk builds used the number here
2772 )
2773 hw.firmwareType = FirmwareType_EFI;
2774 else if ( strFirmwareType == "EFI32")
2775 hw.firmwareType = FirmwareType_EFI32;
2776 else if ( strFirmwareType == "EFI64")
2777 hw.firmwareType = FirmwareType_EFI64;
2778 else if ( strFirmwareType == "EFIDUAL")
2779 hw.firmwareType = FirmwareType_EFIDUAL;
2780 else
2781 throw ConfigFileError(this,
2782 pelmHwChild,
2783 N_("Invalid value '%s' in Firmware/@type"),
2784 strFirmwareType.c_str());
2785 }
2786 }
2787 else if (pelmHwChild->nameEquals("HID"))
2788 {
2789 Utf8Str strHIDType;
2790 if (pelmHwChild->getAttributeValue("Keyboard", strHIDType))
2791 {
2792 if (strHIDType == "None")
2793 hw.keyboardHIDType = KeyboardHIDType_None;
2794 else if (strHIDType == "USBKeyboard")
2795 hw.keyboardHIDType = KeyboardHIDType_USBKeyboard;
2796 else if (strHIDType == "PS2Keyboard")
2797 hw.keyboardHIDType = KeyboardHIDType_PS2Keyboard;
2798 else if (strHIDType == "ComboKeyboard")
2799 hw.keyboardHIDType = KeyboardHIDType_ComboKeyboard;
2800 else
2801 throw ConfigFileError(this,
2802 pelmHwChild,
2803 N_("Invalid value '%s' in HID/Keyboard/@type"),
2804 strHIDType.c_str());
2805 }
2806 if (pelmHwChild->getAttributeValue("Pointing", strHIDType))
2807 {
2808 if (strHIDType == "None")
2809 hw.pointingHIDType = PointingHIDType_None;
2810 else if (strHIDType == "USBMouse")
2811 hw.pointingHIDType = PointingHIDType_USBMouse;
2812 else if (strHIDType == "USBTablet")
2813 hw.pointingHIDType = PointingHIDType_USBTablet;
2814 else if (strHIDType == "PS2Mouse")
2815 hw.pointingHIDType = PointingHIDType_PS2Mouse;
2816 else if (strHIDType == "ComboMouse")
2817 hw.pointingHIDType = PointingHIDType_ComboMouse;
2818 else if (strHIDType == "USBMultiTouch")
2819 hw.pointingHIDType = PointingHIDType_USBMultiTouch;
2820 else
2821 throw ConfigFileError(this,
2822 pelmHwChild,
2823 N_("Invalid value '%s' in HID/Pointing/@type"),
2824 strHIDType.c_str());
2825 }
2826 }
2827 else if (pelmHwChild->nameEquals("Chipset"))
2828 {
2829 Utf8Str strChipsetType;
2830 if (pelmHwChild->getAttributeValue("type", strChipsetType))
2831 {
2832 if (strChipsetType == "PIIX3")
2833 hw.chipsetType = ChipsetType_PIIX3;
2834 else if (strChipsetType == "ICH9")
2835 hw.chipsetType = ChipsetType_ICH9;
2836 else
2837 throw ConfigFileError(this,
2838 pelmHwChild,
2839 N_("Invalid value '%s' in Chipset/@type"),
2840 strChipsetType.c_str());
2841 }
2842 }
2843 else if (pelmHwChild->nameEquals("Paravirt"))
2844 {
2845 Utf8Str strProvider;
2846 if (pelmHwChild->getAttributeValue("provider", strProvider))
2847 {
2848 if (strProvider == "None")
2849 hw.paravirtProvider = ParavirtProvider_None;
2850 else if (strProvider == "Default")
2851 hw.paravirtProvider = ParavirtProvider_Default;
2852 else if (strProvider == "Legacy")
2853 hw.paravirtProvider = ParavirtProvider_Legacy;
2854 else if (strProvider == "Minimal")
2855 hw.paravirtProvider = ParavirtProvider_Minimal;
2856 else if (strProvider == "HyperV")
2857 hw.paravirtProvider = ParavirtProvider_HyperV;
2858 else
2859 throw ConfigFileError(this,
2860 pelmHwChild,
2861 N_("Invalid value '%s' in Paravirt/@provider attribute"),
2862 strProvider.c_str());
2863 }
2864 }
2865 else if (pelmHwChild->nameEquals("HPET"))
2866 {
2867 pelmHwChild->getAttributeValue("enabled", hw.fHPETEnabled);
2868 }
2869 else if (pelmHwChild->nameEquals("Boot"))
2870 {
2871 hw.mapBootOrder.clear();
2872
2873 xml::NodesLoop nl2(*pelmHwChild, "Order");
2874 const xml::ElementNode *pelmOrder;
2875 while ((pelmOrder = nl2.forAllNodes()))
2876 {
2877 uint32_t ulPos;
2878 Utf8Str strDevice;
2879 if (!pelmOrder->getAttributeValue("position", ulPos))
2880 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
2881
2882 if ( ulPos < 1
2883 || ulPos > SchemaDefs::MaxBootPosition
2884 )
2885 throw ConfigFileError(this,
2886 pelmOrder,
2887 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
2888 ulPos,
2889 SchemaDefs::MaxBootPosition + 1);
2890 // XML is 1-based but internal data is 0-based
2891 --ulPos;
2892
2893 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
2894 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
2895
2896 if (!pelmOrder->getAttributeValue("device", strDevice))
2897 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
2898
2899 DeviceType_T type;
2900 if (strDevice == "None")
2901 type = DeviceType_Null;
2902 else if (strDevice == "Floppy")
2903 type = DeviceType_Floppy;
2904 else if (strDevice == "DVD")
2905 type = DeviceType_DVD;
2906 else if (strDevice == "HardDisk")
2907 type = DeviceType_HardDisk;
2908 else if (strDevice == "Network")
2909 type = DeviceType_Network;
2910 else
2911 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
2912 hw.mapBootOrder[ulPos] = type;
2913 }
2914 }
2915 else if (pelmHwChild->nameEquals("Display"))
2916 {
2917 Utf8Str strGraphicsControllerType;
2918 if (!pelmHwChild->getAttributeValue("controller", strGraphicsControllerType))
2919 hw.graphicsControllerType = GraphicsControllerType_VBoxVGA;
2920 else
2921 {
2922 strGraphicsControllerType.toUpper();
2923 GraphicsControllerType_T type;
2924 if (strGraphicsControllerType == "VBOXVGA")
2925 type = GraphicsControllerType_VBoxVGA;
2926 else if (strGraphicsControllerType == "VMSVGA")
2927 type = GraphicsControllerType_VMSVGA;
2928 else if (strGraphicsControllerType == "NONE")
2929 type = GraphicsControllerType_Null;
2930 else
2931 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Display/@controller attribute"), strGraphicsControllerType.c_str());
2932 hw.graphicsControllerType = type;
2933 }
2934 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
2935 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
2936 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
2937 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
2938 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
2939 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
2940 }
2941 else if (pelmHwChild->nameEquals("VideoCapture"))
2942 {
2943 pelmHwChild->getAttributeValue("enabled", hw.fVideoCaptureEnabled);
2944 pelmHwChild->getAttributeValue("screens", hw.u64VideoCaptureScreens);
2945 pelmHwChild->getAttributeValuePath("file", hw.strVideoCaptureFile);
2946 pelmHwChild->getAttributeValue("horzRes", hw.ulVideoCaptureHorzRes);
2947 pelmHwChild->getAttributeValue("vertRes", hw.ulVideoCaptureVertRes);
2948 pelmHwChild->getAttributeValue("rate", hw.ulVideoCaptureRate);
2949 pelmHwChild->getAttributeValue("fps", hw.ulVideoCaptureFPS);
2950 pelmHwChild->getAttributeValue("maxTime", hw.ulVideoCaptureMaxTime);
2951 pelmHwChild->getAttributeValue("maxSize", hw.ulVideoCaptureMaxSize);
2952 }
2953 else if (pelmHwChild->nameEquals("RemoteDisplay"))
2954 {
2955 pelmHwChild->getAttributeValue("enabled", hw.vrdeSettings.fEnabled);
2956
2957 Utf8Str str;
2958 if (pelmHwChild->getAttributeValue("port", str))
2959 hw.vrdeSettings.mapProperties["TCP/Ports"] = str;
2960 if (pelmHwChild->getAttributeValue("netAddress", str))
2961 hw.vrdeSettings.mapProperties["TCP/Address"] = str;
2962
2963 Utf8Str strAuthType;
2964 if (pelmHwChild->getAttributeValue("authType", strAuthType))
2965 {
2966 // settings before 1.3 used lower case so make sure this is case-insensitive
2967 strAuthType.toUpper();
2968 if (strAuthType == "NULL")
2969 hw.vrdeSettings.authType = AuthType_Null;
2970 else if (strAuthType == "GUEST")
2971 hw.vrdeSettings.authType = AuthType_Guest;
2972 else if (strAuthType == "EXTERNAL")
2973 hw.vrdeSettings.authType = AuthType_External;
2974 else
2975 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
2976 }
2977
2978 pelmHwChild->getAttributeValue("authLibrary", hw.vrdeSettings.strAuthLibrary);
2979 pelmHwChild->getAttributeValue("authTimeout", hw.vrdeSettings.ulAuthTimeout);
2980 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
2981 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
2982
2983 /* 3.2 and 4.0 betas, 4.0 has this information in VRDEProperties. */
2984 const xml::ElementNode *pelmVideoChannel;
2985 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
2986 {
2987 bool fVideoChannel = false;
2988 pelmVideoChannel->getAttributeValue("enabled", fVideoChannel);
2989 hw.vrdeSettings.mapProperties["VideoChannel/Enabled"] = fVideoChannel? "true": "false";
2990
2991 uint32_t ulVideoChannelQuality = 75;
2992 pelmVideoChannel->getAttributeValue("quality", ulVideoChannelQuality);
2993 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
2994 char *pszBuffer = NULL;
2995 if (RTStrAPrintf(&pszBuffer, "%d", ulVideoChannelQuality) >= 0)
2996 {
2997 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = pszBuffer;
2998 RTStrFree(pszBuffer);
2999 }
3000 else
3001 hw.vrdeSettings.mapProperties["VideoChannel/Quality"] = "75";
3002 }
3003 pelmHwChild->getAttributeValue("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
3004
3005 const xml::ElementNode *pelmProperties = pelmHwChild->findChildElement("VRDEProperties");
3006 if (pelmProperties != NULL)
3007 {
3008 xml::NodesLoop nl(*pelmProperties);
3009 const xml::ElementNode *pelmProperty;
3010 while ((pelmProperty = nl.forAllNodes()))
3011 {
3012 if (pelmProperty->nameEquals("Property"))
3013 {
3014 /* <Property name="TCP/Ports" value="3000-3002"/> */
3015 Utf8Str strName, strValue;
3016 if ( pelmProperty->getAttributeValue("name", strName)
3017 && pelmProperty->getAttributeValue("value", strValue))
3018 hw.vrdeSettings.mapProperties[strName] = strValue;
3019 else
3020 throw ConfigFileError(this, pelmProperty, N_("Required VRDE Property/@name or @value attribute is missing"));
3021 }
3022 }
3023 }
3024 }
3025 else if (pelmHwChild->nameEquals("BIOS"))
3026 {
3027 const xml::ElementNode *pelmBIOSChild;
3028 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
3029 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
3030 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
3031 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
3032 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
3033 {
3034 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
3035 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
3036 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
3037 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
3038 }
3039 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
3040 {
3041 Utf8Str strBootMenuMode;
3042 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
3043 {
3044 // settings before 1.3 used lower case so make sure this is case-insensitive
3045 strBootMenuMode.toUpper();
3046 if (strBootMenuMode == "DISABLED")
3047 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
3048 else if (strBootMenuMode == "MENUONLY")
3049 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
3050 else if (strBootMenuMode == "MESSAGEANDMENU")
3051 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
3052 else
3053 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
3054 }
3055 }
3056 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
3057 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
3058 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
3059 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
3060
3061 // legacy BIOS/IDEController (pre 1.7)
3062 if ( (m->sv < SettingsVersion_v1_7)
3063 && (pelmBIOSChild = pelmHwChild->findChildElement("IDEController"))
3064 )
3065 {
3066 StorageController sctl;
3067 sctl.strName = "IDE Controller";
3068 sctl.storageBus = StorageBus_IDE;
3069
3070 Utf8Str strType;
3071 if (pelmBIOSChild->getAttributeValue("type", strType))
3072 {
3073 if (strType == "PIIX3")
3074 sctl.controllerType = StorageControllerType_PIIX3;
3075 else if (strType == "PIIX4")
3076 sctl.controllerType = StorageControllerType_PIIX4;
3077 else if (strType == "ICH6")
3078 sctl.controllerType = StorageControllerType_ICH6;
3079 else
3080 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
3081 }
3082 sctl.ulPortCount = 2;
3083 strg.llStorageControllers.push_back(sctl);
3084 }
3085 }
3086 else if ( (m->sv <= SettingsVersion_v1_14)
3087 && pelmHwChild->nameEquals("USBController"))
3088 {
3089 bool fEnabled = false;
3090
3091 pelmHwChild->getAttributeValue("enabled", fEnabled);
3092 if (fEnabled)
3093 {
3094 /* Create OHCI controller with default name. */
3095 USBController ctrl;
3096
3097 ctrl.strName = "OHCI";
3098 ctrl.enmType = USBControllerType_OHCI;
3099 hw.usbSettings.llUSBControllers.push_back(ctrl);
3100 }
3101
3102 pelmHwChild->getAttributeValue("enabledEhci", fEnabled);
3103 if (fEnabled)
3104 {
3105 /* Create OHCI controller with default name. */
3106 USBController ctrl;
3107
3108 ctrl.strName = "EHCI";
3109 ctrl.enmType = USBControllerType_EHCI;
3110 hw.usbSettings.llUSBControllers.push_back(ctrl);
3111 }
3112
3113 readUSBDeviceFilters(*pelmHwChild,
3114 hw.usbSettings.llDeviceFilters);
3115 }
3116 else if (pelmHwChild->nameEquals("USB"))
3117 {
3118 const xml::ElementNode *pelmUSBChild;
3119
3120 if ((pelmUSBChild = pelmHwChild->findChildElement("Controllers")))
3121 {
3122 xml::NodesLoop nl2(*pelmUSBChild, "Controller");
3123 const xml::ElementNode *pelmCtrl;
3124
3125 while ((pelmCtrl = nl2.forAllNodes()))
3126 {
3127 USBController ctrl;
3128 com::Utf8Str strCtrlType;
3129
3130 pelmCtrl->getAttributeValue("name", ctrl.strName);
3131
3132 if (pelmCtrl->getAttributeValue("type", strCtrlType))
3133 {
3134 if (strCtrlType == "OHCI")
3135 ctrl.enmType = USBControllerType_OHCI;
3136 else if (strCtrlType == "EHCI")
3137 ctrl.enmType = USBControllerType_EHCI;
3138 else if (strCtrlType == "XHCI")
3139 ctrl.enmType = USBControllerType_XHCI;
3140 else
3141 throw ConfigFileError(this, pelmCtrl, N_("Invalid value '%s' for Controller/@type attribute"), strCtrlType.c_str());
3142 }
3143
3144 hw.usbSettings.llUSBControllers.push_back(ctrl);
3145 }
3146 }
3147
3148 if ((pelmUSBChild = pelmHwChild->findChildElement("DeviceFilters")))
3149 readUSBDeviceFilters(*pelmUSBChild, hw.usbSettings.llDeviceFilters);
3150 }
3151 else if ( m->sv < SettingsVersion_v1_7
3152 && pelmHwChild->nameEquals("SATAController"))
3153 {
3154 bool f;
3155 if ( pelmHwChild->getAttributeValue("enabled", f)
3156 && f)
3157 {
3158 StorageController sctl;
3159 sctl.strName = "SATA Controller";
3160 sctl.storageBus = StorageBus_SATA;
3161 sctl.controllerType = StorageControllerType_IntelAhci;
3162
3163 readStorageControllerAttributes(*pelmHwChild, sctl);
3164
3165 strg.llStorageControllers.push_back(sctl);
3166 }
3167 }
3168 else if (pelmHwChild->nameEquals("Network"))
3169 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
3170 else if (pelmHwChild->nameEquals("RTC"))
3171 {
3172 Utf8Str strLocalOrUTC;
3173 machineUserData.fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
3174 && strLocalOrUTC == "UTC";
3175 }
3176 else if ( pelmHwChild->nameEquals("UART")
3177 || pelmHwChild->nameEquals("Uart") // used before 1.3
3178 )
3179 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
3180 else if ( pelmHwChild->nameEquals("LPT")
3181 || pelmHwChild->nameEquals("Lpt") // used before 1.3
3182 )
3183 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
3184 else if (pelmHwChild->nameEquals("AudioAdapter"))
3185 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
3186 else if (pelmHwChild->nameEquals("SharedFolders"))
3187 {
3188 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
3189 const xml::ElementNode *pelmFolder;
3190 while ((pelmFolder = nl2.forAllNodes()))
3191 {
3192 SharedFolder sf;
3193 pelmFolder->getAttributeValue("name", sf.strName);
3194 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
3195 pelmFolder->getAttributeValue("writable", sf.fWritable);
3196 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
3197 hw.llSharedFolders.push_back(sf);
3198 }
3199 }
3200 else if (pelmHwChild->nameEquals("Clipboard"))
3201 {
3202 Utf8Str strTemp;
3203 if (pelmHwChild->getAttributeValue("mode", strTemp))
3204 {
3205 if (strTemp == "Disabled")
3206 hw.clipboardMode = ClipboardMode_Disabled;
3207 else if (strTemp == "HostToGuest")
3208 hw.clipboardMode = ClipboardMode_HostToGuest;
3209 else if (strTemp == "GuestToHost")
3210 hw.clipboardMode = ClipboardMode_GuestToHost;
3211 else if (strTemp == "Bidirectional")
3212 hw.clipboardMode = ClipboardMode_Bidirectional;
3213 else
3214 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
3215 }
3216 }
3217 else if (pelmHwChild->nameEquals("DragAndDrop"))
3218 {
3219 Utf8Str strTemp;
3220 if (pelmHwChild->getAttributeValue("mode", strTemp))
3221 {
3222 if (strTemp == "Disabled")
3223 hw.dndMode = DnDMode_Disabled;
3224 else if (strTemp == "HostToGuest")
3225 hw.dndMode = DnDMode_HostToGuest;
3226 else if (strTemp == "GuestToHost")
3227 hw.dndMode = DnDMode_GuestToHost;
3228 else if (strTemp == "Bidirectional")
3229 hw.dndMode = DnDMode_Bidirectional;
3230 else
3231 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in DragAndDrop/@mode attribute"), strTemp.c_str());
3232 }
3233 }
3234 else if (pelmHwChild->nameEquals("Guest"))
3235 {
3236 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
3237 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
3238 }
3239 else if (pelmHwChild->nameEquals("GuestProperties"))
3240 readGuestProperties(*pelmHwChild, hw);
3241 else if (pelmHwChild->nameEquals("IO"))
3242 {
3243 const xml::ElementNode *pelmBwGroups;
3244 const xml::ElementNode *pelmIOChild;
3245
3246 if ((pelmIOChild = pelmHwChild->findChildElement("IoCache")))
3247 {
3248 pelmIOChild->getAttributeValue("enabled", hw.ioSettings.fIOCacheEnabled);
3249 pelmIOChild->getAttributeValue("size", hw.ioSettings.ulIOCacheSize);
3250 }
3251
3252 if ((pelmBwGroups = pelmHwChild->findChildElement("BandwidthGroups")))
3253 {
3254 xml::NodesLoop nl2(*pelmBwGroups, "BandwidthGroup");
3255 const xml::ElementNode *pelmBandwidthGroup;
3256 while ((pelmBandwidthGroup = nl2.forAllNodes()))
3257 {
3258 BandwidthGroup gr;
3259 Utf8Str strTemp;
3260
3261 pelmBandwidthGroup->getAttributeValue("name", gr.strName);
3262
3263 if (pelmBandwidthGroup->getAttributeValue("type", strTemp))
3264 {
3265 if (strTemp == "Disk")
3266 gr.enmType = BandwidthGroupType_Disk;
3267 else if (strTemp == "Network")
3268 gr.enmType = BandwidthGroupType_Network;
3269 else
3270 throw ConfigFileError(this, pelmBandwidthGroup, N_("Invalid value '%s' in BandwidthGroup/@type attribute"), strTemp.c_str());
3271 }
3272 else
3273 throw ConfigFileError(this, pelmBandwidthGroup, N_("Missing BandwidthGroup/@type attribute"));
3274
3275 if (!pelmBandwidthGroup->getAttributeValue("maxBytesPerSec", gr.cMaxBytesPerSec))
3276 {
3277 pelmBandwidthGroup->getAttributeValue("maxMbPerSec", gr.cMaxBytesPerSec);
3278 gr.cMaxBytesPerSec *= _1M;
3279 }
3280 hw.ioSettings.llBandwidthGroups.push_back(gr);
3281 }
3282 }
3283 }
3284 else if (pelmHwChild->nameEquals("HostPci"))
3285 {
3286 const xml::ElementNode *pelmDevices;
3287
3288 if ((pelmDevices = pelmHwChild->findChildElement("Devices")))
3289 {
3290 xml::NodesLoop nl2(*pelmDevices, "Device");
3291 const xml::ElementNode *pelmDevice;
3292 while ((pelmDevice = nl2.forAllNodes()))
3293 {
3294 HostPCIDeviceAttachment hpda;
3295
3296 if (!pelmDevice->getAttributeValue("host", hpda.uHostAddress))
3297 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@host attribute"));
3298
3299 if (!pelmDevice->getAttributeValue("guest", hpda.uGuestAddress))
3300 throw ConfigFileError(this, pelmDevice, N_("Missing Device/@guest attribute"));
3301
3302 /* name is optional */
3303 pelmDevice->getAttributeValue("name", hpda.strDeviceName);
3304
3305 hw.pciAttachments.push_back(hpda);
3306 }
3307 }
3308 }
3309 else if (pelmHwChild->nameEquals("EmulatedUSB"))
3310 {
3311 const xml::ElementNode *pelmCardReader;
3312
3313 if ((pelmCardReader = pelmHwChild->findChildElement("CardReader")))
3314 {
3315 pelmCardReader->getAttributeValue("enabled", hw.fEmulatedUSBCardReader);
3316 }
3317 }
3318 else if (pelmHwChild->nameEquals("Frontend"))
3319 {
3320 const xml::ElementNode *pelmDefault;
3321
3322 if ((pelmDefault = pelmHwChild->findChildElement("Default")))
3323 {
3324 pelmDefault->getAttributeValue("type", hw.strDefaultFrontend);
3325 }
3326 }
3327 }
3328
3329 if (hw.ulMemorySizeMB == (uint32_t)-1)
3330 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
3331}
3332
3333/**
3334 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
3335 * files which have a <HardDiskAttachments> node and storage controller settings
3336 * hidden in the <Hardware> settings. We set the StorageControllers fields just the
3337 * same, just from different sources.
3338 * @param elmHardware <Hardware> XML node.
3339 * @param elmHardDiskAttachments <HardDiskAttachments> XML node.
3340 * @param strg
3341 */
3342void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
3343 Storage &strg)
3344{
3345 StorageController *pIDEController = NULL;
3346 StorageController *pSATAController = NULL;
3347
3348 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
3349 it != strg.llStorageControllers.end();
3350 ++it)
3351 {
3352 StorageController &s = *it;
3353 if (s.storageBus == StorageBus_IDE)
3354 pIDEController = &s;
3355 else if (s.storageBus == StorageBus_SATA)
3356 pSATAController = &s;
3357 }
3358
3359 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
3360 const xml::ElementNode *pelmAttachment;
3361 while ((pelmAttachment = nl1.forAllNodes()))
3362 {
3363 AttachedDevice att;
3364 Utf8Str strUUID, strBus;
3365
3366 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
3367 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
3368 parseUUID(att.uuid, strUUID);
3369
3370 if (!pelmAttachment->getAttributeValue("bus", strBus))
3371 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
3372 // pre-1.7 'channel' is now port
3373 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
3374 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
3375 // pre-1.7 'device' is still device
3376 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
3377 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
3378
3379 att.deviceType = DeviceType_HardDisk;
3380
3381 if (strBus == "IDE")
3382 {
3383 if (!pIDEController)
3384 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
3385 pIDEController->llAttachedDevices.push_back(att);
3386 }
3387 else if (strBus == "SATA")
3388 {
3389 if (!pSATAController)
3390 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
3391 pSATAController->llAttachedDevices.push_back(att);
3392 }
3393 else
3394 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
3395 }
3396}
3397
3398/**
3399 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
3400 * Used both directly from readMachine and from readSnapshot, since snapshots
3401 * have their own storage controllers sections.
3402 *
3403 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
3404 * for earlier versions.
3405 *
3406 * @param elmStorageControllers
3407 */
3408void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
3409 Storage &strg)
3410{
3411 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
3412 const xml::ElementNode *pelmController;
3413 while ((pelmController = nlStorageControllers.forAllNodes()))
3414 {
3415 StorageController sctl;
3416
3417 if (!pelmController->getAttributeValue("name", sctl.strName))
3418 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
3419 // canonicalize storage controller names for configs in the switchover
3420 // period.
3421 if (m->sv < SettingsVersion_v1_9)
3422 {
3423 if (sctl.strName == "IDE")
3424 sctl.strName = "IDE Controller";
3425 else if (sctl.strName == "SATA")
3426 sctl.strName = "SATA Controller";
3427 else if (sctl.strName == "SCSI")
3428 sctl.strName = "SCSI Controller";
3429 }
3430
3431 pelmController->getAttributeValue("Instance", sctl.ulInstance);
3432 // default from constructor is 0
3433
3434 pelmController->getAttributeValue("Bootable", sctl.fBootable);
3435 // default from constructor is true which is true
3436 // for settings below version 1.11 because they allowed only
3437 // one controller per type.
3438
3439 Utf8Str strType;
3440 if (!pelmController->getAttributeValue("type", strType))
3441 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
3442
3443 if (strType == "AHCI")
3444 {
3445 sctl.storageBus = StorageBus_SATA;
3446 sctl.controllerType = StorageControllerType_IntelAhci;
3447 }
3448 else if (strType == "LsiLogic")
3449 {
3450 sctl.storageBus = StorageBus_SCSI;
3451 sctl.controllerType = StorageControllerType_LsiLogic;
3452 }
3453 else if (strType == "BusLogic")
3454 {
3455 sctl.storageBus = StorageBus_SCSI;
3456 sctl.controllerType = StorageControllerType_BusLogic;
3457 }
3458 else if (strType == "PIIX3")
3459 {
3460 sctl.storageBus = StorageBus_IDE;
3461 sctl.controllerType = StorageControllerType_PIIX3;
3462 }
3463 else if (strType == "PIIX4")
3464 {
3465 sctl.storageBus = StorageBus_IDE;
3466 sctl.controllerType = StorageControllerType_PIIX4;
3467 }
3468 else if (strType == "ICH6")
3469 {
3470 sctl.storageBus = StorageBus_IDE;
3471 sctl.controllerType = StorageControllerType_ICH6;
3472 }
3473 else if ( (m->sv >= SettingsVersion_v1_9)
3474 && (strType == "I82078")
3475 )
3476 {
3477 sctl.storageBus = StorageBus_Floppy;
3478 sctl.controllerType = StorageControllerType_I82078;
3479 }
3480 else if (strType == "LsiLogicSas")
3481 {
3482 sctl.storageBus = StorageBus_SAS;
3483 sctl.controllerType = StorageControllerType_LsiLogicSas;
3484 }
3485 else if (strType == "USB")
3486 {
3487 sctl.storageBus = StorageBus_USB;
3488 sctl.controllerType = StorageControllerType_USB;
3489 }
3490 else
3491 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
3492
3493 readStorageControllerAttributes(*pelmController, sctl);
3494
3495 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
3496 const xml::ElementNode *pelmAttached;
3497 while ((pelmAttached = nlAttached.forAllNodes()))
3498 {
3499 AttachedDevice att;
3500 Utf8Str strTemp;
3501 pelmAttached->getAttributeValue("type", strTemp);
3502
3503 att.fDiscard = false;
3504 att.fNonRotational = false;
3505 att.fHotPluggable = false;
3506
3507 if (strTemp == "HardDisk")
3508 {
3509 att.deviceType = DeviceType_HardDisk;
3510 pelmAttached->getAttributeValue("nonrotational", att.fNonRotational);
3511 pelmAttached->getAttributeValue("discard", att.fDiscard);
3512 }
3513 else if (m->sv >= SettingsVersion_v1_9)
3514 {
3515 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
3516 if (strTemp == "DVD")
3517 {
3518 att.deviceType = DeviceType_DVD;
3519 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
3520 pelmAttached->getAttributeValue("tempeject", att.fTempEject);
3521 }
3522 else if (strTemp == "Floppy")
3523 att.deviceType = DeviceType_Floppy;
3524 }
3525
3526 if (att.deviceType != DeviceType_Null)
3527 {
3528 const xml::ElementNode *pelmImage;
3529 // all types can have images attached, but for HardDisk it's required
3530 if (!(pelmImage = pelmAttached->findChildElement("Image")))
3531 {
3532 if (att.deviceType == DeviceType_HardDisk)
3533 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
3534 else
3535 {
3536 // DVDs and floppies can also have <HostDrive> instead of <Image>
3537 const xml::ElementNode *pelmHostDrive;
3538 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
3539 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
3540 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
3541 }
3542 }
3543 else
3544 {
3545 if (!pelmImage->getAttributeValue("uuid", strTemp))
3546 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
3547 parseUUID(att.uuid, strTemp);
3548 }
3549
3550 if (!pelmAttached->getAttributeValue("port", att.lPort))
3551 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
3552 if (!pelmAttached->getAttributeValue("device", att.lDevice))
3553 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
3554
3555 /* AHCI controller ports are hotpluggable by default, keep compatibility with existing settings. */
3556 if (m->sv >= SettingsVersion_v1_15)
3557 pelmAttached->getAttributeValue("hotpluggable", att.fHotPluggable);
3558 else if (sctl.controllerType == StorageControllerType_IntelAhci)
3559 att.fHotPluggable = true;
3560
3561 pelmAttached->getAttributeValue("bandwidthGroup", att.strBwGroup);
3562 sctl.llAttachedDevices.push_back(att);
3563 }
3564 }
3565
3566 strg.llStorageControllers.push_back(sctl);
3567 }
3568}
3569
3570/**
3571 * This gets called for legacy pre-1.9 settings files after having parsed the
3572 * <Hardware> and <StorageControllers> sections to parse <Hardware> once more
3573 * for the <DVDDrive> and <FloppyDrive> sections.
3574 *
3575 * Before settings version 1.9, DVD and floppy drives were specified separately
3576 * under <Hardware>; we then need this extra loop to make sure the storage
3577 * controller structs are already set up so we can add stuff to them.
3578 *
3579 * @param elmHardware
3580 * @param strg
3581 */
3582void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
3583 Storage &strg)
3584{
3585 xml::NodesLoop nl1(elmHardware);
3586 const xml::ElementNode *pelmHwChild;
3587 while ((pelmHwChild = nl1.forAllNodes()))
3588 {
3589 if (pelmHwChild->nameEquals("DVDDrive"))
3590 {
3591 // create a DVD "attached device" and attach it to the existing IDE controller
3592 AttachedDevice att;
3593 att.deviceType = DeviceType_DVD;
3594 // legacy DVD drive is always secondary master (port 1, device 0)
3595 att.lPort = 1;
3596 att.lDevice = 0;
3597 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
3598 pelmHwChild->getAttributeValue("tempeject", att.fTempEject);
3599
3600 const xml::ElementNode *pDriveChild;
3601 Utf8Str strTmp;
3602 if ( (pDriveChild = pelmHwChild->findChildElement("Image")) != NULL
3603 && pDriveChild->getAttributeValue("uuid", strTmp))
3604 parseUUID(att.uuid, strTmp);
3605 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
3606 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
3607
3608 // find the IDE controller and attach the DVD drive
3609 bool fFound = false;
3610 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
3611 it != strg.llStorageControllers.end();
3612 ++it)
3613 {
3614 StorageController &sctl = *it;
3615 if (sctl.storageBus == StorageBus_IDE)
3616 {
3617 sctl.llAttachedDevices.push_back(att);
3618 fFound = true;
3619 break;
3620 }
3621 }
3622
3623 if (!fFound)
3624 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
3625 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
3626 // which should have gotten parsed in <StorageControllers> before this got called
3627 }
3628 else if (pelmHwChild->nameEquals("FloppyDrive"))
3629 {
3630 bool fEnabled;
3631 if ( pelmHwChild->getAttributeValue("enabled", fEnabled)
3632 && fEnabled)
3633 {
3634 // create a new floppy controller and attach a floppy "attached device"
3635 StorageController sctl;
3636 sctl.strName = "Floppy Controller";
3637 sctl.storageBus = StorageBus_Floppy;
3638 sctl.controllerType = StorageControllerType_I82078;
3639 sctl.ulPortCount = 1;
3640
3641 AttachedDevice att;
3642 att.deviceType = DeviceType_Floppy;
3643 att.lPort = 0;
3644 att.lDevice = 0;
3645
3646 const xml::ElementNode *pDriveChild;
3647 Utf8Str strTmp;
3648 if ( (pDriveChild = pelmHwChild->findChildElement("Image"))
3649 && pDriveChild->getAttributeValue("uuid", strTmp) )
3650 parseUUID(att.uuid, strTmp);
3651 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
3652 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
3653
3654 // store attachment with controller
3655 sctl.llAttachedDevices.push_back(att);
3656 // store controller with storage
3657 strg.llStorageControllers.push_back(sctl);
3658 }
3659 }
3660 }
3661}
3662
3663/**
3664 * Called for reading the <Teleporter> element under <Machine>.
3665 */
3666void MachineConfigFile::readTeleporter(const xml::ElementNode *pElmTeleporter,
3667 MachineUserData *pUserData)
3668{
3669 pElmTeleporter->getAttributeValue("enabled", pUserData->fTeleporterEnabled);
3670 pElmTeleporter->getAttributeValue("port", pUserData->uTeleporterPort);
3671 pElmTeleporter->getAttributeValue("address", pUserData->strTeleporterAddress);
3672 pElmTeleporter->getAttributeValue("password", pUserData->strTeleporterPassword);
3673
3674 if ( pUserData->strTeleporterPassword.isNotEmpty()
3675 && !VBoxIsPasswordHashed(&pUserData->strTeleporterPassword))
3676 VBoxHashPassword(&pUserData->strTeleporterPassword);
3677}
3678
3679/**
3680 * Called for reading the <Debugging> element under <Machine> or <Snapshot>.
3681 */
3682void MachineConfigFile::readDebugging(const xml::ElementNode *pElmDebugging, Debugging *pDbg)
3683{
3684 if (!pElmDebugging || m->sv < SettingsVersion_v1_13)
3685 return;
3686
3687 const xml::ElementNode * const pelmTracing = pElmDebugging->findChildElement("Tracing");
3688 if (pelmTracing)
3689 {
3690 pelmTracing->getAttributeValue("enabled", pDbg->fTracingEnabled);
3691 pelmTracing->getAttributeValue("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
3692 pelmTracing->getAttributeValue("config", pDbg->strTracingConfig);
3693 }
3694}
3695
3696/**
3697 * Called for reading the <Autostart> element under <Machine> or <Snapshot>.
3698 */
3699void MachineConfigFile::readAutostart(const xml::ElementNode *pElmAutostart, Autostart *pAutostart)
3700{
3701 Utf8Str strAutostop;
3702
3703 if (!pElmAutostart || m->sv < SettingsVersion_v1_13)
3704 return;
3705
3706 pElmAutostart->getAttributeValue("enabled", pAutostart->fAutostartEnabled);
3707 pElmAutostart->getAttributeValue("delay", pAutostart->uAutostartDelay);
3708 pElmAutostart->getAttributeValue("autostop", strAutostop);
3709 if (strAutostop == "Disabled")
3710 pAutostart->enmAutostopType = AutostopType_Disabled;
3711 else if (strAutostop == "SaveState")
3712 pAutostart->enmAutostopType = AutostopType_SaveState;
3713 else if (strAutostop == "PowerOff")
3714 pAutostart->enmAutostopType = AutostopType_PowerOff;
3715 else if (strAutostop == "AcpiShutdown")
3716 pAutostart->enmAutostopType = AutostopType_AcpiShutdown;
3717 else
3718 throw ConfigFileError(this, pElmAutostart, N_("Invalid value '%s' for Autostart/@autostop attribute"), strAutostop.c_str());
3719}
3720
3721/**
3722 * Called for reading the <Groups> element under <Machine>.
3723 */
3724void MachineConfigFile::readGroups(const xml::ElementNode *pElmGroups, StringsList *pllGroups)
3725{
3726 pllGroups->clear();
3727 if (!pElmGroups || m->sv < SettingsVersion_v1_13)
3728 {
3729 pllGroups->push_back("/");
3730 return;
3731 }
3732
3733 xml::NodesLoop nlGroups(*pElmGroups);
3734 const xml::ElementNode *pelmGroup;
3735 while ((pelmGroup = nlGroups.forAllNodes()))
3736 {
3737 if (pelmGroup->nameEquals("Group"))
3738 {
3739 Utf8Str strGroup;
3740 if (!pelmGroup->getAttributeValue("name", strGroup))
3741 throw ConfigFileError(this, pelmGroup, N_("Required Group/@name attribute is missing"));
3742 pllGroups->push_back(strGroup);
3743 }
3744 }
3745}
3746
3747/**
3748 * Called initially for the <Snapshot> element under <Machine>, if present,
3749 * to store the snapshot's data into the given Snapshot structure (which is
3750 * then the one in the Machine struct). This might then recurse if
3751 * a <Snapshots> (plural) element is found in the snapshot, which should
3752 * contain a list of child snapshots; such lists are maintained in the
3753 * Snapshot structure.
3754 *
3755 * @param curSnapshotUuid
3756 * @param depth
3757 * @param elmSnapshot
3758 * @param snap
3759 * @returns true if curSnapshotUuid is in this snapshot subtree, otherwise false
3760 */
3761bool MachineConfigFile::readSnapshot(const Guid &curSnapshotUuid,
3762 uint32_t depth,
3763 const xml::ElementNode &elmSnapshot,
3764 Snapshot &snap)
3765{
3766 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
3767 throw ConfigFileError(this, &elmSnapshot, N_("Maximum snapshot tree depth of %u exceeded"), depth);
3768
3769 Utf8Str strTemp;
3770
3771 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
3772 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
3773 parseUUID(snap.uuid, strTemp);
3774 bool foundCurrentSnapshot = (snap.uuid == curSnapshotUuid);
3775
3776 if (!elmSnapshot.getAttributeValue("name", snap.strName))
3777 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
3778
3779 // earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
3780 elmSnapshot.getAttributeValue("Description", snap.strDescription);
3781
3782 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
3783 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
3784 parseTimestamp(snap.timestamp, strTemp);
3785
3786 elmSnapshot.getAttributeValuePath("stateFile", snap.strStateFile); // online snapshots only
3787
3788 // parse Hardware before the other elements because other things depend on it
3789 const xml::ElementNode *pelmHardware;
3790 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
3791 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
3792 readHardware(*pelmHardware, snap.hardware, snap.storage);
3793
3794 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
3795 const xml::ElementNode *pelmSnapshotChild;
3796 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
3797 {
3798 if (pelmSnapshotChild->nameEquals("Description"))
3799 snap.strDescription = pelmSnapshotChild->getValue();
3800 else if ( m->sv < SettingsVersion_v1_7
3801 && pelmSnapshotChild->nameEquals("HardDiskAttachments"))
3802 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.storage);
3803 else if ( m->sv >= SettingsVersion_v1_7
3804 && pelmSnapshotChild->nameEquals("StorageControllers"))
3805 readStorageControllers(*pelmSnapshotChild, snap.storage);
3806 else if (pelmSnapshotChild->nameEquals("Snapshots"))
3807 {
3808 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
3809 const xml::ElementNode *pelmChildSnapshot;
3810 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
3811 {
3812 if (pelmChildSnapshot->nameEquals("Snapshot"))
3813 {
3814 // Use the heap to reduce the stack footprint. Each
3815 // recursion needs over 1K, and there can be VMs with
3816 // deeply nested snapshots. The stack can be quite
3817 // small, especially with XPCOM.
3818 Snapshot *child = new Snapshot();
3819 bool found = readSnapshot(curSnapshotUuid, depth + 1, *pelmChildSnapshot, *child);
3820 foundCurrentSnapshot = foundCurrentSnapshot || found;
3821 snap.llChildSnapshots.push_back(*child);
3822 delete child;
3823 }
3824 }
3825 }
3826 }
3827
3828 if (m->sv < SettingsVersion_v1_9)
3829 // go through Hardware once more to repair the settings controller structures
3830 // with data from old DVDDrive and FloppyDrive elements
3831 readDVDAndFloppies_pre1_9(*pelmHardware, snap.storage);
3832
3833 readDebugging(elmSnapshot.findChildElement("Debugging"), &snap.debugging);
3834 readAutostart(elmSnapshot.findChildElement("Autostart"), &snap.autostart);
3835 // note: Groups exist only for Machine, not for Snapshot
3836
3837 return foundCurrentSnapshot;
3838}
3839
3840const struct {
3841 const char *pcszOld;
3842 const char *pcszNew;
3843} aConvertOSTypes[] =
3844{
3845 { "unknown", "Other" },
3846 { "dos", "DOS" },
3847 { "win31", "Windows31" },
3848 { "win95", "Windows95" },
3849 { "win98", "Windows98" },
3850 { "winme", "WindowsMe" },
3851 { "winnt4", "WindowsNT4" },
3852 { "win2k", "Windows2000" },
3853 { "winxp", "WindowsXP" },
3854 { "win2k3", "Windows2003" },
3855 { "winvista", "WindowsVista" },
3856 { "win2k8", "Windows2008" },
3857 { "os2warp3", "OS2Warp3" },
3858 { "os2warp4", "OS2Warp4" },
3859 { "os2warp45", "OS2Warp45" },
3860 { "ecs", "OS2eCS" },
3861 { "linux22", "Linux22" },
3862 { "linux24", "Linux24" },
3863 { "linux26", "Linux26" },
3864 { "archlinux", "ArchLinux" },
3865 { "debian", "Debian" },
3866 { "opensuse", "OpenSUSE" },
3867 { "fedoracore", "Fedora" },
3868 { "gentoo", "Gentoo" },
3869 { "mandriva", "Mandriva" },
3870 { "redhat", "RedHat" },
3871 { "ubuntu", "Ubuntu" },
3872 { "xandros", "Xandros" },
3873 { "freebsd", "FreeBSD" },
3874 { "openbsd", "OpenBSD" },
3875 { "netbsd", "NetBSD" },
3876 { "netware", "Netware" },
3877 { "solaris", "Solaris" },
3878 { "opensolaris", "OpenSolaris" },
3879 { "l4", "L4" }
3880};
3881
3882void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
3883{
3884 for (unsigned u = 0;
3885 u < RT_ELEMENTS(aConvertOSTypes);
3886 ++u)
3887 {
3888 if (str == aConvertOSTypes[u].pcszOld)
3889 {
3890 str = aConvertOSTypes[u].pcszNew;
3891 break;
3892 }
3893 }
3894}
3895
3896/**
3897 * Called from the constructor to actually read in the <Machine> element
3898 * of a machine config file.
3899 * @param elmMachine
3900 */
3901void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
3902{
3903 Utf8Str strUUID;
3904 if ( elmMachine.getAttributeValue("uuid", strUUID)
3905 && elmMachine.getAttributeValue("name", machineUserData.strName))
3906 {
3907 parseUUID(uuid, strUUID);
3908
3909 elmMachine.getAttributeValue("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
3910 elmMachine.getAttributeValue("nameSync", machineUserData.fNameSync);
3911
3912 Utf8Str str;
3913 elmMachine.getAttributeValue("Description", machineUserData.strDescription);
3914 elmMachine.getAttributeValue("OSType", machineUserData.strOsType);
3915 if (m->sv < SettingsVersion_v1_5)
3916 convertOldOSType_pre1_5(machineUserData.strOsType);
3917
3918 elmMachine.getAttributeValuePath("stateFile", strStateFile);
3919
3920 if (elmMachine.getAttributeValue("currentSnapshot", str))
3921 parseUUID(uuidCurrentSnapshot, str);
3922
3923 elmMachine.getAttributeValuePath("snapshotFolder", machineUserData.strSnapshotFolder);
3924
3925 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
3926 fCurrentStateModified = true;
3927 if (elmMachine.getAttributeValue("lastStateChange", str))
3928 parseTimestamp(timeLastStateChange, str);
3929 // constructor has called RTTimeNow(&timeLastStateChange) before
3930 if (elmMachine.getAttributeValue("aborted", fAborted))
3931 fAborted = true;
3932
3933 elmMachine.getAttributeValue("icon", machineUserData.ovIcon);
3934
3935 // parse Hardware before the other elements because other things depend on it
3936 const xml::ElementNode *pelmHardware;
3937 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
3938 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
3939 readHardware(*pelmHardware, hardwareMachine, storageMachine);
3940
3941 xml::NodesLoop nlRootChildren(elmMachine);
3942 const xml::ElementNode *pelmMachineChild;
3943 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
3944 {
3945 if (pelmMachineChild->nameEquals("ExtraData"))
3946 readExtraData(*pelmMachineChild,
3947 mapExtraDataItems);
3948 else if ( (m->sv < SettingsVersion_v1_7)
3949 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
3950 )
3951 readHardDiskAttachments_pre1_7(*pelmMachineChild, storageMachine);
3952 else if ( (m->sv >= SettingsVersion_v1_7)
3953 && (pelmMachineChild->nameEquals("StorageControllers"))
3954 )
3955 readStorageControllers(*pelmMachineChild, storageMachine);
3956 else if (pelmMachineChild->nameEquals("Snapshot"))
3957 {
3958 if (uuidCurrentSnapshot.isZero())
3959 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but required Machine/@currentSnapshot attribute is missing"));
3960 bool foundCurrentSnapshot = false;
3961 Snapshot snap;
3962 // this will recurse into child snapshots, if necessary
3963 foundCurrentSnapshot = readSnapshot(uuidCurrentSnapshot, 1, *pelmMachineChild, snap);
3964 if (!foundCurrentSnapshot)
3965 throw ConfigFileError(this, &elmMachine, N_("Snapshots present but none matches the UUID in the Machine/@currentSnapshot attribute"));
3966 llFirstSnapshot.push_back(snap);
3967 }
3968 else if (pelmMachineChild->nameEquals("Description"))
3969 machineUserData.strDescription = pelmMachineChild->getValue();
3970 else if (pelmMachineChild->nameEquals("Teleporter"))
3971 readTeleporter(pelmMachineChild, &machineUserData);
3972 else if (pelmMachineChild->nameEquals("FaultTolerance"))
3973 {
3974 Utf8Str strFaultToleranceSate;
3975 if (pelmMachineChild->getAttributeValue("state", strFaultToleranceSate))
3976 {
3977 if (strFaultToleranceSate == "master")
3978 machineUserData.enmFaultToleranceState = FaultToleranceState_Master;
3979 else
3980 if (strFaultToleranceSate == "standby")
3981 machineUserData.enmFaultToleranceState = FaultToleranceState_Standby;
3982 else
3983 machineUserData.enmFaultToleranceState = FaultToleranceState_Inactive;
3984 }
3985 pelmMachineChild->getAttributeValue("port", machineUserData.uFaultTolerancePort);
3986 pelmMachineChild->getAttributeValue("address", machineUserData.strFaultToleranceAddress);
3987 pelmMachineChild->getAttributeValue("interval", machineUserData.uFaultToleranceInterval);
3988 pelmMachineChild->getAttributeValue("password", machineUserData.strFaultTolerancePassword);
3989 }
3990 else if (pelmMachineChild->nameEquals("MediaRegistry"))
3991 readMediaRegistry(*pelmMachineChild, mediaRegistry);
3992 else if (pelmMachineChild->nameEquals("Debugging"))
3993 readDebugging(pelmMachineChild, &debugging);
3994 else if (pelmMachineChild->nameEquals("Autostart"))
3995 readAutostart(pelmMachineChild, &autostart);
3996 else if (pelmMachineChild->nameEquals("Groups"))
3997 readGroups(pelmMachineChild, &machineUserData.llGroups);
3998 }
3999
4000 if (m->sv < SettingsVersion_v1_9)
4001 // go through Hardware once more to repair the settings controller structures
4002 // with data from old DVDDrive and FloppyDrive elements
4003 readDVDAndFloppies_pre1_9(*pelmHardware, storageMachine);
4004 }
4005 else
4006 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
4007}
4008
4009/**
4010 * Creates a <Hardware> node under elmParent and then writes out the XML
4011 * keys under that. Called for both the <Machine> node and for snapshots.
4012 * @param elmParent
4013 * @param st
4014 */
4015void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
4016 const Hardware &hw,
4017 const Storage &strg)
4018{
4019 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
4020
4021 if (m->sv >= SettingsVersion_v1_4)
4022 pelmHardware->setAttribute("version", hw.strVersion);
4023
4024 if ((m->sv >= SettingsVersion_v1_9)
4025 && !hw.uuid.isZero()
4026 && hw.uuid.isValid()
4027 )
4028 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
4029
4030 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
4031
4032 xml::ElementNode *pelmHwVirtEx = pelmCPU->createChild("HardwareVirtEx");
4033 pelmHwVirtEx->setAttribute("enabled", hw.fHardwareVirt);
4034
4035 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
4036 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
4037 pelmCPU->createChild("HardwareVirtExUX")->setAttribute("enabled", hw.fUnrestrictedExecution);
4038 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
4039 if (m->sv >= SettingsVersion_v1_14 && hw.enmLongMode != Hardware::LongMode_Legacy)
4040 pelmCPU->createChild("LongMode")->setAttribute("enabled", hw.enmLongMode == Hardware::LongMode_Enabled);
4041
4042 if (hw.fSyntheticCpu)
4043 pelmCPU->createChild("SyntheticCpu")->setAttribute("enabled", hw.fSyntheticCpu);
4044 if (hw.fTripleFaultReset)
4045 pelmCPU->createChild("TripleFaultReset")->setAttribute("enabled", hw.fTripleFaultReset);
4046 pelmCPU->setAttribute("count", hw.cCPUs);
4047 if (hw.ulCpuExecutionCap != 100)
4048 pelmCPU->setAttribute("executionCap", hw.ulCpuExecutionCap);
4049
4050 /* Always save this setting as we have changed the default in 4.0 (on for large memory 64-bit systems). */
4051 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
4052
4053 if (m->sv >= SettingsVersion_v1_9)
4054 pelmCPU->createChild("HardwareVirtForce")->setAttribute("enabled", hw.fHardwareVirtForce);
4055
4056 if (m->sv >= SettingsVersion_v1_10)
4057 {
4058 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
4059
4060 xml::ElementNode *pelmCpuTree = NULL;
4061 for (CpuList::const_iterator it = hw.llCpus.begin();
4062 it != hw.llCpus.end();
4063 ++it)
4064 {
4065 const Cpu &cpu = *it;
4066
4067 if (pelmCpuTree == NULL)
4068 pelmCpuTree = pelmCPU->createChild("CpuTree");
4069
4070 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
4071 pelmCpu->setAttribute("id", cpu.ulId);
4072 }
4073 }
4074
4075 xml::ElementNode *pelmCpuIdTree = NULL;
4076 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
4077 it != hw.llCpuIdLeafs.end();
4078 ++it)
4079 {
4080 const CpuIdLeaf &leaf = *it;
4081
4082 if (pelmCpuIdTree == NULL)
4083 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
4084
4085 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
4086 pelmCpuIdLeaf->setAttribute("id", leaf.ulId);
4087 pelmCpuIdLeaf->setAttribute("eax", leaf.ulEax);
4088 pelmCpuIdLeaf->setAttribute("ebx", leaf.ulEbx);
4089 pelmCpuIdLeaf->setAttribute("ecx", leaf.ulEcx);
4090 pelmCpuIdLeaf->setAttribute("edx", leaf.ulEdx);
4091 }
4092
4093 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
4094 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
4095 if (m->sv >= SettingsVersion_v1_10)
4096 {
4097 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
4098 }
4099
4100 if ( (m->sv >= SettingsVersion_v1_9)
4101 && (hw.firmwareType >= FirmwareType_EFI)
4102 )
4103 {
4104 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
4105 const char *pcszFirmware;
4106
4107 switch (hw.firmwareType)
4108 {
4109 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
4110 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
4111 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
4112 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
4113 default: pcszFirmware = "None"; break;
4114 }
4115 pelmFirmware->setAttribute("type", pcszFirmware);
4116 }
4117
4118 if ( (m->sv >= SettingsVersion_v1_10)
4119 )
4120 {
4121 xml::ElementNode *pelmHID = pelmHardware->createChild("HID");
4122 const char *pcszHID;
4123
4124 switch (hw.pointingHIDType)
4125 {
4126 case PointingHIDType_USBMouse: pcszHID = "USBMouse"; break;
4127 case PointingHIDType_USBTablet: pcszHID = "USBTablet"; break;
4128 case PointingHIDType_PS2Mouse: pcszHID = "PS2Mouse"; break;
4129 case PointingHIDType_ComboMouse: pcszHID = "ComboMouse"; break;
4130 case PointingHIDType_USBMultiTouch: pcszHID = "USBMultiTouch";break;
4131 case PointingHIDType_None: pcszHID = "None"; break;
4132 default: Assert(false); pcszHID = "PS2Mouse"; break;
4133 }
4134 pelmHID->setAttribute("Pointing", pcszHID);
4135
4136 switch (hw.keyboardHIDType)
4137 {
4138 case KeyboardHIDType_USBKeyboard: pcszHID = "USBKeyboard"; break;
4139 case KeyboardHIDType_PS2Keyboard: pcszHID = "PS2Keyboard"; break;
4140 case KeyboardHIDType_ComboKeyboard: pcszHID = "ComboKeyboard"; break;
4141 case KeyboardHIDType_None: pcszHID = "None"; break;
4142 default: Assert(false); pcszHID = "PS2Keyboard"; break;
4143 }
4144 pelmHID->setAttribute("Keyboard", pcszHID);
4145 }
4146
4147 if ( (m->sv >= SettingsVersion_v1_10)
4148 )
4149 {
4150 xml::ElementNode *pelmHPET = pelmHardware->createChild("HPET");
4151 pelmHPET->setAttribute("enabled", hw.fHPETEnabled);
4152 }
4153
4154 if ( (m->sv >= SettingsVersion_v1_11)
4155 )
4156 {
4157 xml::ElementNode *pelmChipset = pelmHardware->createChild("Chipset");
4158 const char *pcszChipset;
4159
4160 switch (hw.chipsetType)
4161 {
4162 case ChipsetType_PIIX3: pcszChipset = "PIIX3"; break;
4163 case ChipsetType_ICH9: pcszChipset = "ICH9"; break;
4164 default: Assert(false); pcszChipset = "PIIX3"; break;
4165 }
4166 pelmChipset->setAttribute("type", pcszChipset);
4167 }
4168
4169 if ( (m->sv >= SettingsVersion_v1_15)
4170 && !hw.areParavirtDefaultSettings()
4171 )
4172 {
4173 const char *pcszParavirtProvider;
4174 switch (hw.paravirtProvider)
4175 {
4176 case ParavirtProvider_None: pcszParavirtProvider = "None"; break;
4177 case ParavirtProvider_Default: pcszParavirtProvider = "Default"; break;
4178 case ParavirtProvider_Legacy: pcszParavirtProvider = "Legacy"; break;
4179 case ParavirtProvider_Minimal: pcszParavirtProvider = "Minimal"; break;
4180 case ParavirtProvider_HyperV: pcszParavirtProvider = "HyperV"; break;
4181 default: Assert(false); pcszParavirtProvider = "None"; break;
4182 }
4183
4184 xml::ElementNode *pelmParavirt = pelmHardware->createChild("Paravirt");
4185 pelmParavirt->setAttribute("provider", pcszParavirtProvider);
4186 }
4187
4188 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
4189 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
4190 it != hw.mapBootOrder.end();
4191 ++it)
4192 {
4193 uint32_t i = it->first;
4194 DeviceType_T type = it->second;
4195 const char *pcszDevice;
4196
4197 switch (type)
4198 {
4199 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
4200 case DeviceType_DVD: pcszDevice = "DVD"; break;
4201 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
4202 case DeviceType_Network: pcszDevice = "Network"; break;
4203 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
4204 }
4205
4206 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
4207 pelmOrder->setAttribute("position",
4208 i + 1); // XML is 1-based but internal data is 0-based
4209 pelmOrder->setAttribute("device", pcszDevice);
4210 }
4211
4212 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
4213 if (hw.graphicsControllerType != GraphicsControllerType_VBoxVGA)
4214 {
4215 const char *pcszGraphics;
4216 switch (hw.graphicsControllerType)
4217 {
4218 case GraphicsControllerType_VBoxVGA: pcszGraphics = "VBoxVGA"; break;
4219 case GraphicsControllerType_VMSVGA: pcszGraphics = "VMSVGA"; break;
4220 default: /*case GraphicsControllerType_Null:*/ pcszGraphics = "None"; break;
4221 }
4222 pelmDisplay->setAttribute("controller", pcszGraphics);
4223 }
4224 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
4225 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
4226 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
4227
4228 if (m->sv >= SettingsVersion_v1_8)
4229 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
4230 xml::ElementNode *pelmVideoCapture = pelmHardware->createChild("VideoCapture");
4231
4232 if (m->sv >= SettingsVersion_v1_14)
4233 {
4234 pelmVideoCapture->setAttribute("enabled", hw.fVideoCaptureEnabled);
4235 pelmVideoCapture->setAttribute("screens", hw.u64VideoCaptureScreens);
4236 if (!hw.strVideoCaptureFile.isEmpty())
4237 pelmVideoCapture->setAttributePath("file", hw.strVideoCaptureFile);
4238 pelmVideoCapture->setAttribute("horzRes", hw.ulVideoCaptureHorzRes);
4239 pelmVideoCapture->setAttribute("vertRes", hw.ulVideoCaptureVertRes);
4240 pelmVideoCapture->setAttribute("rate", hw.ulVideoCaptureRate);
4241 pelmVideoCapture->setAttribute("fps", hw.ulVideoCaptureFPS);
4242 pelmVideoCapture->setAttribute("maxTime", hw.ulVideoCaptureMaxTime);
4243 pelmVideoCapture->setAttribute("maxSize", hw.ulVideoCaptureMaxSize);
4244 }
4245
4246 xml::ElementNode *pelmVRDE = pelmHardware->createChild("RemoteDisplay");
4247 pelmVRDE->setAttribute("enabled", hw.vrdeSettings.fEnabled);
4248 if (m->sv < SettingsVersion_v1_11)
4249 {
4250 /* In VBox 4.0 these attributes are replaced with "Properties". */
4251 Utf8Str strPort;
4252 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("TCP/Ports");
4253 if (it != hw.vrdeSettings.mapProperties.end())
4254 strPort = it->second;
4255 if (!strPort.length())
4256 strPort = "3389";
4257 pelmVRDE->setAttribute("port", strPort);
4258
4259 Utf8Str strAddress;
4260 it = hw.vrdeSettings.mapProperties.find("TCP/Address");
4261 if (it != hw.vrdeSettings.mapProperties.end())
4262 strAddress = it->second;
4263 if (strAddress.length())
4264 pelmVRDE->setAttribute("netAddress", strAddress);
4265 }
4266 const char *pcszAuthType;
4267 switch (hw.vrdeSettings.authType)
4268 {
4269 case AuthType_Guest: pcszAuthType = "Guest"; break;
4270 case AuthType_External: pcszAuthType = "External"; break;
4271 default: /*case AuthType_Null:*/ pcszAuthType = "Null"; break;
4272 }
4273 pelmVRDE->setAttribute("authType", pcszAuthType);
4274
4275 if (hw.vrdeSettings.ulAuthTimeout != 0)
4276 pelmVRDE->setAttribute("authTimeout", hw.vrdeSettings.ulAuthTimeout);
4277 if (hw.vrdeSettings.fAllowMultiConnection)
4278 pelmVRDE->setAttribute("allowMultiConnection", hw.vrdeSettings.fAllowMultiConnection);
4279 if (hw.vrdeSettings.fReuseSingleConnection)
4280 pelmVRDE->setAttribute("reuseSingleConnection", hw.vrdeSettings.fReuseSingleConnection);
4281
4282 if (m->sv == SettingsVersion_v1_10)
4283 {
4284 xml::ElementNode *pelmVideoChannel = pelmVRDE->createChild("VideoChannel");
4285
4286 /* In 4.0 videochannel settings were replaced with properties, so look at properties. */
4287 Utf8Str str;
4288 StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
4289 if (it != hw.vrdeSettings.mapProperties.end())
4290 str = it->second;
4291 bool fVideoChannel = RTStrICmp(str.c_str(), "true") == 0
4292 || RTStrCmp(str.c_str(), "1") == 0;
4293 pelmVideoChannel->setAttribute("enabled", fVideoChannel);
4294
4295 it = hw.vrdeSettings.mapProperties.find("VideoChannel/Quality");
4296 if (it != hw.vrdeSettings.mapProperties.end())
4297 str = it->second;
4298 uint32_t ulVideoChannelQuality = RTStrToUInt32(str.c_str()); /* This returns 0 on invalid string which is ok. */
4299 if (ulVideoChannelQuality == 0)
4300 ulVideoChannelQuality = 75;
4301 else
4302 ulVideoChannelQuality = RT_CLAMP(ulVideoChannelQuality, 10, 100);
4303 pelmVideoChannel->setAttribute("quality", ulVideoChannelQuality);
4304 }
4305 if (m->sv >= SettingsVersion_v1_11)
4306 {
4307 if (hw.vrdeSettings.strAuthLibrary.length())
4308 pelmVRDE->setAttribute("authLibrary", hw.vrdeSettings.strAuthLibrary);
4309 if (hw.vrdeSettings.strVrdeExtPack.isNotEmpty())
4310 pelmVRDE->setAttribute("VRDEExtPack", hw.vrdeSettings.strVrdeExtPack);
4311 if (hw.vrdeSettings.mapProperties.size() > 0)
4312 {
4313 xml::ElementNode *pelmProperties = pelmVRDE->createChild("VRDEProperties");
4314 for (StringsMap::const_iterator it = hw.vrdeSettings.mapProperties.begin();
4315 it != hw.vrdeSettings.mapProperties.end();
4316 ++it)
4317 {
4318 const Utf8Str &strName = it->first;
4319 const Utf8Str &strValue = it->second;
4320 xml::ElementNode *pelm = pelmProperties->createChild("Property");
4321 pelm->setAttribute("name", strName);
4322 pelm->setAttribute("value", strValue);
4323 }
4324 }
4325 }
4326
4327 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
4328 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
4329 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
4330
4331 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
4332 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
4333 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
4334 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
4335 if (hw.biosSettings.strLogoImagePath.length())
4336 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
4337
4338 const char *pcszBootMenu;
4339 switch (hw.biosSettings.biosBootMenuMode)
4340 {
4341 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
4342 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
4343 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
4344 }
4345 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
4346 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
4347 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
4348
4349 if (m->sv < SettingsVersion_v1_9)
4350 {
4351 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
4352 // run thru the storage controllers to see if we have a DVD or floppy drives
4353 size_t cDVDs = 0;
4354 size_t cFloppies = 0;
4355
4356 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
4357 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
4358
4359 for (StorageControllersList::const_iterator it = strg.llStorageControllers.begin();
4360 it != strg.llStorageControllers.end();
4361 ++it)
4362 {
4363 const StorageController &sctl = *it;
4364 // in old settings format, the DVD drive could only have been under the IDE controller
4365 if (sctl.storageBus == StorageBus_IDE)
4366 {
4367 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
4368 it2 != sctl.llAttachedDevices.end();
4369 ++it2)
4370 {
4371 const AttachedDevice &att = *it2;
4372 if (att.deviceType == DeviceType_DVD)
4373 {
4374 if (cDVDs > 0)
4375 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
4376
4377 ++cDVDs;
4378
4379 pelmDVD->setAttribute("passthrough", att.fPassThrough);
4380 if (att.fTempEject)
4381 pelmDVD->setAttribute("tempeject", att.fTempEject);
4382
4383 if (!att.uuid.isZero() && att.uuid.isValid())
4384 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
4385 else if (att.strHostDriveSrc.length())
4386 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
4387 }
4388 }
4389 }
4390 else if (sctl.storageBus == StorageBus_Floppy)
4391 {
4392 size_t cFloppiesHere = sctl.llAttachedDevices.size();
4393 if (cFloppiesHere > 1)
4394 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
4395 if (cFloppiesHere)
4396 {
4397 const AttachedDevice &att = sctl.llAttachedDevices.front();
4398 pelmFloppy->setAttribute("enabled", true);
4399
4400 if (!att.uuid.isZero() && att.uuid.isValid())
4401 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
4402 else if (att.strHostDriveSrc.length())
4403 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
4404 }
4405
4406 cFloppies += cFloppiesHere;
4407 }
4408 }
4409
4410 if (cFloppies == 0)
4411 pelmFloppy->setAttribute("enabled", false);
4412 else if (cFloppies > 1)
4413 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
4414 }
4415
4416 if (m->sv < SettingsVersion_v1_14)
4417 {
4418 bool fOhciEnabled = false;
4419 bool fEhciEnabled = false;
4420 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
4421
4422 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
4423 it != hardwareMachine.usbSettings.llUSBControllers.end();
4424 ++it)
4425 {
4426 const USBController &ctrl = *it;
4427
4428 switch (ctrl.enmType)
4429 {
4430 case USBControllerType_OHCI:
4431 fOhciEnabled = true;
4432 break;
4433 case USBControllerType_EHCI:
4434 fEhciEnabled = true;
4435 break;
4436 default:
4437 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
4438 }
4439 }
4440
4441 pelmUSB->setAttribute("enabled", fOhciEnabled);
4442 pelmUSB->setAttribute("enabledEhci", fEhciEnabled);
4443
4444 buildUSBDeviceFilters(*pelmUSB, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
4445 }
4446 else
4447 {
4448 xml::ElementNode *pelmUSB = pelmHardware->createChild("USB");
4449 xml::ElementNode *pelmCtrls = pelmUSB->createChild("Controllers");
4450
4451 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
4452 it != hardwareMachine.usbSettings.llUSBControllers.end();
4453 ++it)
4454 {
4455 const USBController &ctrl = *it;
4456 com::Utf8Str strType;
4457 xml::ElementNode *pelmCtrl = pelmCtrls->createChild("Controller");
4458
4459 switch (ctrl.enmType)
4460 {
4461 case USBControllerType_OHCI:
4462 strType = "OHCI";
4463 break;
4464 case USBControllerType_EHCI:
4465 strType = "EHCI";
4466 break;
4467 case USBControllerType_XHCI:
4468 strType = "XHCI";
4469 break;
4470 default:
4471 AssertMsgFailed(("Unknown USB controller type %d\n", ctrl.enmType));
4472 }
4473
4474 pelmCtrl->setAttribute("name", ctrl.strName);
4475 pelmCtrl->setAttribute("type", strType);
4476 }
4477
4478 xml::ElementNode *pelmFilters = pelmUSB->createChild("DeviceFilters");
4479 buildUSBDeviceFilters(*pelmFilters, hw.usbSettings.llDeviceFilters, false /* fHostMode */);
4480 }
4481
4482 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
4483 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
4484 it != hw.llNetworkAdapters.end();
4485 ++it)
4486 {
4487 const NetworkAdapter &nic = *it;
4488
4489 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
4490 pelmAdapter->setAttribute("slot", nic.ulSlot);
4491 pelmAdapter->setAttribute("enabled", nic.fEnabled);
4492 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
4493 pelmAdapter->setAttribute("cable", nic.fCableConnected);
4494 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
4495 if (nic.ulBootPriority != 0)
4496 {
4497 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
4498 }
4499 if (nic.fTraceEnabled)
4500 {
4501 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
4502 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
4503 }
4504 if (nic.strBandwidthGroup.isNotEmpty())
4505 pelmAdapter->setAttribute("bandwidthGroup", nic.strBandwidthGroup);
4506
4507 const char *pszPolicy;
4508 switch (nic.enmPromiscModePolicy)
4509 {
4510 case NetworkAdapterPromiscModePolicy_Deny: pszPolicy = NULL; break;
4511 case NetworkAdapterPromiscModePolicy_AllowNetwork: pszPolicy = "AllowNetwork"; break;
4512 case NetworkAdapterPromiscModePolicy_AllowAll: pszPolicy = "AllowAll"; break;
4513 default: pszPolicy = NULL; AssertFailed(); break;
4514 }
4515 if (pszPolicy)
4516 pelmAdapter->setAttribute("promiscuousModePolicy", pszPolicy);
4517
4518 const char *pcszType;
4519 switch (nic.type)
4520 {
4521 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
4522 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
4523 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
4524 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
4525 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
4526 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
4527 }
4528 pelmAdapter->setAttribute("type", pcszType);
4529
4530 xml::ElementNode *pelmNAT;
4531 if (m->sv < SettingsVersion_v1_10)
4532 {
4533 switch (nic.mode)
4534 {
4535 case NetworkAttachmentType_NAT:
4536 pelmNAT = pelmAdapter->createChild("NAT");
4537 if (nic.nat.strNetwork.length())
4538 pelmNAT->setAttribute("network", nic.nat.strNetwork);
4539 break;
4540
4541 case NetworkAttachmentType_Bridged:
4542 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
4543 break;
4544
4545 case NetworkAttachmentType_Internal:
4546 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
4547 break;
4548
4549 case NetworkAttachmentType_HostOnly:
4550 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
4551 break;
4552
4553 default: /*case NetworkAttachmentType_Null:*/
4554 break;
4555 }
4556 }
4557 else
4558 {
4559 /* m->sv >= SettingsVersion_v1_10 */
4560 xml::ElementNode *pelmDisabledNode = NULL;
4561 pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
4562 if (nic.mode != NetworkAttachmentType_NAT)
4563 buildNetworkXML(NetworkAttachmentType_NAT, *pelmDisabledNode, false, nic);
4564 if (nic.mode != NetworkAttachmentType_Bridged)
4565 buildNetworkXML(NetworkAttachmentType_Bridged, *pelmDisabledNode, false, nic);
4566 if (nic.mode != NetworkAttachmentType_Internal)
4567 buildNetworkXML(NetworkAttachmentType_Internal, *pelmDisabledNode, false, nic);
4568 if (nic.mode != NetworkAttachmentType_HostOnly)
4569 buildNetworkXML(NetworkAttachmentType_HostOnly, *pelmDisabledNode, false, nic);
4570 if (nic.mode != NetworkAttachmentType_Generic)
4571 buildNetworkXML(NetworkAttachmentType_Generic, *pelmDisabledNode, false, nic);
4572 if (nic.mode != NetworkAttachmentType_NATNetwork)
4573 buildNetworkXML(NetworkAttachmentType_NATNetwork, *pelmDisabledNode, false, nic);
4574 buildNetworkXML(nic.mode, *pelmAdapter, true, nic);
4575 }
4576 }
4577
4578 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
4579 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
4580 it != hw.llSerialPorts.end();
4581 ++it)
4582 {
4583 const SerialPort &port = *it;
4584 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
4585 pelmPort->setAttribute("slot", port.ulSlot);
4586 pelmPort->setAttribute("enabled", port.fEnabled);
4587 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
4588 pelmPort->setAttribute("IRQ", port.ulIRQ);
4589
4590 const char *pcszHostMode;
4591 switch (port.portMode)
4592 {
4593 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
4594 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
4595 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
4596 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
4597 }
4598 switch (port.portMode)
4599 {
4600 case PortMode_HostPipe:
4601 pelmPort->setAttribute("server", port.fServer);
4602 /* no break */
4603 case PortMode_HostDevice:
4604 case PortMode_RawFile:
4605 pelmPort->setAttribute("path", port.strPath);
4606 break;
4607
4608 default:
4609 break;
4610 }
4611 pelmPort->setAttribute("hostMode", pcszHostMode);
4612 }
4613
4614 pelmPorts = pelmHardware->createChild("LPT");
4615 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
4616 it != hw.llParallelPorts.end();
4617 ++it)
4618 {
4619 const ParallelPort &port = *it;
4620 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
4621 pelmPort->setAttribute("slot", port.ulSlot);
4622 pelmPort->setAttribute("enabled", port.fEnabled);
4623 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
4624 pelmPort->setAttribute("IRQ", port.ulIRQ);
4625 if (port.strPath.length())
4626 pelmPort->setAttribute("path", port.strPath);
4627 }
4628
4629 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
4630 const char *pcszController;
4631 switch (hw.audioAdapter.controllerType)
4632 {
4633 case AudioControllerType_SB16:
4634 pcszController = "SB16";
4635 break;
4636 case AudioControllerType_HDA:
4637 if (m->sv >= SettingsVersion_v1_11)
4638 {
4639 pcszController = "HDA";
4640 break;
4641 }
4642 /* fall through */
4643 case AudioControllerType_AC97:
4644 default:
4645 pcszController = "AC97";
4646 break;
4647 }
4648 pelmAudio->setAttribute("controller", pcszController);
4649
4650 if (m->sv >= SettingsVersion_v1_10)
4651 {
4652 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
4653 pelmRTC->setAttribute("localOrUTC", machineUserData.fRTCUseUTC ? "UTC" : "local");
4654 }
4655
4656 const char *pcszDriver;
4657 switch (hw.audioAdapter.driverType)
4658 {
4659 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
4660 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
4661 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
4662 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
4663 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
4664 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
4665 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
4666 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
4667 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
4668 }
4669 pelmAudio->setAttribute("driver", pcszDriver);
4670
4671 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
4672
4673 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
4674 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
4675 it != hw.llSharedFolders.end();
4676 ++it)
4677 {
4678 const SharedFolder &sf = *it;
4679 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
4680 pelmThis->setAttribute("name", sf.strName);
4681 pelmThis->setAttribute("hostPath", sf.strHostPath);
4682 pelmThis->setAttribute("writable", sf.fWritable);
4683 pelmThis->setAttribute("autoMount", sf.fAutoMount);
4684 }
4685
4686 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
4687 const char *pcszClip;
4688 switch (hw.clipboardMode)
4689 {
4690 default: /*case ClipboardMode_Disabled:*/ pcszClip = "Disabled"; break;
4691 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
4692 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
4693 case ClipboardMode_Bidirectional: pcszClip = "Bidirectional"; break;
4694 }
4695 pelmClip->setAttribute("mode", pcszClip);
4696
4697 xml::ElementNode *pelmDragAndDrop = pelmHardware->createChild("DragAndDrop");
4698 const char *pcszDragAndDrop;
4699 switch (hw.dndMode)
4700 {
4701 default: /*case DnDMode_Disabled:*/ pcszDragAndDrop = "Disabled"; break;
4702 case DnDMode_HostToGuest: pcszDragAndDrop = "HostToGuest"; break;
4703 case DnDMode_GuestToHost: pcszDragAndDrop = "GuestToHost"; break;
4704 case DnDMode_Bidirectional: pcszDragAndDrop = "Bidirectional"; break;
4705 }
4706 pelmDragAndDrop->setAttribute("mode", pcszDragAndDrop);
4707
4708 if (m->sv >= SettingsVersion_v1_10)
4709 {
4710 xml::ElementNode *pelmIO = pelmHardware->createChild("IO");
4711 xml::ElementNode *pelmIOCache;
4712
4713 pelmIOCache = pelmIO->createChild("IoCache");
4714 pelmIOCache->setAttribute("enabled", hw.ioSettings.fIOCacheEnabled);
4715 pelmIOCache->setAttribute("size", hw.ioSettings.ulIOCacheSize);
4716
4717 if (m->sv >= SettingsVersion_v1_11)
4718 {
4719 xml::ElementNode *pelmBandwidthGroups = pelmIO->createChild("BandwidthGroups");
4720 for (BandwidthGroupList::const_iterator it = hw.ioSettings.llBandwidthGroups.begin();
4721 it != hw.ioSettings.llBandwidthGroups.end();
4722 ++it)
4723 {
4724 const BandwidthGroup &gr = *it;
4725 const char *pcszType;
4726 xml::ElementNode *pelmThis = pelmBandwidthGroups->createChild("BandwidthGroup");
4727 pelmThis->setAttribute("name", gr.strName);
4728 switch (gr.enmType)
4729 {
4730 case BandwidthGroupType_Network: pcszType = "Network"; break;
4731 default: /* BandwidthGrouptype_Disk */ pcszType = "Disk"; break;
4732 }
4733 pelmThis->setAttribute("type", pcszType);
4734 if (m->sv >= SettingsVersion_v1_13)
4735 pelmThis->setAttribute("maxBytesPerSec", gr.cMaxBytesPerSec);
4736 else
4737 pelmThis->setAttribute("maxMbPerSec", gr.cMaxBytesPerSec / _1M);
4738 }
4739 }
4740 }
4741
4742 if (m->sv >= SettingsVersion_v1_12)
4743 {
4744 xml::ElementNode *pelmPCI = pelmHardware->createChild("HostPci");
4745 xml::ElementNode *pelmPCIDevices = pelmPCI->createChild("Devices");
4746
4747 for (HostPCIDeviceAttachmentList::const_iterator it = hw.pciAttachments.begin();
4748 it != hw.pciAttachments.end();
4749 ++it)
4750 {
4751 const HostPCIDeviceAttachment &hpda = *it;
4752
4753 xml::ElementNode *pelmThis = pelmPCIDevices->createChild("Device");
4754
4755 pelmThis->setAttribute("host", hpda.uHostAddress);
4756 pelmThis->setAttribute("guest", hpda.uGuestAddress);
4757 pelmThis->setAttribute("name", hpda.strDeviceName);
4758 }
4759 }
4760
4761 if (m->sv >= SettingsVersion_v1_12)
4762 {
4763 xml::ElementNode *pelmEmulatedUSB = pelmHardware->createChild("EmulatedUSB");
4764
4765 xml::ElementNode *pelmCardReader = pelmEmulatedUSB->createChild("CardReader");
4766 pelmCardReader->setAttribute("enabled", hw.fEmulatedUSBCardReader);
4767 }
4768
4769 if ( m->sv >= SettingsVersion_v1_14
4770 && !hw.strDefaultFrontend.isEmpty())
4771 {
4772 xml::ElementNode *pelmFrontend = pelmHardware->createChild("Frontend");
4773 xml::ElementNode *pelmDefault = pelmFrontend->createChild("Default");
4774 pelmDefault->setAttribute("type", hw.strDefaultFrontend);
4775 }
4776
4777 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
4778 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
4779
4780 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
4781 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
4782 it != hw.llGuestProperties.end();
4783 ++it)
4784 {
4785 const GuestProperty &prop = *it;
4786 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
4787 pelmProp->setAttribute("name", prop.strName);
4788 pelmProp->setAttribute("value", prop.strValue);
4789 pelmProp->setAttribute("timestamp", prop.timestamp);
4790 pelmProp->setAttribute("flags", prop.strFlags);
4791 }
4792
4793 if (hw.strNotificationPatterns.length())
4794 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
4795}
4796
4797/**
4798 * Fill a <Network> node. Only relevant for XML version >= v1_10.
4799 * @param mode
4800 * @param elmParent
4801 * @param fEnabled
4802 * @param nic
4803 */
4804void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
4805 xml::ElementNode &elmParent,
4806 bool fEnabled,
4807 const NetworkAdapter &nic)
4808{
4809 switch (mode)
4810 {
4811 case NetworkAttachmentType_NAT:
4812 xml::ElementNode *pelmNAT;
4813 pelmNAT = elmParent.createChild("NAT");
4814
4815 if (nic.nat.strNetwork.length())
4816 pelmNAT->setAttribute("network", nic.nat.strNetwork);
4817 if (nic.nat.strBindIP.length())
4818 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
4819 if (nic.nat.u32Mtu)
4820 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
4821 if (nic.nat.u32SockRcv)
4822 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
4823 if (nic.nat.u32SockSnd)
4824 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
4825 if (nic.nat.u32TcpRcv)
4826 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
4827 if (nic.nat.u32TcpSnd)
4828 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
4829 xml::ElementNode *pelmDNS;
4830 pelmDNS = pelmNAT->createChild("DNS");
4831 pelmDNS->setAttribute("pass-domain", nic.nat.fDNSPassDomain);
4832 pelmDNS->setAttribute("use-proxy", nic.nat.fDNSProxy);
4833 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDNSUseHostResolver);
4834
4835 xml::ElementNode *pelmAlias;
4836 pelmAlias = pelmNAT->createChild("Alias");
4837 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
4838 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
4839 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
4840
4841 if ( nic.nat.strTFTPPrefix.length()
4842 || nic.nat.strTFTPBootFile.length()
4843 || nic.nat.strTFTPNextServer.length())
4844 {
4845 xml::ElementNode *pelmTFTP;
4846 pelmTFTP = pelmNAT->createChild("TFTP");
4847 if (nic.nat.strTFTPPrefix.length())
4848 pelmTFTP->setAttribute("prefix", nic.nat.strTFTPPrefix);
4849 if (nic.nat.strTFTPBootFile.length())
4850 pelmTFTP->setAttribute("boot-file", nic.nat.strTFTPBootFile);
4851 if (nic.nat.strTFTPNextServer.length())
4852 pelmTFTP->setAttribute("next-server", nic.nat.strTFTPNextServer);
4853 }
4854 buildNATForwardRuleList(*pelmNAT, nic.nat.llRules);
4855 break;
4856
4857 case NetworkAttachmentType_Bridged:
4858 if (fEnabled || !nic.strBridgedName.isEmpty())
4859 elmParent.createChild("BridgedInterface")->setAttribute("name", nic.strBridgedName);
4860 break;
4861
4862 case NetworkAttachmentType_Internal:
4863 if (fEnabled || !nic.strInternalNetworkName.isEmpty())
4864 elmParent.createChild("InternalNetwork")->setAttribute("name", nic.strInternalNetworkName);
4865 break;
4866
4867 case NetworkAttachmentType_HostOnly:
4868 if (fEnabled || !nic.strHostOnlyName.isEmpty())
4869 elmParent.createChild("HostOnlyInterface")->setAttribute("name", nic.strHostOnlyName);
4870 break;
4871
4872 case NetworkAttachmentType_Generic:
4873 if (fEnabled || !nic.strGenericDriver.isEmpty() || nic.genericProperties.size())
4874 {
4875 xml::ElementNode *pelmMode = elmParent.createChild("GenericInterface");
4876 pelmMode->setAttribute("driver", nic.strGenericDriver);
4877 for (StringsMap::const_iterator it = nic.genericProperties.begin();
4878 it != nic.genericProperties.end();
4879 ++it)
4880 {
4881 xml::ElementNode *pelmProp = pelmMode->createChild("Property");
4882 pelmProp->setAttribute("name", it->first);
4883 pelmProp->setAttribute("value", it->second);
4884 }
4885 }
4886 break;
4887
4888 case NetworkAttachmentType_NATNetwork:
4889 if (fEnabled || !nic.strNATNetworkName.isEmpty())
4890 elmParent.createChild("NATNetwork")->setAttribute("name", nic.strNATNetworkName);
4891 break;
4892
4893 default: /*case NetworkAttachmentType_Null:*/
4894 break;
4895 }
4896}
4897
4898/**
4899 * Creates a <StorageControllers> node under elmParent and then writes out the XML
4900 * keys under that. Called for both the <Machine> node and for snapshots.
4901 * @param elmParent
4902 * @param st
4903 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
4904 * an empty drive is always written instead. This is for the OVF export case.
4905 * This parameter is ignored unless the settings version is at least v1.9, which
4906 * is always the case when this gets called for OVF export.
4907 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
4908 * pointers to which we will append all elements that we created here that contain
4909 * UUID attributes. This allows the OVF export code to quickly replace the internal
4910 * media UUIDs with the UUIDs of the media that were exported.
4911 */
4912void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
4913 const Storage &st,
4914 bool fSkipRemovableMedia,
4915 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
4916{
4917 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
4918
4919 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
4920 it != st.llStorageControllers.end();
4921 ++it)
4922 {
4923 const StorageController &sc = *it;
4924
4925 if ( (m->sv < SettingsVersion_v1_9)
4926 && (sc.controllerType == StorageControllerType_I82078)
4927 )
4928 // floppy controller already got written into <Hardware>/<FloppyController> in buildHardwareXML()
4929 // for pre-1.9 settings
4930 continue;
4931
4932 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
4933 com::Utf8Str name = sc.strName;
4934 if (m->sv < SettingsVersion_v1_8)
4935 {
4936 // pre-1.8 settings use shorter controller names, they are
4937 // expanded when reading the settings
4938 if (name == "IDE Controller")
4939 name = "IDE";
4940 else if (name == "SATA Controller")
4941 name = "SATA";
4942 else if (name == "SCSI Controller")
4943 name = "SCSI";
4944 }
4945 pelmController->setAttribute("name", sc.strName);
4946
4947 const char *pcszType;
4948 switch (sc.controllerType)
4949 {
4950 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
4951 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
4952 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
4953 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
4954 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
4955 case StorageControllerType_I82078: pcszType = "I82078"; break;
4956 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
4957 case StorageControllerType_USB: pcszType = "USB"; break;
4958 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
4959 }
4960 pelmController->setAttribute("type", pcszType);
4961
4962 pelmController->setAttribute("PortCount", sc.ulPortCount);
4963
4964 if (m->sv >= SettingsVersion_v1_9)
4965 if (sc.ulInstance)
4966 pelmController->setAttribute("Instance", sc.ulInstance);
4967
4968 if (m->sv >= SettingsVersion_v1_10)
4969 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
4970
4971 if (m->sv >= SettingsVersion_v1_11)
4972 pelmController->setAttribute("Bootable", sc.fBootable);
4973
4974 if (sc.controllerType == StorageControllerType_IntelAhci)
4975 {
4976 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
4977 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
4978 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
4979 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
4980 }
4981
4982 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
4983 it2 != sc.llAttachedDevices.end();
4984 ++it2)
4985 {
4986 const AttachedDevice &att = *it2;
4987
4988 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
4989 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
4990 // the floppy controller at the top of the loop
4991 if ( att.deviceType == DeviceType_DVD
4992 && m->sv < SettingsVersion_v1_9
4993 )
4994 continue;
4995
4996 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
4997
4998 pcszType = NULL;
4999
5000 switch (att.deviceType)
5001 {
5002 case DeviceType_HardDisk:
5003 pcszType = "HardDisk";
5004 if (att.fNonRotational)
5005 pelmDevice->setAttribute("nonrotational", att.fNonRotational);
5006 if (att.fDiscard)
5007 pelmDevice->setAttribute("discard", att.fDiscard);
5008 break;
5009
5010 case DeviceType_DVD:
5011 pcszType = "DVD";
5012 pelmDevice->setAttribute("passthrough", att.fPassThrough);
5013 if (att.fTempEject)
5014 pelmDevice->setAttribute("tempeject", att.fTempEject);
5015 break;
5016
5017 case DeviceType_Floppy:
5018 pcszType = "Floppy";
5019 break;
5020 }
5021
5022 pelmDevice->setAttribute("type", pcszType);
5023
5024 if (m->sv >= SettingsVersion_v1_15)
5025 pelmDevice->setAttribute("hotpluggable", att.fHotPluggable);
5026
5027 pelmDevice->setAttribute("port", att.lPort);
5028 pelmDevice->setAttribute("device", att.lDevice);
5029
5030 if (att.strBwGroup.length())
5031 pelmDevice->setAttribute("bandwidthGroup", att.strBwGroup);
5032
5033 // attached image, if any
5034 if (!att.uuid.isZero()
5035 && att.uuid.isValid()
5036 && (att.deviceType == DeviceType_HardDisk
5037 || !fSkipRemovableMedia
5038 )
5039 )
5040 {
5041 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
5042 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
5043
5044 // if caller wants a list of UUID elements, give it to them
5045 if (pllElementsWithUuidAttributes)
5046 pllElementsWithUuidAttributes->push_back(pelmImage);
5047 }
5048 else if ( (m->sv >= SettingsVersion_v1_9)
5049 && (att.strHostDriveSrc.length())
5050 )
5051 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
5052 }
5053 }
5054}
5055
5056/**
5057 * Creates a <Debugging> node under elmParent and then writes out the XML
5058 * keys under that. Called for both the <Machine> node and for snapshots.
5059 *
5060 * @param pElmParent Pointer to the parent element.
5061 * @param pDbg Pointer to the debugging settings.
5062 */
5063void MachineConfigFile::buildDebuggingXML(xml::ElementNode *pElmParent, const Debugging *pDbg)
5064{
5065 if (m->sv < SettingsVersion_v1_13 || pDbg->areDefaultSettings())
5066 return;
5067
5068 xml::ElementNode *pElmDebugging = pElmParent->createChild("Debugging");
5069 xml::ElementNode *pElmTracing = pElmDebugging->createChild("Tracing");
5070 pElmTracing->setAttribute("enabled", pDbg->fTracingEnabled);
5071 pElmTracing->setAttribute("allowTracingToAccessVM", pDbg->fAllowTracingToAccessVM);
5072 pElmTracing->setAttribute("config", pDbg->strTracingConfig);
5073}
5074
5075/**
5076 * Creates a <Autostart> node under elmParent and then writes out the XML
5077 * keys under that. Called for both the <Machine> node and for snapshots.
5078 *
5079 * @param pElmParent Pointer to the parent element.
5080 * @param pAutostart Pointer to the autostart settings.
5081 */
5082void MachineConfigFile::buildAutostartXML(xml::ElementNode *pElmParent, const Autostart *pAutostart)
5083{
5084 const char *pcszAutostop = NULL;
5085
5086 if (m->sv < SettingsVersion_v1_13 || pAutostart->areDefaultSettings())
5087 return;
5088
5089 xml::ElementNode *pElmAutostart = pElmParent->createChild("Autostart");
5090 pElmAutostart->setAttribute("enabled", pAutostart->fAutostartEnabled);
5091 pElmAutostart->setAttribute("delay", pAutostart->uAutostartDelay);
5092
5093 switch (pAutostart->enmAutostopType)
5094 {
5095 case AutostopType_Disabled: pcszAutostop = "Disabled"; break;
5096 case AutostopType_SaveState: pcszAutostop = "SaveState"; break;
5097 case AutostopType_PowerOff: pcszAutostop = "PowerOff"; break;
5098 case AutostopType_AcpiShutdown: pcszAutostop = "AcpiShutdown"; break;
5099 default: Assert(false); pcszAutostop = "Disabled"; break;
5100 }
5101 pElmAutostart->setAttribute("autostop", pcszAutostop);
5102}
5103
5104/**
5105 * Creates a <Groups> node under elmParent and then writes out the XML
5106 * keys under that. Called for the <Machine> node only.
5107 *
5108 * @param pElmParent Pointer to the parent element.
5109 * @param pllGroups Pointer to the groups list.
5110 */
5111void MachineConfigFile::buildGroupsXML(xml::ElementNode *pElmParent, const StringsList *pllGroups)
5112{
5113 if ( m->sv < SettingsVersion_v1_13 || pllGroups->size() == 0
5114 || (pllGroups->size() == 1 && pllGroups->front() == "/"))
5115 return;
5116
5117 xml::ElementNode *pElmGroups = pElmParent->createChild("Groups");
5118 for (StringsList::const_iterator it = pllGroups->begin();
5119 it != pllGroups->end();
5120 ++it)
5121 {
5122 const Utf8Str &group = *it;
5123 xml::ElementNode *pElmGroup = pElmGroups->createChild("Group");
5124 pElmGroup->setAttribute("name", group);
5125 }
5126}
5127
5128/**
5129 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
5130 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
5131 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
5132 *
5133 * @param depth
5134 * @param elmParent
5135 * @param snap
5136 */
5137void MachineConfigFile::buildSnapshotXML(uint32_t depth,
5138 xml::ElementNode &elmParent,
5139 const Snapshot &snap)
5140{
5141 if (depth > SETTINGS_SNAPSHOT_DEPTH_MAX)
5142 throw ConfigFileError(this, NULL, N_("Maximum snapshot tree depth of %u exceeded"), SETTINGS_SNAPSHOT_DEPTH_MAX);
5143
5144 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
5145
5146 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
5147 pelmSnapshot->setAttribute("name", snap.strName);
5148 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
5149
5150 if (snap.strStateFile.length())
5151 pelmSnapshot->setAttributePath("stateFile", snap.strStateFile);
5152
5153 if (snap.strDescription.length())
5154 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
5155
5156 buildHardwareXML(*pelmSnapshot, snap.hardware, snap.storage);
5157 buildStorageControllersXML(*pelmSnapshot,
5158 snap.storage,
5159 false /* fSkipRemovableMedia */,
5160 NULL); /* pllElementsWithUuidAttributes */
5161 // we only skip removable media for OVF, but we never get here for OVF
5162 // since snapshots never get written then
5163 buildDebuggingXML(pelmSnapshot, &snap.debugging);
5164 buildAutostartXML(pelmSnapshot, &snap.autostart);
5165 // note: Groups exist only for Machine, not for Snapshot
5166
5167 if (snap.llChildSnapshots.size())
5168 {
5169 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
5170 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
5171 it != snap.llChildSnapshots.end();
5172 ++it)
5173 {
5174 const Snapshot &child = *it;
5175 buildSnapshotXML(depth + 1, *pelmChildren, child);
5176 }
5177 }
5178}
5179
5180/**
5181 * Builds the XML DOM tree for the machine config under the given XML element.
5182 *
5183 * This has been separated out from write() so it can be called from elsewhere,
5184 * such as the OVF code, to build machine XML in an existing XML tree.
5185 *
5186 * As a result, this gets called from two locations:
5187 *
5188 * -- MachineConfigFile::write();
5189 *
5190 * -- Appliance::buildXMLForOneVirtualSystem()
5191 *
5192 * In fl, the following flag bits are recognized:
5193 *
5194 * -- BuildMachineXML_MediaRegistry: If set, the machine's media registry will
5195 * be written, if present. This is not set when called from OVF because OVF
5196 * has its own variant of a media registry. This flag is ignored unless the
5197 * settings version is at least v1.11 (VirtualBox 4.0).
5198 *
5199 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
5200 * of the machine and write out <Snapshot> and possibly more snapshots under
5201 * that, if snapshots are present. Otherwise all snapshots are suppressed
5202 * (when called from OVF).
5203 *
5204 * -- BuildMachineXML_WriteVBoxVersionAttribute: If set, add a settingsVersion
5205 * attribute to the machine tag with the vbox settings version. This is for
5206 * the OVF export case in which we don't have the settings version set in
5207 * the root element.
5208 *
5209 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
5210 * (DVDs, floppies) are silently skipped. This is for the OVF export case
5211 * until we support copying ISO and RAW media as well. This flag is ignored
5212 * unless the settings version is at least v1.9, which is always the case
5213 * when this gets called for OVF export.
5214 *
5215 * -- BuildMachineXML_SuppressSavedState: If set, the Machine/@stateFile
5216 * attribute is never set. This is also for the OVF export case because we
5217 * cannot save states with OVF.
5218 *
5219 * @param elmMachine XML <Machine> element to add attributes and elements to.
5220 * @param fl Flags.
5221 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
5222 * see buildStorageControllersXML() for details.
5223 */
5224void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
5225 uint32_t fl,
5226 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
5227{
5228 if (fl & BuildMachineXML_WriteVBoxVersionAttribute)
5229 // add settings version attribute to machine element
5230 setVersionAttribute(elmMachine);
5231
5232 elmMachine.setAttribute("uuid", uuid.toStringCurly());
5233 elmMachine.setAttribute("name", machineUserData.strName);
5234 if (machineUserData.fDirectoryIncludesUUID)
5235 elmMachine.setAttribute("directoryIncludesUUID", machineUserData.fDirectoryIncludesUUID);
5236 if (!machineUserData.fNameSync)
5237 elmMachine.setAttribute("nameSync", machineUserData.fNameSync);
5238 if (machineUserData.strDescription.length())
5239 elmMachine.createChild("Description")->addContent(machineUserData.strDescription);
5240 elmMachine.setAttribute("OSType", machineUserData.strOsType);
5241 if ( strStateFile.length()
5242 && !(fl & BuildMachineXML_SuppressSavedState)
5243 )
5244 elmMachine.setAttributePath("stateFile", strStateFile);
5245
5246 if ((fl & BuildMachineXML_IncludeSnapshots)
5247 && !uuidCurrentSnapshot.isZero()
5248 && uuidCurrentSnapshot.isValid())
5249 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
5250
5251 if (machineUserData.strSnapshotFolder.length())
5252 elmMachine.setAttributePath("snapshotFolder", machineUserData.strSnapshotFolder);
5253 if (!fCurrentStateModified)
5254 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
5255 elmMachine.setAttribute("lastStateChange", makeString(timeLastStateChange));
5256 if (fAborted)
5257 elmMachine.setAttribute("aborted", fAborted);
5258 // Please keep the icon last so that one doesn't have to check if there
5259 // is anything in the line after this very long attribute in the XML.
5260 if (machineUserData.ovIcon.length())
5261 elmMachine.setAttribute("icon", machineUserData.ovIcon);
5262 if ( m->sv >= SettingsVersion_v1_9
5263 && ( machineUserData.fTeleporterEnabled
5264 || machineUserData.uTeleporterPort
5265 || !machineUserData.strTeleporterAddress.isEmpty()
5266 || !machineUserData.strTeleporterPassword.isEmpty()
5267 )
5268 )
5269 {
5270 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
5271 pelmTeleporter->setAttribute("enabled", machineUserData.fTeleporterEnabled);
5272 pelmTeleporter->setAttribute("port", machineUserData.uTeleporterPort);
5273 pelmTeleporter->setAttribute("address", machineUserData.strTeleporterAddress);
5274 pelmTeleporter->setAttribute("password", machineUserData.strTeleporterPassword);
5275 }
5276
5277 if ( m->sv >= SettingsVersion_v1_11
5278 && ( machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
5279 || machineUserData.uFaultTolerancePort
5280 || machineUserData.uFaultToleranceInterval
5281 || !machineUserData.strFaultToleranceAddress.isEmpty()
5282 )
5283 )
5284 {
5285 xml::ElementNode *pelmFaultTolerance = elmMachine.createChild("FaultTolerance");
5286 switch (machineUserData.enmFaultToleranceState)
5287 {
5288 case FaultToleranceState_Inactive:
5289 pelmFaultTolerance->setAttribute("state", "inactive");
5290 break;
5291 case FaultToleranceState_Master:
5292 pelmFaultTolerance->setAttribute("state", "master");
5293 break;
5294 case FaultToleranceState_Standby:
5295 pelmFaultTolerance->setAttribute("state", "standby");
5296 break;
5297 }
5298
5299 pelmFaultTolerance->setAttribute("port", machineUserData.uFaultTolerancePort);
5300 pelmFaultTolerance->setAttribute("address", machineUserData.strFaultToleranceAddress);
5301 pelmFaultTolerance->setAttribute("interval", machineUserData.uFaultToleranceInterval);
5302 pelmFaultTolerance->setAttribute("password", machineUserData.strFaultTolerancePassword);
5303 }
5304
5305 if ( (fl & BuildMachineXML_MediaRegistry)
5306 && (m->sv >= SettingsVersion_v1_11)
5307 )
5308 buildMediaRegistry(elmMachine, mediaRegistry);
5309
5310 buildExtraData(elmMachine, mapExtraDataItems);
5311
5312 if ( (fl & BuildMachineXML_IncludeSnapshots)
5313 && llFirstSnapshot.size())
5314 buildSnapshotXML(1, elmMachine, llFirstSnapshot.front());
5315
5316 buildHardwareXML(elmMachine, hardwareMachine, storageMachine);
5317 buildStorageControllersXML(elmMachine,
5318 storageMachine,
5319 !!(fl & BuildMachineXML_SkipRemovableMedia),
5320 pllElementsWithUuidAttributes);
5321 buildDebuggingXML(&elmMachine, &debugging);
5322 buildAutostartXML(&elmMachine, &autostart);
5323 buildGroupsXML(&elmMachine, &machineUserData.llGroups);
5324}
5325
5326/**
5327 * Returns true only if the given AudioDriverType is supported on
5328 * the current host platform. For example, this would return false
5329 * for AudioDriverType_DirectSound when compiled on a Linux host.
5330 * @param drv AudioDriverType_* enum to test.
5331 * @return true only if the current host supports that driver.
5332 */
5333/*static*/
5334bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
5335{
5336 switch (drv)
5337 {
5338 case AudioDriverType_Null:
5339#ifdef RT_OS_WINDOWS
5340# ifdef VBOX_WITH_WINMM
5341 case AudioDriverType_WinMM:
5342# endif
5343 case AudioDriverType_DirectSound:
5344#endif /* RT_OS_WINDOWS */
5345#ifdef RT_OS_SOLARIS
5346 case AudioDriverType_SolAudio:
5347#endif
5348#ifdef RT_OS_LINUX
5349# ifdef VBOX_WITH_ALSA
5350 case AudioDriverType_ALSA:
5351# endif
5352# ifdef VBOX_WITH_PULSE
5353 case AudioDriverType_Pulse:
5354# endif
5355#endif /* RT_OS_LINUX */
5356#if defined (RT_OS_LINUX) || defined (RT_OS_FREEBSD) || defined(VBOX_WITH_SOLARIS_OSS)
5357 case AudioDriverType_OSS:
5358#endif
5359#ifdef RT_OS_FREEBSD
5360# ifdef VBOX_WITH_PULSE
5361 case AudioDriverType_Pulse:
5362# endif
5363#endif
5364#ifdef RT_OS_DARWIN
5365 case AudioDriverType_CoreAudio:
5366#endif
5367#ifdef RT_OS_OS2
5368 case AudioDriverType_MMPM:
5369#endif
5370 return true;
5371 }
5372
5373 return false;
5374}
5375
5376/**
5377 * Returns the AudioDriverType_* which should be used by default on this
5378 * host platform. On Linux, this will check at runtime whether PulseAudio
5379 * or ALSA are actually supported on the first call.
5380 * @return
5381 */
5382/*static*/
5383AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
5384{
5385#if defined(RT_OS_WINDOWS)
5386# ifdef VBOX_WITH_WINMM
5387 return AudioDriverType_WinMM;
5388# else /* VBOX_WITH_WINMM */
5389 return AudioDriverType_DirectSound;
5390# endif /* !VBOX_WITH_WINMM */
5391#elif defined(RT_OS_SOLARIS)
5392 return AudioDriverType_SolAudio;
5393#elif defined(RT_OS_LINUX)
5394 // on Linux, we need to check at runtime what's actually supported...
5395 static RTCLockMtx s_mtx;
5396 static AudioDriverType_T s_linuxDriver = -1;
5397 RTCLock lock(s_mtx);
5398 if (s_linuxDriver == (AudioDriverType_T)-1)
5399 {
5400# if defined(VBOX_WITH_PULSE)
5401 /* Check for the pulse library & that the pulse audio daemon is running. */
5402 if (RTProcIsRunningByName("pulseaudio") &&
5403 RTLdrIsLoadable("libpulse.so.0"))
5404 s_linuxDriver = AudioDriverType_Pulse;
5405 else
5406# endif /* VBOX_WITH_PULSE */
5407# if defined(VBOX_WITH_ALSA)
5408 /* Check if we can load the ALSA library */
5409 if (RTLdrIsLoadable("libasound.so.2"))
5410 s_linuxDriver = AudioDriverType_ALSA;
5411 else
5412# endif /* VBOX_WITH_ALSA */
5413 s_linuxDriver = AudioDriverType_OSS;
5414 }
5415 return s_linuxDriver;
5416// end elif defined(RT_OS_LINUX)
5417#elif defined(RT_OS_DARWIN)
5418 return AudioDriverType_CoreAudio;
5419#elif defined(RT_OS_OS2)
5420 return AudioDriverType_MMPM;
5421#elif defined(RT_OS_FREEBSD)
5422 return AudioDriverType_OSS;
5423#else
5424 return AudioDriverType_Null;
5425#endif
5426}
5427
5428/**
5429 * Called from write() before calling ConfigFileBase::createStubDocument().
5430 * This adjusts the settings version in m->sv if incompatible settings require
5431 * a settings bump, whereas otherwise we try to preserve the settings version
5432 * to avoid breaking compatibility with older versions.
5433 *
5434 * We do the checks in here in reverse order: newest first, oldest last, so
5435 * that we avoid unnecessary checks since some of these are expensive.
5436 */
5437void MachineConfigFile::bumpSettingsVersionIfNeeded()
5438{
5439 if (m->sv < SettingsVersion_v1_15)
5440 {
5441 /*
5442 * Check whether a paravirtualization provider other than "Legacy" is used, if so bump the version.
5443 */
5444 if (hardwareMachine.paravirtProvider != ParavirtProvider_Legacy)
5445 m->sv = SettingsVersion_v1_15;
5446 else
5447 {
5448 /*
5449 * Check whether the hotpluggable flag of all storage devices differs
5450 * from the default for old settings.
5451 * AHCI ports are hotpluggable by default every other device is not.
5452 */
5453 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
5454 it != storageMachine.llStorageControllers.end();
5455 ++it)
5456 {
5457 bool fSettingsBumped = false;
5458 const StorageController &sctl = *it;
5459
5460 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
5461 it2 != sctl.llAttachedDevices.end();
5462 ++it2)
5463 {
5464 const AttachedDevice &att = *it2;
5465
5466 if ( ( att.fHotPluggable
5467 && sctl.controllerType != StorageControllerType_IntelAhci)
5468 || ( !att.fHotPluggable
5469 && sctl.controllerType == StorageControllerType_IntelAhci))
5470 {
5471 m->sv = SettingsVersion_v1_15;
5472 fSettingsBumped = true;
5473 break;
5474 }
5475 }
5476
5477 /* Abort early if possible. */
5478 if (fSettingsBumped)
5479 break;
5480 }
5481 }
5482 }
5483
5484 if (m->sv < SettingsVersion_v1_14)
5485 {
5486 // VirtualBox 4.3 adds default frontend setting, graphics controller
5487 // setting, explicit long mode setting, video capturing and NAT networking.
5488 if ( !hardwareMachine.strDefaultFrontend.isEmpty()
5489 || hardwareMachine.graphicsControllerType != GraphicsControllerType_VBoxVGA
5490 || hardwareMachine.enmLongMode != Hardware::LongMode_Legacy
5491 || machineUserData.ovIcon.length() > 0
5492 || hardwareMachine.fVideoCaptureEnabled)
5493 {
5494 m->sv = SettingsVersion_v1_14;
5495 return;
5496 }
5497 NetworkAdaptersList::const_iterator netit;
5498 for (netit = hardwareMachine.llNetworkAdapters.begin();
5499 netit != hardwareMachine.llNetworkAdapters.end();
5500 ++netit)
5501 {
5502 if (netit->mode == NetworkAttachmentType_NATNetwork)
5503 {
5504 m->sv = SettingsVersion_v1_14;
5505 break;
5506 }
5507 }
5508 }
5509
5510 if (m->sv < SettingsVersion_v1_14)
5511 {
5512 unsigned cOhciCtrls = 0;
5513 unsigned cEhciCtrls = 0;
5514 bool fNonStdName = false;
5515
5516 for (USBControllerList::const_iterator it = hardwareMachine.usbSettings.llUSBControllers.begin();
5517 it != hardwareMachine.usbSettings.llUSBControllers.end();
5518 ++it)
5519 {
5520 const USBController &ctrl = *it;
5521
5522 switch (ctrl.enmType)
5523 {
5524 case USBControllerType_OHCI:
5525 cOhciCtrls++;
5526 if (ctrl.strName != "OHCI")
5527 fNonStdName = true;
5528 break;
5529 case USBControllerType_EHCI:
5530 cEhciCtrls++;
5531 if (ctrl.strName != "EHCI")
5532 fNonStdName = true;
5533 break;
5534 default:
5535 /* Anything unknown forces a bump. */
5536 fNonStdName = true;
5537 }
5538
5539 /* Skip checking other controllers if the settings bump is necessary. */
5540 if (cOhciCtrls > 1 || cEhciCtrls > 1 || fNonStdName)
5541 {
5542 m->sv = SettingsVersion_v1_14;
5543 break;
5544 }
5545 }
5546 }
5547
5548 if (m->sv < SettingsVersion_v1_13)
5549 {
5550 // VirtualBox 4.2 adds tracing, autostart, UUID in directory and groups.
5551 if ( !debugging.areDefaultSettings()
5552 || !autostart.areDefaultSettings()
5553 || machineUserData.fDirectoryIncludesUUID
5554 || machineUserData.llGroups.size() > 1
5555 || machineUserData.llGroups.front() != "/")
5556 m->sv = SettingsVersion_v1_13;
5557 }
5558
5559 if (m->sv < SettingsVersion_v1_13)
5560 {
5561 // VirtualBox 4.2 changes the units for bandwidth group limits.
5562 for (BandwidthGroupList::const_iterator it = hardwareMachine.ioSettings.llBandwidthGroups.begin();
5563 it != hardwareMachine.ioSettings.llBandwidthGroups.end();
5564 ++it)
5565 {
5566 const BandwidthGroup &gr = *it;
5567 if (gr.cMaxBytesPerSec % _1M)
5568 {
5569 // Bump version if a limit cannot be expressed in megabytes
5570 m->sv = SettingsVersion_v1_13;
5571 break;
5572 }
5573 }
5574 }
5575
5576 if (m->sv < SettingsVersion_v1_12)
5577 {
5578 // VirtualBox 4.1 adds PCI passthrough and emulated USB Smart Card reader
5579 if ( hardwareMachine.pciAttachments.size()
5580 || hardwareMachine.fEmulatedUSBCardReader)
5581 m->sv = SettingsVersion_v1_12;
5582 }
5583
5584 if (m->sv < SettingsVersion_v1_12)
5585 {
5586 // VirtualBox 4.1 adds a promiscuous mode policy to the network
5587 // adapters and a generic network driver transport.
5588 NetworkAdaptersList::const_iterator netit;
5589 for (netit = hardwareMachine.llNetworkAdapters.begin();
5590 netit != hardwareMachine.llNetworkAdapters.end();
5591 ++netit)
5592 {
5593 if ( netit->enmPromiscModePolicy != NetworkAdapterPromiscModePolicy_Deny
5594 || netit->mode == NetworkAttachmentType_Generic
5595 || !netit->strGenericDriver.isEmpty()
5596 || netit->genericProperties.size()
5597 )
5598 {
5599 m->sv = SettingsVersion_v1_12;
5600 break;
5601 }
5602 }
5603 }
5604
5605 if (m->sv < SettingsVersion_v1_11)
5606 {
5607 // VirtualBox 4.0 adds HD audio, CPU priorities, fault tolerance,
5608 // per-machine media registries, VRDE, JRockitVE, bandwidth groups,
5609 // ICH9 chipset
5610 if ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
5611 || hardwareMachine.ulCpuExecutionCap != 100
5612 || machineUserData.enmFaultToleranceState != FaultToleranceState_Inactive
5613 || machineUserData.uFaultTolerancePort
5614 || machineUserData.uFaultToleranceInterval
5615 || !machineUserData.strFaultToleranceAddress.isEmpty()
5616 || mediaRegistry.llHardDisks.size()
5617 || mediaRegistry.llDvdImages.size()
5618 || mediaRegistry.llFloppyImages.size()
5619 || !hardwareMachine.vrdeSettings.strVrdeExtPack.isEmpty()
5620 || !hardwareMachine.vrdeSettings.strAuthLibrary.isEmpty()
5621 || machineUserData.strOsType == "JRockitVE"
5622 || hardwareMachine.ioSettings.llBandwidthGroups.size()
5623 || hardwareMachine.chipsetType == ChipsetType_ICH9
5624 )
5625 m->sv = SettingsVersion_v1_11;
5626 }
5627
5628 if (m->sv < SettingsVersion_v1_10)
5629 {
5630 /* If the properties contain elements other than "TCP/Ports" and "TCP/Address",
5631 * then increase the version to at least VBox 3.2, which can have video channel properties.
5632 */
5633 unsigned cOldProperties = 0;
5634
5635 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
5636 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5637 cOldProperties++;
5638 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
5639 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5640 cOldProperties++;
5641
5642 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
5643 m->sv = SettingsVersion_v1_10;
5644 }
5645
5646 if (m->sv < SettingsVersion_v1_11)
5647 {
5648 /* If the properties contain elements other than "TCP/Ports", "TCP/Address",
5649 * "VideoChannel/Enabled" and "VideoChannel/Quality" then increase the version to VBox 4.0.
5650 */
5651 unsigned cOldProperties = 0;
5652
5653 StringsMap::const_iterator it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Ports");
5654 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5655 cOldProperties++;
5656 it = hardwareMachine.vrdeSettings.mapProperties.find("TCP/Address");
5657 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5658 cOldProperties++;
5659 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Enabled");
5660 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5661 cOldProperties++;
5662 it = hardwareMachine.vrdeSettings.mapProperties.find("VideoChannel/Quality");
5663 if (it != hardwareMachine.vrdeSettings.mapProperties.end())
5664 cOldProperties++;
5665
5666 if (hardwareMachine.vrdeSettings.mapProperties.size() != cOldProperties)
5667 m->sv = SettingsVersion_v1_11;
5668 }
5669
5670 // settings version 1.9 is required if there is not exactly one DVD
5671 // or more than one floppy drive present or the DVD is not at the secondary
5672 // master; this check is a bit more complicated
5673 //
5674 // settings version 1.10 is required if the host cache should be disabled
5675 //
5676 // settings version 1.11 is required for bandwidth limits and if more than
5677 // one controller of each type is present.
5678 if (m->sv < SettingsVersion_v1_11)
5679 {
5680 // count attached DVDs and floppies (only if < v1.9)
5681 size_t cDVDs = 0;
5682 size_t cFloppies = 0;
5683
5684 // count storage controllers (if < v1.11)
5685 size_t cSata = 0;
5686 size_t cScsiLsi = 0;
5687 size_t cScsiBuslogic = 0;
5688 size_t cSas = 0;
5689 size_t cIde = 0;
5690 size_t cFloppy = 0;
5691
5692 // need to run thru all the storage controllers and attached devices to figure this out
5693 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
5694 it != storageMachine.llStorageControllers.end();
5695 ++it)
5696 {
5697 const StorageController &sctl = *it;
5698
5699 // count storage controllers of each type; 1.11 is required if more than one
5700 // controller of one type is present
5701 switch (sctl.storageBus)
5702 {
5703 case StorageBus_IDE:
5704 cIde++;
5705 break;
5706 case StorageBus_SATA:
5707 cSata++;
5708 break;
5709 case StorageBus_SAS:
5710 cSas++;
5711 break;
5712 case StorageBus_SCSI:
5713 if (sctl.controllerType == StorageControllerType_LsiLogic)
5714 cScsiLsi++;
5715 else
5716 cScsiBuslogic++;
5717 break;
5718 case StorageBus_Floppy:
5719 cFloppy++;
5720 break;
5721 default:
5722 // Do nothing
5723 break;
5724 }
5725
5726 if ( cSata > 1
5727 || cScsiLsi > 1
5728 || cScsiBuslogic > 1
5729 || cSas > 1
5730 || cIde > 1
5731 || cFloppy > 1)
5732 {
5733 m->sv = SettingsVersion_v1_11;
5734 break; // abort the loop -- we will not raise the version further
5735 }
5736
5737 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
5738 it2 != sctl.llAttachedDevices.end();
5739 ++it2)
5740 {
5741 const AttachedDevice &att = *it2;
5742
5743 // Bandwidth limitations are new in VirtualBox 4.0 (1.11)
5744 if (m->sv < SettingsVersion_v1_11)
5745 {
5746 if (att.strBwGroup.length() != 0)
5747 {
5748 m->sv = SettingsVersion_v1_11;
5749 break; // abort the loop -- we will not raise the version further
5750 }
5751 }
5752
5753 // disabling the host IO cache requires settings version 1.10
5754 if ( (m->sv < SettingsVersion_v1_10)
5755 && (!sctl.fUseHostIOCache)
5756 )
5757 m->sv = SettingsVersion_v1_10;
5758
5759 // we can only write the StorageController/@Instance attribute with v1.9
5760 if ( (m->sv < SettingsVersion_v1_9)
5761 && (sctl.ulInstance != 0)
5762 )
5763 m->sv = SettingsVersion_v1_9;
5764
5765 if (m->sv < SettingsVersion_v1_9)
5766 {
5767 if (att.deviceType == DeviceType_DVD)
5768 {
5769 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
5770 || (att.lPort != 1) // DVDs not at secondary master?
5771 || (att.lDevice != 0)
5772 )
5773 m->sv = SettingsVersion_v1_9;
5774
5775 ++cDVDs;
5776 }
5777 else if (att.deviceType == DeviceType_Floppy)
5778 ++cFloppies;
5779 }
5780 }
5781
5782 if (m->sv >= SettingsVersion_v1_11)
5783 break; // abort the loop -- we will not raise the version further
5784 }
5785
5786 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
5787 // so any deviation from that will require settings version 1.9
5788 if ( (m->sv < SettingsVersion_v1_9)
5789 && ( (cDVDs != 1)
5790 || (cFloppies > 1)
5791 )
5792 )
5793 m->sv = SettingsVersion_v1_9;
5794 }
5795
5796 // VirtualBox 3.2: Check for non default I/O settings
5797 if (m->sv < SettingsVersion_v1_10)
5798 {
5799 if ( (hardwareMachine.ioSettings.fIOCacheEnabled != true)
5800 || (hardwareMachine.ioSettings.ulIOCacheSize != 5)
5801 // and page fusion
5802 || (hardwareMachine.fPageFusionEnabled)
5803 // and CPU hotplug, RTC timezone control, HID type and HPET
5804 || machineUserData.fRTCUseUTC
5805 || hardwareMachine.fCpuHotPlug
5806 || hardwareMachine.pointingHIDType != PointingHIDType_PS2Mouse
5807 || hardwareMachine.keyboardHIDType != KeyboardHIDType_PS2Keyboard
5808 || hardwareMachine.fHPETEnabled
5809 )
5810 m->sv = SettingsVersion_v1_10;
5811 }
5812
5813 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main
5814 // VirtualBox 4.0 adds network bandwitdth
5815 if (m->sv < SettingsVersion_v1_11)
5816 {
5817 NetworkAdaptersList::const_iterator netit;
5818 for (netit = hardwareMachine.llNetworkAdapters.begin();
5819 netit != hardwareMachine.llNetworkAdapters.end();
5820 ++netit)
5821 {
5822 if ( (m->sv < SettingsVersion_v1_12)
5823 && (netit->strBandwidthGroup.isNotEmpty())
5824 )
5825 {
5826 /* New in VirtualBox 4.1 */
5827 m->sv = SettingsVersion_v1_12;
5828 break;
5829 }
5830 else if ( (m->sv < SettingsVersion_v1_10)
5831 && (netit->fEnabled)
5832 && (netit->mode == NetworkAttachmentType_NAT)
5833 && ( netit->nat.u32Mtu != 0
5834 || netit->nat.u32SockRcv != 0
5835 || netit->nat.u32SockSnd != 0
5836 || netit->nat.u32TcpRcv != 0
5837 || netit->nat.u32TcpSnd != 0
5838 || !netit->nat.fDNSPassDomain
5839 || netit->nat.fDNSProxy
5840 || netit->nat.fDNSUseHostResolver
5841 || netit->nat.fAliasLog
5842 || netit->nat.fAliasProxyOnly
5843 || netit->nat.fAliasUseSamePorts
5844 || netit->nat.strTFTPPrefix.length()
5845 || netit->nat.strTFTPBootFile.length()
5846 || netit->nat.strTFTPNextServer.length()
5847 || netit->nat.llRules.size()
5848 )
5849 )
5850 {
5851 m->sv = SettingsVersion_v1_10;
5852 // no break because we still might need v1.11 above
5853 }
5854 else if ( (m->sv < SettingsVersion_v1_10)
5855 && (netit->fEnabled)
5856 && (netit->ulBootPriority != 0)
5857 )
5858 {
5859 m->sv = SettingsVersion_v1_10;
5860 // no break because we still might need v1.11 above
5861 }
5862 }
5863 }
5864
5865 // all the following require settings version 1.9
5866 if ( (m->sv < SettingsVersion_v1_9)
5867 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
5868 || machineUserData.fTeleporterEnabled
5869 || machineUserData.uTeleporterPort
5870 || !machineUserData.strTeleporterAddress.isEmpty()
5871 || !machineUserData.strTeleporterPassword.isEmpty()
5872 || (!hardwareMachine.uuid.isZero() && hardwareMachine.uuid.isValid())
5873 )
5874 )
5875 m->sv = SettingsVersion_v1_9;
5876
5877 // "accelerate 2d video" requires settings version 1.8
5878 if ( (m->sv < SettingsVersion_v1_8)
5879 && (hardwareMachine.fAccelerate2DVideo)
5880 )
5881 m->sv = SettingsVersion_v1_8;
5882
5883 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
5884 if ( m->sv < SettingsVersion_v1_4
5885 && hardwareMachine.strVersion != "1"
5886 )
5887 m->sv = SettingsVersion_v1_4;
5888}
5889
5890/**
5891 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
5892 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
5893 * in particular if the file cannot be written.
5894 */
5895void MachineConfigFile::write(const com::Utf8Str &strFilename)
5896{
5897 try
5898 {
5899 // createStubDocument() sets the settings version to at least 1.7; however,
5900 // we might need to enfore a later settings version if incompatible settings
5901 // are present:
5902 bumpSettingsVersionIfNeeded();
5903
5904 m->strFilename = strFilename;
5905 createStubDocument();
5906
5907 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
5908 buildMachineXML(*pelmMachine,
5909 MachineConfigFile::BuildMachineXML_IncludeSnapshots
5910 | MachineConfigFile::BuildMachineXML_MediaRegistry,
5911 // but not BuildMachineXML_WriteVBoxVersionAttribute
5912 NULL); /* pllElementsWithUuidAttributes */
5913
5914 // now go write the XML
5915 xml::XmlFileWriter writer(*m->pDoc);
5916 writer.write(m->strFilename.c_str(), true /*fSafe*/);
5917
5918 m->fFileExists = true;
5919 clearDocument();
5920 }
5921 catch (...)
5922 {
5923 clearDocument();
5924 throw;
5925 }
5926}
Note: See TracBrowser for help on using the repository browser.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette