VirtualBox

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

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

More resource control changes

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