VirtualBox

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

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

Default large page support to on for new VMs or VMs that didn't specify this setting before. (only relevant for nested paging)

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