VirtualBox

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

Last change on this file since 55043 was 54948, checked in by vboxsync, 10 years ago

Main/Medium+Snapshot: make all code recursing over trees (objects containing lists of child objects) use as little stack as possible, and establish safe depth limits to avoid crashes, plus related cleanups in related code areas

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

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