VirtualBox

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

最後變更 在這個檔案從49029是 49028,由 vboxsync 提交於 11 年 前

iprt/cpp/xml: Fixed attribute lookup with namespace by doing the same way as for elements. Also renamed the two methods with namespace prefix and name in the 'logical' order so they can safely be changed to the order dictated C++ default parameter value handling (name first, then optionally ns-prefix), like the rest.

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

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