VirtualBox

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

Last change on this file since 37200 was 37200, checked in by vboxsync, 14 years ago

API+Frontends: Generic network attachment driver support which obsoletes the special case for VDE. Big API cleanup in the same area. Adapt all frontends to these changes (full implementation in VBoxManage, minimum implementation in GUI).

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