VirtualBox

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

Last change on this file since 28179 was 28165, checked in by vboxsync, 15 years ago

Main/OVF: import vbox:machine with disks, the rest. Todo some bugfixing.

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

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