VirtualBox

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

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

Initial API changes for resource control (storage/network/cpu)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 174.4 KB
Line 
1/* $Id: Settings.cpp 31287 2010-08-02 12:13:00Z 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 )
1581 );
1582}
1583
1584/**
1585 * Comparison operator. This gets called from MachineConfigFile::operator==,
1586 * which in turn gets called from Machine::saveSettings to figure out whether
1587 * machine settings have really changed and thus need to be written out to disk.
1588 */
1589bool StorageController::operator==(const StorageController &s) const
1590{
1591 return ( (this == &s)
1592 || ( (strName == s.strName)
1593 && (storageBus == s.storageBus)
1594 && (controllerType == s.controllerType)
1595 && (ulPortCount == s.ulPortCount)
1596 && (ulInstance == s.ulInstance)
1597 && (fUseHostIOCache == s.fUseHostIOCache)
1598 && (lIDE0MasterEmulationPort == s.lIDE0MasterEmulationPort)
1599 && (lIDE0SlaveEmulationPort == s.lIDE0SlaveEmulationPort)
1600 && (lIDE1MasterEmulationPort == s.lIDE1MasterEmulationPort)
1601 && (lIDE1SlaveEmulationPort == s.lIDE1SlaveEmulationPort)
1602 && (llAttachedDevices == s.llAttachedDevices)
1603 )
1604 );
1605}
1606
1607/**
1608 * Comparison operator. This gets called from MachineConfigFile::operator==,
1609 * which in turn gets called from Machine::saveSettings to figure out whether
1610 * machine settings have really changed and thus need to be written out to disk.
1611 */
1612bool Storage::operator==(const Storage &s) const
1613{
1614 return ( (this == &s)
1615 || (llStorageControllers == s.llStorageControllers) // deep compare
1616 );
1617}
1618
1619/**
1620 * Comparison operator. This gets called from MachineConfigFile::operator==,
1621 * which in turn gets called from Machine::saveSettings to figure out whether
1622 * machine settings have really changed and thus need to be written out to disk.
1623 */
1624bool Snapshot::operator==(const Snapshot &s) const
1625{
1626 return ( (this == &s)
1627 || ( (uuid == s.uuid)
1628 && (strName == s.strName)
1629 && (strDescription == s.strDescription)
1630 && (RTTimeSpecIsEqual(&timestamp, &s.timestamp))
1631 && (strStateFile == s.strStateFile)
1632 && (hardware == s.hardware) // deep compare
1633 && (storage == s.storage) // deep compare
1634 && (llChildSnapshots == s.llChildSnapshots) // deep compare
1635 )
1636 );
1637}
1638
1639/**
1640 * IoSettings constructor.
1641 */
1642IoSettings::IoSettings()
1643{
1644 fIoCacheEnabled = true;
1645 ulIoCacheSize = 5;
1646}
1647
1648////////////////////////////////////////////////////////////////////////////////
1649//
1650// MachineConfigFile
1651//
1652////////////////////////////////////////////////////////////////////////////////
1653
1654/**
1655 * Constructor.
1656 *
1657 * If pstrFilename is != NULL, this reads the given settings file into the member
1658 * variables and various substructures and lists. Otherwise, the member variables
1659 * are initialized with default values.
1660 *
1661 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1662 * the caller should catch; if this constructor does not throw, then the member
1663 * variables contain meaningful values (either from the file or defaults).
1664 *
1665 * @param strFilename
1666 */
1667MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
1668 : ConfigFileBase(pstrFilename),
1669 fNameSync(true),
1670 fTeleporterEnabled(false),
1671 uTeleporterPort(0),
1672 fRTCUseUTC(false),
1673 fCurrentStateModified(true),
1674 fAborted(false)
1675{
1676 RTTimeNow(&timeLastStateChange);
1677
1678 if (pstrFilename)
1679 {
1680 // the ConfigFileBase constructor has loaded the XML file, so now
1681 // we need only analyze what is in there
1682
1683 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1684 const xml::ElementNode *pelmRootChild;
1685 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1686 {
1687 if (pelmRootChild->nameEquals("Machine"))
1688 readMachine(*pelmRootChild);
1689 }
1690
1691 // clean up memory allocated by XML engine
1692 clearDocument();
1693 }
1694}
1695
1696/**
1697 * Public routine which allows for importing machine XML from an external DOM tree.
1698 * Use this after having called the constructor with a NULL argument.
1699 *
1700 * This is used by the OVF code if a <vbox:Machine> element has been encountered
1701 * in an OVF VirtualSystem element.
1702 *
1703 * @param elmMachine
1704 */
1705void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine)
1706{
1707 readMachine(elmMachine);
1708}
1709
1710/**
1711 * Comparison operator. This gets called from Machine::saveSettings to figure out
1712 * whether machine settings have really changed and thus need to be written out to disk.
1713 *
1714 * Even though this is called operator==, this does NOT compare all fields; the "equals"
1715 * should be understood as "has the same machine config as". The following fields are
1716 * NOT compared:
1717 * -- settings versions and file names inherited from ConfigFileBase;
1718 * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
1719 *
1720 * The "deep" comparisons marked below will invoke the operator== functions of the
1721 * structs defined in this file, which may in turn go into comparing lists of
1722 * other structures. As a result, invoking this can be expensive, but it's
1723 * less expensive than writing out XML to disk.
1724 */
1725bool MachineConfigFile::operator==(const MachineConfigFile &c) const
1726{
1727 return ( (this == &c)
1728 || ( (uuid == c.uuid)
1729 && (strName == c.strName)
1730 && (fNameSync == c.fNameSync)
1731 && (strDescription == c.strDescription)
1732 && (strOsType == c.strOsType)
1733 && (strStateFile == c.strStateFile)
1734 && (uuidCurrentSnapshot == c.uuidCurrentSnapshot)
1735 && (strSnapshotFolder == c.strSnapshotFolder)
1736 && (fTeleporterEnabled == c.fTeleporterEnabled)
1737 && (uTeleporterPort == c.uTeleporterPort)
1738 && (strTeleporterAddress == c.strTeleporterAddress)
1739 && (strTeleporterPassword == c.strTeleporterPassword)
1740 && (fRTCUseUTC == c.fRTCUseUTC)
1741 // skip fCurrentStateModified!
1742 && (RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange))
1743 && (fAborted == c.fAborted)
1744 && (hardwareMachine == c.hardwareMachine) // this one's deep
1745 && (storageMachine == c.storageMachine) // this one's deep
1746 && (mapExtraDataItems == c.mapExtraDataItems) // this one's deep
1747 && (llFirstSnapshot == c.llFirstSnapshot) // this one's deep
1748 )
1749 );
1750}
1751
1752/**
1753 * Called from MachineConfigFile::readHardware() to read cpu information.
1754 * @param elmCpuid
1755 * @param ll
1756 */
1757void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
1758 CpuList &ll)
1759{
1760 xml::NodesLoop nl1(elmCpu, "Cpu");
1761 const xml::ElementNode *pelmCpu;
1762 while ((pelmCpu = nl1.forAllNodes()))
1763 {
1764 Cpu cpu;
1765
1766 if (!pelmCpu->getAttributeValue("id", cpu.ulId))
1767 throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
1768
1769 ll.push_back(cpu);
1770 }
1771}
1772
1773/**
1774 * Called from MachineConfigFile::readHardware() to cpuid information.
1775 * @param elmCpuid
1776 * @param ll
1777 */
1778void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid,
1779 CpuIdLeafsList &ll)
1780{
1781 xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf");
1782 const xml::ElementNode *pelmCpuIdLeaf;
1783 while ((pelmCpuIdLeaf = nl1.forAllNodes()))
1784 {
1785 CpuIdLeaf leaf;
1786
1787 if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.ulId))
1788 throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing"));
1789
1790 pelmCpuIdLeaf->getAttributeValue("eax", leaf.ulEax);
1791 pelmCpuIdLeaf->getAttributeValue("ebx", leaf.ulEbx);
1792 pelmCpuIdLeaf->getAttributeValue("ecx", leaf.ulEcx);
1793 pelmCpuIdLeaf->getAttributeValue("edx", leaf.ulEdx);
1794
1795 ll.push_back(leaf);
1796 }
1797}
1798
1799/**
1800 * Called from MachineConfigFile::readHardware() to network information.
1801 * @param elmNetwork
1802 * @param ll
1803 */
1804void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
1805 NetworkAdaptersList &ll)
1806{
1807 xml::NodesLoop nl1(elmNetwork, "Adapter");
1808 const xml::ElementNode *pelmAdapter;
1809 while ((pelmAdapter = nl1.forAllNodes()))
1810 {
1811 NetworkAdapter nic;
1812
1813 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
1814 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
1815
1816 Utf8Str strTemp;
1817 if (pelmAdapter->getAttributeValue("type", strTemp))
1818 {
1819 if (strTemp == "Am79C970A")
1820 nic.type = NetworkAdapterType_Am79C970A;
1821 else if (strTemp == "Am79C973")
1822 nic.type = NetworkAdapterType_Am79C973;
1823 else if (strTemp == "82540EM")
1824 nic.type = NetworkAdapterType_I82540EM;
1825 else if (strTemp == "82543GC")
1826 nic.type = NetworkAdapterType_I82543GC;
1827 else if (strTemp == "82545EM")
1828 nic.type = NetworkAdapterType_I82545EM;
1829 else if (strTemp == "virtio")
1830 nic.type = NetworkAdapterType_Virtio;
1831 else
1832 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
1833 }
1834
1835 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
1836 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
1837 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
1838 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
1839 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
1840 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
1841 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
1842 pelmAdapter->getAttributeValue("bandwidthLimit", nic.ulBandwidthLimit);
1843
1844 xml::ElementNodesList llNetworkModes;
1845 pelmAdapter->getChildElements(llNetworkModes);
1846 xml::ElementNodesList::iterator it;
1847 /* We should have only active mode descriptor and disabled modes set */
1848 if (llNetworkModes.size() > 2)
1849 {
1850 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
1851 }
1852 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
1853 {
1854 const xml::ElementNode *pelmNode = *it;
1855 if (pelmNode->nameEquals("DisabledModes"))
1856 {
1857 xml::ElementNodesList llDisabledNetworkModes;
1858 xml::ElementNodesList::iterator itDisabled;
1859 pelmNode->getChildElements(llDisabledNetworkModes);
1860 /* run over disabled list and load settings */
1861 for (itDisabled = llDisabledNetworkModes.begin();
1862 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
1863 {
1864 const xml::ElementNode *pelmDisabledNode = *itDisabled;
1865 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
1866 }
1867 }
1868 else
1869 readAttachedNetworkMode(*pelmNode, true, nic);
1870 }
1871 // else: default is NetworkAttachmentType_Null
1872
1873 ll.push_back(nic);
1874 }
1875}
1876
1877void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
1878{
1879 if (elmMode.nameEquals("NAT"))
1880 {
1881 if (fEnabled)
1882 nic.mode = NetworkAttachmentType_NAT;
1883
1884 nic.fHasDisabledNAT = (nic.mode != NetworkAttachmentType_NAT && !fEnabled);
1885 elmMode.getAttributeValue("network", nic.nat.strNetwork); // optional network name
1886 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
1887 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
1888 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
1889 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
1890 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
1891 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
1892 const xml::ElementNode *pelmDNS;
1893 if ((pelmDNS = elmMode.findChildElement("DNS")))
1894 {
1895 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDnsPassDomain);
1896 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDnsProxy);
1897 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDnsUseHostResolver);
1898 }
1899 const xml::ElementNode *pelmAlias;
1900 if ((pelmAlias = elmMode.findChildElement("Alias")))
1901 {
1902 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
1903 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
1904 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
1905 }
1906 const xml::ElementNode *pelmTFTP;
1907 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
1908 {
1909 pelmTFTP->getAttributeValue("prefix", nic.nat.strTftpPrefix);
1910 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTftpBootFile);
1911 pelmTFTP->getAttributeValue("next-server", nic.nat.strTftpNextServer);
1912 }
1913 xml::ElementNodesList plstNatPF;
1914 elmMode.getChildElements(plstNatPF, "Forwarding");
1915 for (xml::ElementNodesList::iterator pf = plstNatPF.begin(); pf != plstNatPF.end(); ++pf)
1916 {
1917 NATRule rule;
1918 uint32_t port = 0;
1919 (*pf)->getAttributeValue("name", rule.strName);
1920 (*pf)->getAttributeValue("proto", rule.u32Proto);
1921 (*pf)->getAttributeValue("hostip", rule.strHostIP);
1922 (*pf)->getAttributeValue("hostport", port);
1923 rule.u16HostPort = port;
1924 (*pf)->getAttributeValue("guestip", rule.strGuestIP);
1925 (*pf)->getAttributeValue("guestport", port);
1926 rule.u16GuestPort = port;
1927 nic.nat.llRules.push_back(rule);
1928 }
1929 }
1930 else if ( fEnabled
1931 && ( (elmMode.nameEquals("HostInterface"))
1932 || (elmMode.nameEquals("BridgedInterface")))
1933 )
1934 {
1935 nic.mode = NetworkAttachmentType_Bridged;
1936 elmMode.getAttributeValue("name", nic.strName); // optional host interface name
1937 }
1938 else if ( fEnabled
1939 && elmMode.nameEquals("InternalNetwork"))
1940 {
1941 nic.mode = NetworkAttachmentType_Internal;
1942 if (!elmMode.getAttributeValue("name", nic.strName)) // required network name
1943 throw ConfigFileError(this, &elmMode, N_("Required InternalNetwork/@name element is missing"));
1944 }
1945 else if ( fEnabled
1946 && elmMode.nameEquals("HostOnlyInterface"))
1947 {
1948 nic.mode = NetworkAttachmentType_HostOnly;
1949 if (!elmMode.getAttributeValue("name", nic.strName)) // required network name
1950 throw ConfigFileError(this, &elmMode, N_("Required HostOnlyInterface/@name element is missing"));
1951 }
1952#if defined(VBOX_WITH_VDE)
1953 else if ( fEnabled
1954 && elmMode.nameEquals("VDE"))
1955 {
1956 nic.mode = NetworkAttachmentType_VDE;
1957 elmMode.getAttributeValue("network", nic.strName); // optional network name
1958 }
1959#endif
1960}
1961
1962/**
1963 * Called from MachineConfigFile::readHardware() to read serial port information.
1964 * @param elmUART
1965 * @param ll
1966 */
1967void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
1968 SerialPortsList &ll)
1969{
1970 xml::NodesLoop nl1(elmUART, "Port");
1971 const xml::ElementNode *pelmPort;
1972 while ((pelmPort = nl1.forAllNodes()))
1973 {
1974 SerialPort port;
1975 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
1976 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
1977
1978 // slot must be unique
1979 for (SerialPortsList::const_iterator it = ll.begin();
1980 it != ll.end();
1981 ++it)
1982 if ((*it).ulSlot == port.ulSlot)
1983 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
1984
1985 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
1986 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
1987 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
1988 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
1989 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
1990 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
1991
1992 Utf8Str strPortMode;
1993 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
1994 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
1995 if (strPortMode == "RawFile")
1996 port.portMode = PortMode_RawFile;
1997 else if (strPortMode == "HostPipe")
1998 port.portMode = PortMode_HostPipe;
1999 else if (strPortMode == "HostDevice")
2000 port.portMode = PortMode_HostDevice;
2001 else if (strPortMode == "Disconnected")
2002 port.portMode = PortMode_Disconnected;
2003 else
2004 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
2005
2006 pelmPort->getAttributeValue("path", port.strPath);
2007 pelmPort->getAttributeValue("server", port.fServer);
2008
2009 ll.push_back(port);
2010 }
2011}
2012
2013/**
2014 * Called from MachineConfigFile::readHardware() to read parallel port information.
2015 * @param elmLPT
2016 * @param ll
2017 */
2018void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
2019 ParallelPortsList &ll)
2020{
2021 xml::NodesLoop nl1(elmLPT, "Port");
2022 const xml::ElementNode *pelmPort;
2023 while ((pelmPort = nl1.forAllNodes()))
2024 {
2025 ParallelPort port;
2026 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
2027 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
2028
2029 // slot must be unique
2030 for (ParallelPortsList::const_iterator it = ll.begin();
2031 it != ll.end();
2032 ++it)
2033 if ((*it).ulSlot == port.ulSlot)
2034 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
2035
2036 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
2037 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
2038 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
2039 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
2040 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
2041 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
2042
2043 pelmPort->getAttributeValue("path", port.strPath);
2044
2045 ll.push_back(port);
2046 }
2047}
2048
2049/**
2050 * Called from MachineConfigFile::readHardware() to read audio adapter information
2051 * and maybe fix driver information depending on the current host hardware.
2052 *
2053 * @param elmAudioAdapter "AudioAdapter" XML element.
2054 * @param hw
2055 */
2056void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
2057 AudioAdapter &aa)
2058{
2059 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
2060
2061 Utf8Str strTemp;
2062 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
2063 {
2064 if (strTemp == "SB16")
2065 aa.controllerType = AudioControllerType_SB16;
2066 else if (strTemp == "AC97")
2067 aa.controllerType = AudioControllerType_AC97;
2068 else if (strTemp == "HDA")
2069 aa.controllerType = AudioControllerType_HDA;
2070 else
2071 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
2072 }
2073
2074 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
2075 {
2076 // settings before 1.3 used lower case so make sure this is case-insensitive
2077 strTemp.toUpper();
2078 if (strTemp == "NULL")
2079 aa.driverType = AudioDriverType_Null;
2080 else if (strTemp == "WINMM")
2081 aa.driverType = AudioDriverType_WinMM;
2082 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
2083 aa.driverType = AudioDriverType_DirectSound;
2084 else if (strTemp == "SOLAUDIO")
2085 aa.driverType = AudioDriverType_SolAudio;
2086 else if (strTemp == "ALSA")
2087 aa.driverType = AudioDriverType_ALSA;
2088 else if (strTemp == "PULSE")
2089 aa.driverType = AudioDriverType_Pulse;
2090 else if (strTemp == "OSS")
2091 aa.driverType = AudioDriverType_OSS;
2092 else if (strTemp == "COREAUDIO")
2093 aa.driverType = AudioDriverType_CoreAudio;
2094 else if (strTemp == "MMPM")
2095 aa.driverType = AudioDriverType_MMPM;
2096 else
2097 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
2098
2099 // now check if this is actually supported on the current host platform;
2100 // people might be opening a file created on a Windows host, and that
2101 // VM should still start on a Linux host
2102 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
2103 aa.driverType = getHostDefaultAudioDriver();
2104 }
2105}
2106
2107/**
2108 * Called from MachineConfigFile::readHardware() to read guest property information.
2109 * @param elmGuestProperties
2110 * @param hw
2111 */
2112void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
2113 Hardware &hw)
2114{
2115 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
2116 const xml::ElementNode *pelmProp;
2117 while ((pelmProp = nl1.forAllNodes()))
2118 {
2119 GuestProperty prop;
2120 pelmProp->getAttributeValue("name", prop.strName);
2121 pelmProp->getAttributeValue("value", prop.strValue);
2122
2123 pelmProp->getAttributeValue("timestamp", prop.timestamp);
2124 pelmProp->getAttributeValue("flags", prop.strFlags);
2125 hw.llGuestProperties.push_back(prop);
2126 }
2127
2128 elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
2129}
2130
2131/**
2132 * Helper function to read attributes that are common to <SATAController> (pre-1.7)
2133 * and <StorageController>.
2134 * @param elmStorageController
2135 * @param strg
2136 */
2137void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
2138 StorageController &sctl)
2139{
2140 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
2141 elmStorageController.getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
2142 elmStorageController.getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
2143 elmStorageController.getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
2144 elmStorageController.getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
2145
2146 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
2147}
2148
2149/**
2150 * Reads in a <Hardware> block and stores it in the given structure. Used
2151 * both directly from readMachine and from readSnapshot, since snapshots
2152 * have their own hardware sections.
2153 *
2154 * For legacy pre-1.7 settings we also need a storage structure because
2155 * the IDE and SATA controllers used to be defined under <Hardware>.
2156 *
2157 * @param elmHardware
2158 * @param hw
2159 */
2160void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
2161 Hardware &hw,
2162 Storage &strg)
2163{
2164 if (!elmHardware.getAttributeValue("version", hw.strVersion))
2165 {
2166 /* KLUDGE ALERT! For a while during the 3.1 development this was not
2167 written because it was thought to have a default value of "2". For
2168 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
2169 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
2170 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
2171 missing the hardware version, then it probably should be "2" instead
2172 of "1". */
2173 if (m->sv < SettingsVersion_v1_7)
2174 hw.strVersion = "1";
2175 else
2176 hw.strVersion = "2";
2177 }
2178 Utf8Str strUUID;
2179 if (elmHardware.getAttributeValue("uuid", strUUID))
2180 parseUUID(hw.uuid, strUUID);
2181
2182 xml::NodesLoop nl1(elmHardware);
2183 const xml::ElementNode *pelmHwChild;
2184 while ((pelmHwChild = nl1.forAllNodes()))
2185 {
2186 if (pelmHwChild->nameEquals("CPU"))
2187 {
2188 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
2189 {
2190 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
2191 const xml::ElementNode *pelmCPUChild;
2192 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
2193 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
2194 }
2195
2196 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
2197 pelmHwChild->getAttributeValue("priority", hw.ulCpuPriority);
2198
2199 const xml::ElementNode *pelmCPUChild;
2200 if (hw.fCpuHotPlug)
2201 {
2202 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
2203 readCpuTree(*pelmCPUChild, hw.llCpus);
2204 }
2205
2206 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
2207 {
2208 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
2209 pelmCPUChild->getAttributeValue("exclusive", hw.fHardwareVirtExclusive); // settings version 1.9
2210 }
2211 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
2212 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
2213 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
2214 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
2215 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
2216 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
2217
2218 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
2219 {
2220 /* The default for pre 3.1 was false, so we must respect that. */
2221 if (m->sv < SettingsVersion_v1_9)
2222 hw.fPAE = false;
2223 }
2224 else
2225 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
2226
2227 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
2228 pelmCPUChild->getAttributeValue("enabled", hw.fSyntheticCpu);
2229 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
2230 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
2231 }
2232 else if (pelmHwChild->nameEquals("Memory"))
2233 {
2234 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
2235 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
2236 }
2237 else if (pelmHwChild->nameEquals("Firmware"))
2238 {
2239 Utf8Str strFirmwareType;
2240 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
2241 {
2242 if ( (strFirmwareType == "BIOS")
2243 || (strFirmwareType == "1") // some trunk builds used the number here
2244 )
2245 hw.firmwareType = FirmwareType_BIOS;
2246 else if ( (strFirmwareType == "EFI")
2247 || (strFirmwareType == "2") // some trunk builds used the number here
2248 )
2249 hw.firmwareType = FirmwareType_EFI;
2250 else if ( strFirmwareType == "EFI32")
2251 hw.firmwareType = FirmwareType_EFI32;
2252 else if ( strFirmwareType == "EFI64")
2253 hw.firmwareType = FirmwareType_EFI64;
2254 else if ( strFirmwareType == "EFIDUAL")
2255 hw.firmwareType = FirmwareType_EFIDUAL;
2256 else
2257 throw ConfigFileError(this,
2258 pelmHwChild,
2259 N_("Invalid value '%s' in Firmware/@type"),
2260 strFirmwareType.c_str());
2261 }
2262 }
2263 else if (pelmHwChild->nameEquals("HID"))
2264 {
2265 Utf8Str strHidType;
2266 if (pelmHwChild->getAttributeValue("Keyboard", strHidType))
2267 {
2268 if (strHidType == "None")
2269 hw.keyboardHidType = KeyboardHidType_None;
2270 else if (strHidType == "USBKeyboard")
2271 hw.keyboardHidType = KeyboardHidType_USBKeyboard;
2272 else if (strHidType == "PS2Keyboard")
2273 hw.keyboardHidType = KeyboardHidType_PS2Keyboard;
2274 else if (strHidType == "ComboKeyboard")
2275 hw.keyboardHidType = KeyboardHidType_ComboKeyboard;
2276 else
2277 throw ConfigFileError(this,
2278 pelmHwChild,
2279 N_("Invalid value '%s' in HID/Keyboard/@type"),
2280 strHidType.c_str());
2281 }
2282 if (pelmHwChild->getAttributeValue("Pointing", strHidType))
2283 {
2284 if (strHidType == "None")
2285 hw.pointingHidType = PointingHidType_None;
2286 else if (strHidType == "USBMouse")
2287 hw.pointingHidType = PointingHidType_USBMouse;
2288 else if (strHidType == "USBTablet")
2289 hw.pointingHidType = PointingHidType_USBTablet;
2290 else if (strHidType == "PS2Mouse")
2291 hw.pointingHidType = PointingHidType_PS2Mouse;
2292 else if (strHidType == "ComboMouse")
2293 hw.pointingHidType = PointingHidType_ComboMouse;
2294 else
2295 throw ConfigFileError(this,
2296 pelmHwChild,
2297 N_("Invalid value '%s' in HID/Pointing/@type"),
2298 strHidType.c_str());
2299 }
2300 }
2301 else if (pelmHwChild->nameEquals("HPET"))
2302 {
2303 pelmHwChild->getAttributeValue("enabled", hw.fHpetEnabled);
2304 }
2305 else if (pelmHwChild->nameEquals("Boot"))
2306 {
2307 hw.mapBootOrder.clear();
2308
2309 xml::NodesLoop nl2(*pelmHwChild, "Order");
2310 const xml::ElementNode *pelmOrder;
2311 while ((pelmOrder = nl2.forAllNodes()))
2312 {
2313 uint32_t ulPos;
2314 Utf8Str strDevice;
2315 if (!pelmOrder->getAttributeValue("position", ulPos))
2316 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
2317
2318 if ( ulPos < 1
2319 || ulPos > SchemaDefs::MaxBootPosition
2320 )
2321 throw ConfigFileError(this,
2322 pelmOrder,
2323 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
2324 ulPos,
2325 SchemaDefs::MaxBootPosition + 1);
2326 // XML is 1-based but internal data is 0-based
2327 --ulPos;
2328
2329 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
2330 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
2331
2332 if (!pelmOrder->getAttributeValue("device", strDevice))
2333 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
2334
2335 DeviceType_T type;
2336 if (strDevice == "None")
2337 type = DeviceType_Null;
2338 else if (strDevice == "Floppy")
2339 type = DeviceType_Floppy;
2340 else if (strDevice == "DVD")
2341 type = DeviceType_DVD;
2342 else if (strDevice == "HardDisk")
2343 type = DeviceType_HardDisk;
2344 else if (strDevice == "Network")
2345 type = DeviceType_Network;
2346 else
2347 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
2348 hw.mapBootOrder[ulPos] = type;
2349 }
2350 }
2351 else if (pelmHwChild->nameEquals("Display"))
2352 {
2353 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
2354 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
2355 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
2356 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
2357 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
2358 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
2359 }
2360 else if (pelmHwChild->nameEquals("RemoteDisplay"))
2361 {
2362 pelmHwChild->getAttributeValue("enabled", hw.vrdpSettings.fEnabled);
2363 pelmHwChild->getAttributeValue("port", hw.vrdpSettings.strPort);
2364 pelmHwChild->getAttributeValue("netAddress", hw.vrdpSettings.strNetAddress);
2365
2366 Utf8Str strAuthType;
2367 if (pelmHwChild->getAttributeValue("authType", strAuthType))
2368 {
2369 // settings before 1.3 used lower case so make sure this is case-insensitive
2370 strAuthType.toUpper();
2371 if (strAuthType == "NULL")
2372 hw.vrdpSettings.authType = VRDPAuthType_Null;
2373 else if (strAuthType == "GUEST")
2374 hw.vrdpSettings.authType = VRDPAuthType_Guest;
2375 else if (strAuthType == "EXTERNAL")
2376 hw.vrdpSettings.authType = VRDPAuthType_External;
2377 else
2378 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
2379 }
2380
2381 pelmHwChild->getAttributeValue("authTimeout", hw.vrdpSettings.ulAuthTimeout);
2382 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
2383 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
2384
2385 const xml::ElementNode *pelmVideoChannel;
2386 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
2387 {
2388 pelmVideoChannel->getAttributeValue("enabled", hw.vrdpSettings.fVideoChannel);
2389 pelmVideoChannel->getAttributeValue("quality", hw.vrdpSettings.ulVideoChannelQuality);
2390 hw.vrdpSettings.ulVideoChannelQuality = RT_CLAMP(hw.vrdpSettings.ulVideoChannelQuality, 10, 100);
2391 }
2392 }
2393 else if (pelmHwChild->nameEquals("BIOS"))
2394 {
2395 const xml::ElementNode *pelmBIOSChild;
2396 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
2397 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
2398 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
2399 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
2400 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
2401 {
2402 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
2403 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
2404 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
2405 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
2406 }
2407 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
2408 {
2409 Utf8Str strBootMenuMode;
2410 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
2411 {
2412 // settings before 1.3 used lower case so make sure this is case-insensitive
2413 strBootMenuMode.toUpper();
2414 if (strBootMenuMode == "DISABLED")
2415 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
2416 else if (strBootMenuMode == "MENUONLY")
2417 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
2418 else if (strBootMenuMode == "MESSAGEANDMENU")
2419 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
2420 else
2421 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
2422 }
2423 }
2424 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
2425 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
2426 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
2427 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
2428
2429 // legacy BIOS/IDEController (pre 1.7)
2430 if ( (m->sv < SettingsVersion_v1_7)
2431 && ((pelmBIOSChild = pelmHwChild->findChildElement("IDEController")))
2432 )
2433 {
2434 StorageController sctl;
2435 sctl.strName = "IDE Controller";
2436 sctl.storageBus = StorageBus_IDE;
2437
2438 Utf8Str strType;
2439 if (pelmBIOSChild->getAttributeValue("type", strType))
2440 {
2441 if (strType == "PIIX3")
2442 sctl.controllerType = StorageControllerType_PIIX3;
2443 else if (strType == "PIIX4")
2444 sctl.controllerType = StorageControllerType_PIIX4;
2445 else if (strType == "ICH6")
2446 sctl.controllerType = StorageControllerType_ICH6;
2447 else
2448 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
2449 }
2450 sctl.ulPortCount = 2;
2451 strg.llStorageControllers.push_back(sctl);
2452 }
2453 }
2454 else if (pelmHwChild->nameEquals("USBController"))
2455 {
2456 pelmHwChild->getAttributeValue("enabled", hw.usbController.fEnabled);
2457 pelmHwChild->getAttributeValue("enabledEhci", hw.usbController.fEnabledEHCI);
2458
2459 readUSBDeviceFilters(*pelmHwChild,
2460 hw.usbController.llDeviceFilters);
2461 }
2462 else if ( (m->sv < SettingsVersion_v1_7)
2463 && (pelmHwChild->nameEquals("SATAController"))
2464 )
2465 {
2466 bool f;
2467 if ( (pelmHwChild->getAttributeValue("enabled", f))
2468 && (f)
2469 )
2470 {
2471 StorageController sctl;
2472 sctl.strName = "SATA Controller";
2473 sctl.storageBus = StorageBus_SATA;
2474 sctl.controllerType = StorageControllerType_IntelAhci;
2475
2476 readStorageControllerAttributes(*pelmHwChild, sctl);
2477
2478 strg.llStorageControllers.push_back(sctl);
2479 }
2480 }
2481 else if (pelmHwChild->nameEquals("Network"))
2482 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
2483 else if (pelmHwChild->nameEquals("RTC"))
2484 {
2485 Utf8Str strLocalOrUTC;
2486 fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
2487 && strLocalOrUTC == "UTC";
2488 }
2489 else if ( (pelmHwChild->nameEquals("UART"))
2490 || (pelmHwChild->nameEquals("Uart")) // used before 1.3
2491 )
2492 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
2493 else if ( (pelmHwChild->nameEquals("LPT"))
2494 || (pelmHwChild->nameEquals("Lpt")) // used before 1.3
2495 )
2496 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
2497 else if (pelmHwChild->nameEquals("AudioAdapter"))
2498 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
2499 else if (pelmHwChild->nameEquals("SharedFolders"))
2500 {
2501 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
2502 const xml::ElementNode *pelmFolder;
2503 while ((pelmFolder = nl2.forAllNodes()))
2504 {
2505 SharedFolder sf;
2506 pelmFolder->getAttributeValue("name", sf.strName);
2507 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
2508 pelmFolder->getAttributeValue("writable", sf.fWritable);
2509 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
2510 hw.llSharedFolders.push_back(sf);
2511 }
2512 }
2513 else if (pelmHwChild->nameEquals("Clipboard"))
2514 {
2515 Utf8Str strTemp;
2516 if (pelmHwChild->getAttributeValue("mode", strTemp))
2517 {
2518 if (strTemp == "Disabled")
2519 hw.clipboardMode = ClipboardMode_Disabled;
2520 else if (strTemp == "HostToGuest")
2521 hw.clipboardMode = ClipboardMode_HostToGuest;
2522 else if (strTemp == "GuestToHost")
2523 hw.clipboardMode = ClipboardMode_GuestToHost;
2524 else if (strTemp == "Bidirectional")
2525 hw.clipboardMode = ClipboardMode_Bidirectional;
2526 else
2527 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
2528 }
2529 }
2530 else if (pelmHwChild->nameEquals("Guest"))
2531 {
2532 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
2533 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
2534 }
2535 else if (pelmHwChild->nameEquals("GuestProperties"))
2536 readGuestProperties(*pelmHwChild, hw);
2537 else if (pelmHwChild->nameEquals("IO"))
2538 {
2539 const xml::ElementNode *pelmIoChild;
2540
2541 if ((pelmIoChild = pelmHwChild->findChildElement("IoCache")))
2542 {
2543 pelmIoChild->getAttributeValue("enabled", hw.ioSettings.fIoCacheEnabled);
2544 pelmIoChild->getAttributeValue("size", hw.ioSettings.ulIoCacheSize);
2545 }
2546 }
2547 }
2548
2549 if (hw.ulMemorySizeMB == (uint32_t)-1)
2550 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
2551}
2552
2553/**
2554 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
2555 * files which have a <HardDiskAttachments> node and storage controller settings
2556 * hidden in the <Hardware> settings. We set the StorageControllers fields just the
2557 * same, just from different sources.
2558 * @param elmHardware <Hardware> XML node.
2559 * @param elmHardDiskAttachments <HardDiskAttachments> XML node.
2560 * @param strg
2561 */
2562void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
2563 Storage &strg)
2564{
2565 StorageController *pIDEController = NULL;
2566 StorageController *pSATAController = NULL;
2567
2568 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
2569 it != strg.llStorageControllers.end();
2570 ++it)
2571 {
2572 StorageController &s = *it;
2573 if (s.storageBus == StorageBus_IDE)
2574 pIDEController = &s;
2575 else if (s.storageBus == StorageBus_SATA)
2576 pSATAController = &s;
2577 }
2578
2579 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
2580 const xml::ElementNode *pelmAttachment;
2581 while ((pelmAttachment = nl1.forAllNodes()))
2582 {
2583 AttachedDevice att;
2584 Utf8Str strUUID, strBus;
2585
2586 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
2587 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
2588 parseUUID(att.uuid, strUUID);
2589
2590 if (!pelmAttachment->getAttributeValue("bus", strBus))
2591 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
2592 // pre-1.7 'channel' is now port
2593 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
2594 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
2595 // pre-1.7 'device' is still device
2596 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
2597 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
2598
2599 att.deviceType = DeviceType_HardDisk;
2600
2601 if (strBus == "IDE")
2602 {
2603 if (!pIDEController)
2604 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
2605 pIDEController->llAttachedDevices.push_back(att);
2606 }
2607 else if (strBus == "SATA")
2608 {
2609 if (!pSATAController)
2610 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
2611 pSATAController->llAttachedDevices.push_back(att);
2612 }
2613 else
2614 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
2615 }
2616}
2617
2618/**
2619 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
2620 * Used both directly from readMachine and from readSnapshot, since snapshots
2621 * have their own storage controllers sections.
2622 *
2623 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
2624 * for earlier versions.
2625 *
2626 * @param elmStorageControllers
2627 */
2628void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
2629 Storage &strg)
2630{
2631 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
2632 const xml::ElementNode *pelmController;
2633 while ((pelmController = nlStorageControllers.forAllNodes()))
2634 {
2635 StorageController sctl;
2636
2637 if (!pelmController->getAttributeValue("name", sctl.strName))
2638 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
2639 // canonicalize storage controller names for configs in the switchover
2640 // period.
2641 if (m->sv < SettingsVersion_v1_9)
2642 {
2643 if (sctl.strName == "IDE")
2644 sctl.strName = "IDE Controller";
2645 else if (sctl.strName == "SATA")
2646 sctl.strName = "SATA Controller";
2647 else if (sctl.strName == "SCSI")
2648 sctl.strName = "SCSI Controller";
2649 }
2650
2651 pelmController->getAttributeValue("Instance", sctl.ulInstance);
2652 // default from constructor is 0
2653
2654 Utf8Str strType;
2655 if (!pelmController->getAttributeValue("type", strType))
2656 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
2657
2658 if (strType == "AHCI")
2659 {
2660 sctl.storageBus = StorageBus_SATA;
2661 sctl.controllerType = StorageControllerType_IntelAhci;
2662 }
2663 else if (strType == "LsiLogic")
2664 {
2665 sctl.storageBus = StorageBus_SCSI;
2666 sctl.controllerType = StorageControllerType_LsiLogic;
2667 }
2668 else if (strType == "BusLogic")
2669 {
2670 sctl.storageBus = StorageBus_SCSI;
2671 sctl.controllerType = StorageControllerType_BusLogic;
2672 }
2673 else if (strType == "PIIX3")
2674 {
2675 sctl.storageBus = StorageBus_IDE;
2676 sctl.controllerType = StorageControllerType_PIIX3;
2677 }
2678 else if (strType == "PIIX4")
2679 {
2680 sctl.storageBus = StorageBus_IDE;
2681 sctl.controllerType = StorageControllerType_PIIX4;
2682 }
2683 else if (strType == "ICH6")
2684 {
2685 sctl.storageBus = StorageBus_IDE;
2686 sctl.controllerType = StorageControllerType_ICH6;
2687 }
2688 else if ( (m->sv >= SettingsVersion_v1_9)
2689 && (strType == "I82078")
2690 )
2691 {
2692 sctl.storageBus = StorageBus_Floppy;
2693 sctl.controllerType = StorageControllerType_I82078;
2694 }
2695 else if (strType == "LsiLogicSas")
2696 {
2697 sctl.storageBus = StorageBus_SAS;
2698 sctl.controllerType = StorageControllerType_LsiLogicSas;
2699 }
2700 else
2701 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
2702
2703 readStorageControllerAttributes(*pelmController, sctl);
2704
2705 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
2706 const xml::ElementNode *pelmAttached;
2707 while ((pelmAttached = nlAttached.forAllNodes()))
2708 {
2709 AttachedDevice att;
2710 Utf8Str strTemp;
2711 pelmAttached->getAttributeValue("type", strTemp);
2712
2713 if (strTemp == "HardDisk")
2714 att.deviceType = DeviceType_HardDisk;
2715 else if (m->sv >= SettingsVersion_v1_9)
2716 {
2717 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
2718 if (strTemp == "DVD")
2719 {
2720 att.deviceType = DeviceType_DVD;
2721 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
2722 }
2723 else if (strTemp == "Floppy")
2724 att.deviceType = DeviceType_Floppy;
2725 }
2726
2727 if (att.deviceType != DeviceType_Null)
2728 {
2729 const xml::ElementNode *pelmImage;
2730 // all types can have images attached, but for HardDisk it's required
2731 if (!(pelmImage = pelmAttached->findChildElement("Image")))
2732 {
2733 if (att.deviceType == DeviceType_HardDisk)
2734 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
2735 else
2736 {
2737 // DVDs and floppies can also have <HostDrive> instead of <Image>
2738 const xml::ElementNode *pelmHostDrive;
2739 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
2740 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
2741 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
2742 }
2743 }
2744 else
2745 {
2746 if (!pelmImage->getAttributeValue("uuid", strTemp))
2747 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
2748 parseUUID(att.uuid, strTemp);
2749 }
2750
2751 if (!pelmAttached->getAttributeValue("port", att.lPort))
2752 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
2753 if (!pelmAttached->getAttributeValue("device", att.lDevice))
2754 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
2755
2756 sctl.llAttachedDevices.push_back(att);
2757 }
2758 }
2759
2760 strg.llStorageControllers.push_back(sctl);
2761 }
2762}
2763
2764/**
2765 * This gets called for legacy pre-1.9 settings files after having parsed the
2766 * <Hardware> and <StorageControllers> sections to parse <Hardware> once more
2767 * for the <DVDDrive> and <FloppyDrive> sections.
2768 *
2769 * Before settings version 1.9, DVD and floppy drives were specified separately
2770 * under <Hardware>; we then need this extra loop to make sure the storage
2771 * controller structs are already set up so we can add stuff to them.
2772 *
2773 * @param elmHardware
2774 * @param strg
2775 */
2776void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
2777 Storage &strg)
2778{
2779 xml::NodesLoop nl1(elmHardware);
2780 const xml::ElementNode *pelmHwChild;
2781 while ((pelmHwChild = nl1.forAllNodes()))
2782 {
2783 if (pelmHwChild->nameEquals("DVDDrive"))
2784 {
2785 // create a DVD "attached device" and attach it to the existing IDE controller
2786 AttachedDevice att;
2787 att.deviceType = DeviceType_DVD;
2788 // legacy DVD drive is always secondary master (port 1, device 0)
2789 att.lPort = 1;
2790 att.lDevice = 0;
2791 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
2792
2793 const xml::ElementNode *pDriveChild;
2794 Utf8Str strTmp;
2795 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2796 && (pDriveChild->getAttributeValue("uuid", strTmp))
2797 )
2798 parseUUID(att.uuid, strTmp);
2799 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2800 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2801
2802 // find the IDE controller and attach the DVD drive
2803 bool fFound = false;
2804 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
2805 it != strg.llStorageControllers.end();
2806 ++it)
2807 {
2808 StorageController &sctl = *it;
2809 if (sctl.storageBus == StorageBus_IDE)
2810 {
2811 sctl.llAttachedDevices.push_back(att);
2812 fFound = true;
2813 break;
2814 }
2815 }
2816
2817 if (!fFound)
2818 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
2819 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
2820 // which should have gotten parsed in <StorageControllers> before this got called
2821 }
2822 else if (pelmHwChild->nameEquals("FloppyDrive"))
2823 {
2824 bool fEnabled;
2825 if ( (pelmHwChild->getAttributeValue("enabled", fEnabled))
2826 && (fEnabled)
2827 )
2828 {
2829 // create a new floppy controller and attach a floppy "attached device"
2830 StorageController sctl;
2831 sctl.strName = "Floppy Controller";
2832 sctl.storageBus = StorageBus_Floppy;
2833 sctl.controllerType = StorageControllerType_I82078;
2834 sctl.ulPortCount = 1;
2835
2836 AttachedDevice att;
2837 att.deviceType = DeviceType_Floppy;
2838 att.lPort = 0;
2839 att.lDevice = 0;
2840
2841 const xml::ElementNode *pDriveChild;
2842 Utf8Str strTmp;
2843 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2844 && (pDriveChild->getAttributeValue("uuid", strTmp))
2845 )
2846 parseUUID(att.uuid, strTmp);
2847 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2848 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2849
2850 // store attachment with controller
2851 sctl.llAttachedDevices.push_back(att);
2852 // store controller with storage
2853 strg.llStorageControllers.push_back(sctl);
2854 }
2855 }
2856 }
2857}
2858
2859/**
2860 * Called initially for the <Snapshot> element under <Machine>, if present,
2861 * to store the snapshot's data into the given Snapshot structure (which is
2862 * then the one in the Machine struct). This might then recurse if
2863 * a <Snapshots> (plural) element is found in the snapshot, which should
2864 * contain a list of child snapshots; such lists are maintained in the
2865 * Snapshot structure.
2866 *
2867 * @param elmSnapshot
2868 * @param snap
2869 */
2870void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
2871 Snapshot &snap)
2872{
2873 Utf8Str strTemp;
2874
2875 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
2876 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
2877 parseUUID(snap.uuid, strTemp);
2878
2879 if (!elmSnapshot.getAttributeValue("name", snap.strName))
2880 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
2881
2882 // earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
2883 elmSnapshot.getAttributeValue("Description", snap.strDescription);
2884
2885 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
2886 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
2887 parseTimestamp(snap.timestamp, strTemp);
2888
2889 elmSnapshot.getAttributeValue("stateFile", snap.strStateFile); // online snapshots only
2890
2891 // parse Hardware before the other elements because other things depend on it
2892 const xml::ElementNode *pelmHardware;
2893 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
2894 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
2895 readHardware(*pelmHardware, snap.hardware, snap.storage);
2896
2897 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
2898 const xml::ElementNode *pelmSnapshotChild;
2899 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
2900 {
2901 if (pelmSnapshotChild->nameEquals("Description"))
2902 snap.strDescription = pelmSnapshotChild->getValue();
2903 else if ( (m->sv < SettingsVersion_v1_7)
2904 && (pelmSnapshotChild->nameEquals("HardDiskAttachments"))
2905 )
2906 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.storage);
2907 else if ( (m->sv >= SettingsVersion_v1_7)
2908 && (pelmSnapshotChild->nameEquals("StorageControllers"))
2909 )
2910 readStorageControllers(*pelmSnapshotChild, snap.storage);
2911 else if (pelmSnapshotChild->nameEquals("Snapshots"))
2912 {
2913 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
2914 const xml::ElementNode *pelmChildSnapshot;
2915 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
2916 {
2917 if (pelmChildSnapshot->nameEquals("Snapshot"))
2918 {
2919 Snapshot child;
2920 readSnapshot(*pelmChildSnapshot, child);
2921 snap.llChildSnapshots.push_back(child);
2922 }
2923 }
2924 }
2925 }
2926
2927 if (m->sv < SettingsVersion_v1_9)
2928 // go through Hardware once more to repair the settings controller structures
2929 // with data from old DVDDrive and FloppyDrive elements
2930 readDVDAndFloppies_pre1_9(*pelmHardware, snap.storage);
2931}
2932
2933const struct {
2934 const char *pcszOld;
2935 const char *pcszNew;
2936} aConvertOSTypes[] =
2937{
2938 { "unknown", "Other" },
2939 { "dos", "DOS" },
2940 { "win31", "Windows31" },
2941 { "win95", "Windows95" },
2942 { "win98", "Windows98" },
2943 { "winme", "WindowsMe" },
2944 { "winnt4", "WindowsNT4" },
2945 { "win2k", "Windows2000" },
2946 { "winxp", "WindowsXP" },
2947 { "win2k3", "Windows2003" },
2948 { "winvista", "WindowsVista" },
2949 { "win2k8", "Windows2008" },
2950 { "os2warp3", "OS2Warp3" },
2951 { "os2warp4", "OS2Warp4" },
2952 { "os2warp45", "OS2Warp45" },
2953 { "ecs", "OS2eCS" },
2954 { "linux22", "Linux22" },
2955 { "linux24", "Linux24" },
2956 { "linux26", "Linux26" },
2957 { "archlinux", "ArchLinux" },
2958 { "debian", "Debian" },
2959 { "opensuse", "OpenSUSE" },
2960 { "fedoracore", "Fedora" },
2961 { "gentoo", "Gentoo" },
2962 { "mandriva", "Mandriva" },
2963 { "redhat", "RedHat" },
2964 { "ubuntu", "Ubuntu" },
2965 { "xandros", "Xandros" },
2966 { "freebsd", "FreeBSD" },
2967 { "openbsd", "OpenBSD" },
2968 { "netbsd", "NetBSD" },
2969 { "netware", "Netware" },
2970 { "solaris", "Solaris" },
2971 { "opensolaris", "OpenSolaris" },
2972 { "l4", "L4" }
2973};
2974
2975void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
2976{
2977 for (unsigned u = 0;
2978 u < RT_ELEMENTS(aConvertOSTypes);
2979 ++u)
2980 {
2981 if (str == aConvertOSTypes[u].pcszOld)
2982 {
2983 str = aConvertOSTypes[u].pcszNew;
2984 break;
2985 }
2986 }
2987}
2988
2989/**
2990 * Called from the constructor to actually read in the <Machine> element
2991 * of a machine config file.
2992 * @param elmMachine
2993 */
2994void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
2995{
2996 Utf8Str strUUID;
2997 if ( (elmMachine.getAttributeValue("uuid", strUUID))
2998 && (elmMachine.getAttributeValue("name", strName))
2999 )
3000 {
3001 parseUUID(uuid, strUUID);
3002
3003 if (!elmMachine.getAttributeValue("nameSync", fNameSync))
3004 fNameSync = true;
3005
3006 Utf8Str str;
3007 elmMachine.getAttributeValue("Description", strDescription);
3008
3009 elmMachine.getAttributeValue("OSType", strOsType);
3010 if (m->sv < SettingsVersion_v1_5)
3011 convertOldOSType_pre1_5(strOsType);
3012
3013 elmMachine.getAttributeValue("stateFile", strStateFile);
3014 if (elmMachine.getAttributeValue("currentSnapshot", str))
3015 parseUUID(uuidCurrentSnapshot, str);
3016 elmMachine.getAttributeValue("snapshotFolder", strSnapshotFolder);
3017 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
3018 fCurrentStateModified = true;
3019 if (elmMachine.getAttributeValue("lastStateChange", str))
3020 parseTimestamp(timeLastStateChange, str);
3021 // constructor has called RTTimeNow(&timeLastStateChange) before
3022
3023 // parse Hardware before the other elements because other things depend on it
3024 const xml::ElementNode *pelmHardware;
3025 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
3026 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
3027 readHardware(*pelmHardware, hardwareMachine, storageMachine);
3028
3029 xml::NodesLoop nlRootChildren(elmMachine);
3030 const xml::ElementNode *pelmMachineChild;
3031 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
3032 {
3033 if (pelmMachineChild->nameEquals("ExtraData"))
3034 readExtraData(*pelmMachineChild,
3035 mapExtraDataItems);
3036 else if ( (m->sv < SettingsVersion_v1_7)
3037 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
3038 )
3039 readHardDiskAttachments_pre1_7(*pelmMachineChild, storageMachine);
3040 else if ( (m->sv >= SettingsVersion_v1_7)
3041 && (pelmMachineChild->nameEquals("StorageControllers"))
3042 )
3043 readStorageControllers(*pelmMachineChild, storageMachine);
3044 else if (pelmMachineChild->nameEquals("Snapshot"))
3045 {
3046 Snapshot snap;
3047 // this will recurse into child snapshots, if necessary
3048 readSnapshot(*pelmMachineChild, snap);
3049 llFirstSnapshot.push_back(snap);
3050 }
3051 else if (pelmMachineChild->nameEquals("Description"))
3052 strDescription = pelmMachineChild->getValue();
3053 else if (pelmMachineChild->nameEquals("Teleporter"))
3054 {
3055 if (!pelmMachineChild->getAttributeValue("enabled", fTeleporterEnabled))
3056 fTeleporterEnabled = false;
3057 if (!pelmMachineChild->getAttributeValue("port", uTeleporterPort))
3058 uTeleporterPort = 0;
3059 if (!pelmMachineChild->getAttributeValue("address", strTeleporterAddress))
3060 strTeleporterAddress = "";
3061 if (!pelmMachineChild->getAttributeValue("password", strTeleporterPassword))
3062 strTeleporterPassword = "";
3063 }
3064 }
3065
3066 if (m->sv < SettingsVersion_v1_9)
3067 // go through Hardware once more to repair the settings controller structures
3068 // with data from old DVDDrive and FloppyDrive elements
3069 readDVDAndFloppies_pre1_9(*pelmHardware, storageMachine);
3070 }
3071 else
3072 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
3073}
3074
3075/**
3076 * Creates a <Hardware> node under elmParent and then writes out the XML
3077 * keys under that. Called for both the <Machine> node and for snapshots.
3078 * @param elmParent
3079 * @param st
3080 */
3081void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
3082 const Hardware &hw,
3083 const Storage &strg)
3084{
3085 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
3086
3087 if (m->sv >= SettingsVersion_v1_4)
3088 pelmHardware->setAttribute("version", hw.strVersion);
3089 if ( (m->sv >= SettingsVersion_v1_9)
3090 && (!hw.uuid.isEmpty())
3091 )
3092 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
3093
3094 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
3095
3096 xml::ElementNode *pelmHwVirtEx = pelmCPU->createChild("HardwareVirtEx");
3097 pelmHwVirtEx->setAttribute("enabled", hw.fHardwareVirt);
3098 if (m->sv >= SettingsVersion_v1_9)
3099 pelmHwVirtEx->setAttribute("exclusive", hw.fHardwareVirtExclusive);
3100
3101 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
3102 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
3103 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
3104
3105 if (hw.fSyntheticCpu)
3106 pelmCPU->createChild("SyntheticCpu")->setAttribute("enabled", hw.fSyntheticCpu);
3107 pelmCPU->setAttribute("count", hw.cCPUs);
3108 if (hw.ulCpuPriority != 100)
3109 pelmCPU->setAttribute("priority", hw.ulCpuPriority);
3110
3111 if (hw.fLargePages)
3112 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
3113
3114 if (m->sv >= SettingsVersion_v1_10)
3115 {
3116 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
3117
3118 xml::ElementNode *pelmCpuTree = NULL;
3119 for (CpuList::const_iterator it = hw.llCpus.begin();
3120 it != hw.llCpus.end();
3121 ++it)
3122 {
3123 const Cpu &cpu = *it;
3124
3125 if (pelmCpuTree == NULL)
3126 pelmCpuTree = pelmCPU->createChild("CpuTree");
3127
3128 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
3129 pelmCpu->setAttribute("id", cpu.ulId);
3130 }
3131 }
3132
3133 xml::ElementNode *pelmCpuIdTree = NULL;
3134 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
3135 it != hw.llCpuIdLeafs.end();
3136 ++it)
3137 {
3138 const CpuIdLeaf &leaf = *it;
3139
3140 if (pelmCpuIdTree == NULL)
3141 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
3142
3143 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
3144 pelmCpuIdLeaf->setAttribute("id", leaf.ulId);
3145 pelmCpuIdLeaf->setAttribute("eax", leaf.ulEax);
3146 pelmCpuIdLeaf->setAttribute("ebx", leaf.ulEbx);
3147 pelmCpuIdLeaf->setAttribute("ecx", leaf.ulEcx);
3148 pelmCpuIdLeaf->setAttribute("edx", leaf.ulEdx);
3149 }
3150
3151 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
3152 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
3153 if (m->sv >= SettingsVersion_v1_10)
3154 {
3155 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
3156 }
3157
3158 if ( (m->sv >= SettingsVersion_v1_9)
3159 && (hw.firmwareType >= FirmwareType_EFI)
3160 )
3161 {
3162 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
3163 const char *pcszFirmware;
3164
3165 switch (hw.firmwareType)
3166 {
3167 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
3168 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
3169 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
3170 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
3171 default: pcszFirmware = "None"; break;
3172 }
3173 pelmFirmware->setAttribute("type", pcszFirmware);
3174 }
3175
3176 if ( (m->sv >= SettingsVersion_v1_10)
3177 )
3178 {
3179 xml::ElementNode *pelmHid = pelmHardware->createChild("HID");
3180 const char *pcszHid;
3181
3182 switch (hw.pointingHidType)
3183 {
3184 case PointingHidType_USBMouse: pcszHid = "USBMouse"; break;
3185 case PointingHidType_USBTablet: pcszHid = "USBTablet"; break;
3186 case PointingHidType_PS2Mouse: pcszHid = "PS2Mouse"; break;
3187 case PointingHidType_ComboMouse: pcszHid = "ComboMouse"; break;
3188 case PointingHidType_None: pcszHid = "None"; break;
3189 default: Assert(false); pcszHid = "PS2Mouse"; break;
3190 }
3191 pelmHid->setAttribute("Pointing", pcszHid);
3192
3193 switch (hw.keyboardHidType)
3194 {
3195 case KeyboardHidType_USBKeyboard: pcszHid = "USBKeyboard"; break;
3196 case KeyboardHidType_PS2Keyboard: pcszHid = "PS2Keyboard"; break;
3197 case KeyboardHidType_ComboKeyboard: pcszHid = "ComboKeyboard"; break;
3198 case KeyboardHidType_None: pcszHid = "None"; break;
3199 default: Assert(false); pcszHid = "PS2Keyboard"; break;
3200 }
3201 pelmHid->setAttribute("Keyboard", pcszHid);
3202 }
3203
3204 if ( (m->sv >= SettingsVersion_v1_10)
3205 )
3206 {
3207 xml::ElementNode *pelmHpet = pelmHardware->createChild("HPET");
3208 pelmHpet->setAttribute("enabled", hw.fHpetEnabled);
3209 }
3210
3211 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
3212 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
3213 it != hw.mapBootOrder.end();
3214 ++it)
3215 {
3216 uint32_t i = it->first;
3217 DeviceType_T type = it->second;
3218 const char *pcszDevice;
3219
3220 switch (type)
3221 {
3222 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
3223 case DeviceType_DVD: pcszDevice = "DVD"; break;
3224 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
3225 case DeviceType_Network: pcszDevice = "Network"; break;
3226 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
3227 }
3228
3229 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
3230 pelmOrder->setAttribute("position",
3231 i + 1); // XML is 1-based but internal data is 0-based
3232 pelmOrder->setAttribute("device", pcszDevice);
3233 }
3234
3235 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
3236 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
3237 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
3238 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
3239
3240 if (m->sv >= SettingsVersion_v1_8)
3241 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
3242
3243 xml::ElementNode *pelmVRDP = pelmHardware->createChild("RemoteDisplay");
3244 pelmVRDP->setAttribute("enabled", hw.vrdpSettings.fEnabled);
3245 Utf8Str strPort = hw.vrdpSettings.strPort;
3246 if (!strPort.length())
3247 strPort = "3389";
3248 pelmVRDP->setAttribute("port", strPort);
3249 if (hw.vrdpSettings.strNetAddress.length())
3250 pelmVRDP->setAttribute("netAddress", hw.vrdpSettings.strNetAddress);
3251 const char *pcszAuthType;
3252 switch (hw.vrdpSettings.authType)
3253 {
3254 case VRDPAuthType_Guest: pcszAuthType = "Guest"; break;
3255 case VRDPAuthType_External: pcszAuthType = "External"; break;
3256 default: /*case VRDPAuthType_Null:*/ pcszAuthType = "Null"; break;
3257 }
3258 pelmVRDP->setAttribute("authType", pcszAuthType);
3259
3260 if (hw.vrdpSettings.ulAuthTimeout != 0)
3261 pelmVRDP->setAttribute("authTimeout", hw.vrdpSettings.ulAuthTimeout);
3262 if (hw.vrdpSettings.fAllowMultiConnection)
3263 pelmVRDP->setAttribute("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
3264 if (hw.vrdpSettings.fReuseSingleConnection)
3265 pelmVRDP->setAttribute("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
3266
3267 if (m->sv >= SettingsVersion_v1_10)
3268 {
3269 xml::ElementNode *pelmVideoChannel = pelmVRDP->createChild("VideoChannel");
3270 pelmVideoChannel->setAttribute("enabled", hw.vrdpSettings.fVideoChannel);
3271 pelmVideoChannel->setAttribute("quality", hw.vrdpSettings.ulVideoChannelQuality);
3272 }
3273
3274 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
3275 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
3276 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
3277
3278 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
3279 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
3280 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
3281 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
3282 if (hw.biosSettings.strLogoImagePath.length())
3283 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
3284
3285 const char *pcszBootMenu;
3286 switch (hw.biosSettings.biosBootMenuMode)
3287 {
3288 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
3289 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
3290 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
3291 }
3292 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
3293 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
3294 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
3295
3296 if (m->sv < SettingsVersion_v1_9)
3297 {
3298 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
3299 // run thru the storage controllers to see if we have a DVD or floppy drives
3300 size_t cDVDs = 0;
3301 size_t cFloppies = 0;
3302
3303 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
3304 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
3305
3306 for (StorageControllersList::const_iterator it = strg.llStorageControllers.begin();
3307 it != strg.llStorageControllers.end();
3308 ++it)
3309 {
3310 const StorageController &sctl = *it;
3311 // in old settings format, the DVD drive could only have been under the IDE controller
3312 if (sctl.storageBus == StorageBus_IDE)
3313 {
3314 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
3315 it2 != sctl.llAttachedDevices.end();
3316 ++it2)
3317 {
3318 const AttachedDevice &att = *it2;
3319 if (att.deviceType == DeviceType_DVD)
3320 {
3321 if (cDVDs > 0)
3322 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
3323
3324 ++cDVDs;
3325
3326 pelmDVD->setAttribute("passthrough", att.fPassThrough);
3327 if (!att.uuid.isEmpty())
3328 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
3329 else if (att.strHostDriveSrc.length())
3330 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
3331 }
3332 }
3333 }
3334 else if (sctl.storageBus == StorageBus_Floppy)
3335 {
3336 size_t cFloppiesHere = sctl.llAttachedDevices.size();
3337 if (cFloppiesHere > 1)
3338 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
3339 if (cFloppiesHere)
3340 {
3341 const AttachedDevice &att = sctl.llAttachedDevices.front();
3342 pelmFloppy->setAttribute("enabled", true);
3343 if (!att.uuid.isEmpty())
3344 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
3345 else if (att.strHostDriveSrc.length())
3346 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
3347 }
3348
3349 cFloppies += cFloppiesHere;
3350 }
3351 }
3352
3353 if (cFloppies == 0)
3354 pelmFloppy->setAttribute("enabled", false);
3355 else if (cFloppies > 1)
3356 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
3357 }
3358
3359 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
3360 pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
3361 pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
3362
3363 writeUSBDeviceFilters(*pelmUSB,
3364 hw.usbController.llDeviceFilters,
3365 false); // fHostMode
3366
3367 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
3368 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
3369 it != hw.llNetworkAdapters.end();
3370 ++it)
3371 {
3372 const NetworkAdapter &nic = *it;
3373
3374 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
3375 pelmAdapter->setAttribute("slot", nic.ulSlot);
3376 pelmAdapter->setAttribute("enabled", nic.fEnabled);
3377 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
3378 pelmAdapter->setAttribute("cable", nic.fCableConnected);
3379 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
3380 if (nic.ulBootPriority != 0)
3381 {
3382 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
3383 }
3384 if (nic.fTraceEnabled)
3385 {
3386 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
3387 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
3388 }
3389 if (nic.ulBandwidthLimit)
3390 pelmAdapter->setAttribute("bandwidthLimit", nic.ulBandwidthLimit);
3391
3392 const char *pcszType;
3393 switch (nic.type)
3394 {
3395 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
3396 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
3397 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
3398 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
3399 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
3400 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
3401 }
3402 pelmAdapter->setAttribute("type", pcszType);
3403
3404 xml::ElementNode *pelmNAT;
3405 if (m->sv < SettingsVersion_v1_10)
3406 {
3407 switch (nic.mode)
3408 {
3409 case NetworkAttachmentType_NAT:
3410 pelmNAT = pelmAdapter->createChild("NAT");
3411 if (nic.nat.strNetwork.length())
3412 pelmNAT->setAttribute("network", nic.nat.strNetwork);
3413 break;
3414
3415 case NetworkAttachmentType_Bridged:
3416 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
3417 break;
3418
3419 case NetworkAttachmentType_Internal:
3420 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
3421 break;
3422
3423 case NetworkAttachmentType_HostOnly:
3424 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
3425 break;
3426
3427#if defined(VBOX_WITH_VDE)
3428 case NetworkAttachmentType_VDE:
3429 pelmAdapter->createChild("VDE")->setAttribute("network", nic.strName);
3430 break;
3431#endif
3432
3433 default: /*case NetworkAttachmentType_Null:*/
3434 break;
3435 }
3436 }
3437 else
3438 {
3439 /* m->sv >= SettingsVersion_v1_10 */
3440 xml::ElementNode *pelmDisabledNode= NULL;
3441 if (nic.fHasDisabledNAT)
3442 pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
3443 if (nic.fHasDisabledNAT)
3444 buildNetworkXML(NetworkAttachmentType_NAT, *pelmDisabledNode, nic);
3445 buildNetworkXML(nic.mode, *pelmAdapter, nic);
3446 }
3447 }
3448
3449 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
3450 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
3451 it != hw.llSerialPorts.end();
3452 ++it)
3453 {
3454 const SerialPort &port = *it;
3455 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
3456 pelmPort->setAttribute("slot", port.ulSlot);
3457 pelmPort->setAttribute("enabled", port.fEnabled);
3458 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
3459 pelmPort->setAttribute("IRQ", port.ulIRQ);
3460
3461 const char *pcszHostMode;
3462 switch (port.portMode)
3463 {
3464 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
3465 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
3466 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
3467 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
3468 }
3469 switch (port.portMode)
3470 {
3471 case PortMode_HostPipe:
3472 pelmPort->setAttribute("server", port.fServer);
3473 /* no break */
3474 case PortMode_HostDevice:
3475 case PortMode_RawFile:
3476 pelmPort->setAttribute("path", port.strPath);
3477 break;
3478
3479 default:
3480 break;
3481 }
3482 pelmPort->setAttribute("hostMode", pcszHostMode);
3483 }
3484
3485 pelmPorts = pelmHardware->createChild("LPT");
3486 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
3487 it != hw.llParallelPorts.end();
3488 ++it)
3489 {
3490 const ParallelPort &port = *it;
3491 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
3492 pelmPort->setAttribute("slot", port.ulSlot);
3493 pelmPort->setAttribute("enabled", port.fEnabled);
3494 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
3495 pelmPort->setAttribute("IRQ", port.ulIRQ);
3496 if (port.strPath.length())
3497 pelmPort->setAttribute("path", port.strPath);
3498 }
3499
3500 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
3501 const char *pcszController;
3502 switch (hw.audioAdapter.controllerType)
3503 {
3504 case AudioControllerType_SB16:
3505 pcszController = "SB16";
3506 break;
3507 case AudioControllerType_HDA:
3508 if (m->sv >= SettingsVersion_v1_11)
3509 {
3510 pcszController = "HDA";
3511 break;
3512 }
3513 /* fall through */
3514 case AudioControllerType_AC97:
3515 default:
3516 pcszController = "AC97"; break;
3517 }
3518 pelmAudio->setAttribute("controller", pcszController);
3519
3520 if (m->sv >= SettingsVersion_v1_10)
3521 {
3522 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
3523 pelmRTC->setAttribute("localOrUTC", fRTCUseUTC ? "UTC" : "local");
3524 }
3525
3526 const char *pcszDriver;
3527 switch (hw.audioAdapter.driverType)
3528 {
3529 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
3530 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
3531 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
3532 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
3533 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
3534 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
3535 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
3536 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
3537 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
3538 }
3539 pelmAudio->setAttribute("driver", pcszDriver);
3540
3541 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
3542
3543 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
3544 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
3545 it != hw.llSharedFolders.end();
3546 ++it)
3547 {
3548 const SharedFolder &sf = *it;
3549 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
3550 pelmThis->setAttribute("name", sf.strName);
3551 pelmThis->setAttribute("hostPath", sf.strHostPath);
3552 pelmThis->setAttribute("writable", sf.fWritable);
3553 pelmThis->setAttribute("autoMount", sf.fAutoMount);
3554 }
3555
3556 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
3557 const char *pcszClip;
3558 switch (hw.clipboardMode)
3559 {
3560 case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
3561 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
3562 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
3563 default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
3564 }
3565 pelmClip->setAttribute("mode", pcszClip);
3566
3567 if (m->sv >= SettingsVersion_v1_10)
3568 {
3569 xml::ElementNode *pelmIo = pelmHardware->createChild("IO");
3570 xml::ElementNode *pelmIoCache;
3571 xml::ElementNode *pelmIoBandwidth;
3572
3573 pelmIoCache = pelmIo->createChild("IoCache");
3574 pelmIoCache->setAttribute("enabled", hw.ioSettings.fIoCacheEnabled);
3575 pelmIoCache->setAttribute("size", hw.ioSettings.ulIoCacheSize);
3576 pelmIoBandwidth = pelmIo->createChild("IoBandwidth");
3577 }
3578
3579 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
3580 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
3581
3582 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
3583 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
3584 it != hw.llGuestProperties.end();
3585 ++it)
3586 {
3587 const GuestProperty &prop = *it;
3588 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
3589 pelmProp->setAttribute("name", prop.strName);
3590 pelmProp->setAttribute("value", prop.strValue);
3591 pelmProp->setAttribute("timestamp", prop.timestamp);
3592 pelmProp->setAttribute("flags", prop.strFlags);
3593 }
3594
3595 if (hw.strNotificationPatterns.length())
3596 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
3597}
3598
3599/**
3600 * Fill a <Network> node. Only relevant for XML version >= v1_10.
3601 * @param mode
3602 * @param elmParent
3603 * @param nice
3604 */
3605void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
3606 xml::ElementNode &elmParent,
3607 const NetworkAdapter &nic)
3608{
3609 switch (mode)
3610 {
3611 case NetworkAttachmentType_NAT:
3612 xml::ElementNode *pelmNAT;
3613 pelmNAT = elmParent.createChild("NAT");
3614
3615 if (nic.nat.strNetwork.length())
3616 pelmNAT->setAttribute("network", nic.nat.strNetwork);
3617 if (nic.nat.strBindIP.length())
3618 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
3619 if (nic.nat.u32Mtu)
3620 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
3621 if (nic.nat.u32SockRcv)
3622 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
3623 if (nic.nat.u32SockSnd)
3624 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
3625 if (nic.nat.u32TcpRcv)
3626 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
3627 if (nic.nat.u32TcpSnd)
3628 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
3629 xml::ElementNode *pelmDNS;
3630 pelmDNS = pelmNAT->createChild("DNS");
3631 pelmDNS->setAttribute("pass-domain", nic.nat.fDnsPassDomain);
3632 pelmDNS->setAttribute("use-proxy", nic.nat.fDnsProxy);
3633 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDnsUseHostResolver);
3634
3635 xml::ElementNode *pelmAlias;
3636 pelmAlias = pelmNAT->createChild("Alias");
3637 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
3638 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
3639 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
3640
3641 if ( nic.nat.strTftpPrefix.length()
3642 || nic.nat.strTftpBootFile.length()
3643 || nic.nat.strTftpNextServer.length())
3644 {
3645 xml::ElementNode *pelmTFTP;
3646 pelmTFTP = pelmNAT->createChild("TFTP");
3647 if (nic.nat.strTftpPrefix.length())
3648 pelmTFTP->setAttribute("prefix", nic.nat.strTftpPrefix);
3649 if (nic.nat.strTftpBootFile.length())
3650 pelmTFTP->setAttribute("boot-file", nic.nat.strTftpBootFile);
3651 if (nic.nat.strTftpNextServer.length())
3652 pelmTFTP->setAttribute("next-server", nic.nat.strTftpNextServer);
3653 }
3654 for (NATRuleList::const_iterator rule = nic.nat.llRules.begin();
3655 rule != nic.nat.llRules.end(); ++rule)
3656 {
3657 xml::ElementNode *pelmPF;
3658 pelmPF = pelmNAT->createChild("Forwarding");
3659 if ((*rule).strName.length())
3660 pelmPF->setAttribute("name", (*rule).strName);
3661 pelmPF->setAttribute("proto", (*rule).u32Proto);
3662 if ((*rule).strHostIP.length())
3663 pelmPF->setAttribute("hostip", (*rule).strHostIP);
3664 if ((*rule).u16HostPort)
3665 pelmPF->setAttribute("hostport", (*rule).u16HostPort);
3666 if ((*rule).strGuestIP.length())
3667 pelmPF->setAttribute("guestip", (*rule).strGuestIP);
3668 if ((*rule).u16GuestPort)
3669 pelmPF->setAttribute("guestport", (*rule).u16GuestPort);
3670 }
3671 break;
3672
3673 case NetworkAttachmentType_Bridged:
3674 elmParent.createChild("BridgedInterface")->setAttribute("name", nic.strName);
3675 break;
3676
3677 case NetworkAttachmentType_Internal:
3678 elmParent.createChild("InternalNetwork")->setAttribute("name", nic.strName);
3679 break;
3680
3681 case NetworkAttachmentType_HostOnly:
3682 elmParent.createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
3683 break;
3684
3685#ifdef VBOX_WITH_VDE
3686 case NetworkAttachmentType_VDE:
3687 elmParent.createChild("VDE")->setAttribute("network", nic.strName);
3688 break;
3689#endif
3690
3691 default: /*case NetworkAttachmentType_Null:*/
3692 break;
3693 }
3694}
3695
3696/**
3697 * Creates a <StorageControllers> node under elmParent and then writes out the XML
3698 * keys under that. Called for both the <Machine> node and for snapshots.
3699 * @param elmParent
3700 * @param st
3701 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
3702 * an empty drive is always written instead. This is for the OVF export case.
3703 * This parameter is ignored unless the settings version is at least v1.9, which
3704 * is always the case when this gets called for OVF export.
3705 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
3706 * pointers to which we will append all allements that we created here that contain
3707 * UUID attributes. This allows the OVF export code to quickly replace the internal
3708 * media UUIDs with the UUIDs of the media that were exported.
3709 */
3710void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
3711 const Storage &st,
3712 bool fSkipRemovableMedia,
3713 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
3714{
3715 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
3716
3717 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
3718 it != st.llStorageControllers.end();
3719 ++it)
3720 {
3721 const StorageController &sc = *it;
3722
3723 if ( (m->sv < SettingsVersion_v1_9)
3724 && (sc.controllerType == StorageControllerType_I82078)
3725 )
3726 // floppy controller already got written into <Hardware>/<FloppyController> in writeHardware()
3727 // for pre-1.9 settings
3728 continue;
3729
3730 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
3731 com::Utf8Str name = sc.strName.raw();
3732 //
3733 if (m->sv < SettingsVersion_v1_8)
3734 {
3735 // pre-1.8 settings use shorter controller names, they are
3736 // expanded when reading the settings
3737 if (name == "IDE Controller")
3738 name = "IDE";
3739 else if (name == "SATA Controller")
3740 name = "SATA";
3741 else if (name == "SCSI Controller")
3742 name = "SCSI";
3743 }
3744 pelmController->setAttribute("name", sc.strName);
3745
3746 const char *pcszType;
3747 switch (sc.controllerType)
3748 {
3749 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
3750 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
3751 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
3752 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
3753 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
3754 case StorageControllerType_I82078: pcszType = "I82078"; break;
3755 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
3756 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
3757 }
3758 pelmController->setAttribute("type", pcszType);
3759
3760 pelmController->setAttribute("PortCount", sc.ulPortCount);
3761
3762 if (m->sv >= SettingsVersion_v1_9)
3763 if (sc.ulInstance)
3764 pelmController->setAttribute("Instance", sc.ulInstance);
3765
3766 if (m->sv >= SettingsVersion_v1_10)
3767 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
3768
3769 if (sc.controllerType == StorageControllerType_IntelAhci)
3770 {
3771 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
3772 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
3773 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
3774 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
3775 }
3776
3777 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
3778 it2 != sc.llAttachedDevices.end();
3779 ++it2)
3780 {
3781 const AttachedDevice &att = *it2;
3782
3783 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
3784 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
3785 // the floppy controller at the top of the loop
3786 if ( att.deviceType == DeviceType_DVD
3787 && m->sv < SettingsVersion_v1_9
3788 )
3789 continue;
3790
3791 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
3792
3793 pcszType = NULL;
3794
3795 switch (att.deviceType)
3796 {
3797 case DeviceType_HardDisk:
3798 pcszType = "HardDisk";
3799 break;
3800
3801 case DeviceType_DVD:
3802 pcszType = "DVD";
3803 pelmDevice->setAttribute("passthrough", att.fPassThrough);
3804 break;
3805
3806 case DeviceType_Floppy:
3807 pcszType = "Floppy";
3808 break;
3809 }
3810
3811 pelmDevice->setAttribute("type", pcszType);
3812
3813 pelmDevice->setAttribute("port", att.lPort);
3814 pelmDevice->setAttribute("device", att.lDevice);
3815
3816 // attached image, if any
3817 if ( !att.uuid.isEmpty()
3818 && ( att.deviceType == DeviceType_HardDisk
3819 || !fSkipRemovableMedia
3820 )
3821 )
3822 {
3823 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
3824 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
3825
3826 // if caller wants a list of UUID elements, give it to them
3827 if (pllElementsWithUuidAttributes)
3828 pllElementsWithUuidAttributes->push_back(pelmImage);
3829 }
3830 else if ( (m->sv >= SettingsVersion_v1_9)
3831 && (att.strHostDriveSrc.length())
3832 )
3833 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
3834 }
3835 }
3836}
3837
3838/**
3839 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
3840 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
3841 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
3842 * @param elmParent
3843 * @param snap
3844 */
3845void MachineConfigFile::buildSnapshotXML(xml::ElementNode &elmParent,
3846 const Snapshot &snap)
3847{
3848 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
3849
3850 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
3851 pelmSnapshot->setAttribute("name", snap.strName);
3852 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
3853
3854 if (snap.strStateFile.length())
3855 pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
3856
3857 if (snap.strDescription.length())
3858 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
3859
3860 buildHardwareXML(*pelmSnapshot, snap.hardware, snap.storage);
3861 buildStorageControllersXML(*pelmSnapshot,
3862 snap.storage,
3863 false /* fSkipRemovableMedia */,
3864 NULL); /* pllElementsWithUuidAttributes */
3865 // we only skip removable media for OVF, but we never get here for OVF
3866 // since snapshots never get written then
3867
3868 if (snap.llChildSnapshots.size())
3869 {
3870 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
3871 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
3872 it != snap.llChildSnapshots.end();
3873 ++it)
3874 {
3875 const Snapshot &child = *it;
3876 buildSnapshotXML(*pelmChildren, child);
3877 }
3878 }
3879}
3880
3881/**
3882 * Builds the XML DOM tree for the machine config under the given XML element.
3883 *
3884 * This has been separated out from write() so it can be called from elsewhere,
3885 * such as the OVF code, to build machine XML in an existing XML tree.
3886 *
3887 * As a result, this gets called from two locations:
3888 *
3889 * -- MachineConfigFile::write();
3890 *
3891 * -- Appliance::buildXMLForOneVirtualSystem()
3892 *
3893 * In fl, the following flag bits are recognized:
3894 *
3895 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
3896 * of the machine and write out <Snapshot> and possibly more snapshots under
3897 * that, if snapshots are present. Otherwise all snapshots are suppressed.
3898 *
3899 * -- BuildMachineXML_WriteVboxVersionAttribute: If set, add a settingsVersion
3900 * attribute to the machine tag with the vbox settings version. This is for
3901 * the OVF export case in which we don't have the settings version set in
3902 * the root element.
3903 *
3904 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
3905 * (DVDs, floppies) are silently skipped. This is for the OVF export case
3906 * until we support copying ISO and RAW media as well. This flas is ignored
3907 * unless the settings version is at least v1.9, which is always the case
3908 * when this gets called for OVF export.
3909 *
3910 * @param elmMachine XML <Machine> element to add attributes and elements to.
3911 * @param fl Flags.
3912 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
3913 * see buildStorageControllersXML() for details.
3914 */
3915void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
3916 uint32_t fl,
3917 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
3918{
3919 if (fl & BuildMachineXML_WriteVboxVersionAttribute)
3920 // add settings version attribute to machine element
3921 setVersionAttribute(elmMachine);
3922
3923 elmMachine.setAttribute("uuid", uuid.toStringCurly());
3924 elmMachine.setAttribute("name", strName);
3925 if (!fNameSync)
3926 elmMachine.setAttribute("nameSync", fNameSync);
3927 if (strDescription.length())
3928 elmMachine.createChild("Description")->addContent(strDescription);
3929 elmMachine.setAttribute("OSType", strOsType);
3930 if (strStateFile.length())
3931 elmMachine.setAttribute("stateFile", strStateFile);
3932 if ( (fl & BuildMachineXML_IncludeSnapshots)
3933 && !uuidCurrentSnapshot.isEmpty())
3934 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
3935 if (strSnapshotFolder.length())
3936 elmMachine.setAttribute("snapshotFolder", strSnapshotFolder);
3937 if (!fCurrentStateModified)
3938 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
3939 elmMachine.setAttribute("lastStateChange", makeString(timeLastStateChange));
3940 if (fAborted)
3941 elmMachine.setAttribute("aborted", fAborted);
3942 if ( m->sv >= SettingsVersion_v1_9
3943 && ( fTeleporterEnabled
3944 || uTeleporterPort
3945 || !strTeleporterAddress.isEmpty()
3946 || !strTeleporterPassword.isEmpty()
3947 )
3948 )
3949 {
3950 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
3951 pelmTeleporter->setAttribute("enabled", fTeleporterEnabled);
3952 pelmTeleporter->setAttribute("port", uTeleporterPort);
3953 pelmTeleporter->setAttribute("address", strTeleporterAddress);
3954 pelmTeleporter->setAttribute("password", strTeleporterPassword);
3955 }
3956
3957 writeExtraData(elmMachine, mapExtraDataItems);
3958
3959 if ( (fl & BuildMachineXML_IncludeSnapshots)
3960 && llFirstSnapshot.size())
3961 buildSnapshotXML(elmMachine, llFirstSnapshot.front());
3962
3963 buildHardwareXML(elmMachine, hardwareMachine, storageMachine);
3964 buildStorageControllersXML(elmMachine,
3965 storageMachine,
3966 !!(fl & BuildMachineXML_SkipRemovableMedia),
3967 pllElementsWithUuidAttributes);
3968}
3969
3970/**
3971 * Returns true only if the given AudioDriverType is supported on
3972 * the current host platform. For example, this would return false
3973 * for AudioDriverType_DirectSound when compiled on a Linux host.
3974 * @param drv AudioDriverType_* enum to test.
3975 * @return true only if the current host supports that driver.
3976 */
3977/*static*/
3978bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
3979{
3980 switch (drv)
3981 {
3982 case AudioDriverType_Null:
3983#ifdef RT_OS_WINDOWS
3984# ifdef VBOX_WITH_WINMM
3985 case AudioDriverType_WinMM:
3986# endif
3987 case AudioDriverType_DirectSound:
3988#endif /* RT_OS_WINDOWS */
3989#ifdef RT_OS_SOLARIS
3990 case AudioDriverType_SolAudio:
3991#endif
3992#ifdef RT_OS_LINUX
3993# ifdef VBOX_WITH_ALSA
3994 case AudioDriverType_ALSA:
3995# endif
3996# ifdef VBOX_WITH_PULSE
3997 case AudioDriverType_Pulse:
3998# endif
3999#endif /* RT_OS_LINUX */
4000#if defined (RT_OS_LINUX) || defined (RT_OS_FREEBSD) || defined(VBOX_WITH_SOLARIS_OSS)
4001 case AudioDriverType_OSS:
4002#endif
4003#ifdef RT_OS_FREEBSD
4004# ifdef VBOX_WITH_PULSE
4005 case AudioDriverType_Pulse:
4006# endif
4007#endif
4008#ifdef RT_OS_DARWIN
4009 case AudioDriverType_CoreAudio:
4010#endif
4011#ifdef RT_OS_OS2
4012 case AudioDriverType_MMPM:
4013#endif
4014 return true;
4015 }
4016
4017 return false;
4018}
4019
4020/**
4021 * Returns the AudioDriverType_* which should be used by default on this
4022 * host platform. On Linux, this will check at runtime whether PulseAudio
4023 * or ALSA are actually supported on the first call.
4024 * @return
4025 */
4026/*static*/
4027AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
4028{
4029#if defined(RT_OS_WINDOWS)
4030# ifdef VBOX_WITH_WINMM
4031 return AudioDriverType_WinMM;
4032# else /* VBOX_WITH_WINMM */
4033 return AudioDriverType_DirectSound;
4034# endif /* !VBOX_WITH_WINMM */
4035#elif defined(RT_OS_SOLARIS)
4036 return AudioDriverType_SolAudio;
4037#elif defined(RT_OS_LINUX)
4038 // on Linux, we need to check at runtime what's actually supported...
4039 static RTLockMtx s_mtx;
4040 static AudioDriverType_T s_linuxDriver = -1;
4041 RTLock lock(s_mtx);
4042 if (s_linuxDriver == (AudioDriverType_T)-1)
4043 {
4044# if defined(VBOX_WITH_PULSE)
4045 /* Check for the pulse library & that the pulse audio daemon is running. */
4046 if (RTProcIsRunningByName("pulseaudio") &&
4047 RTLdrIsLoadable("libpulse.so.0"))
4048 s_linuxDriver = AudioDriverType_Pulse;
4049 else
4050# endif /* VBOX_WITH_PULSE */
4051# if defined(VBOX_WITH_ALSA)
4052 /* Check if we can load the ALSA library */
4053 if (RTLdrIsLoadable("libasound.so.2"))
4054 s_linuxDriver = AudioDriverType_ALSA;
4055 else
4056# endif /* VBOX_WITH_ALSA */
4057 s_linuxDriver = AudioDriverType_OSS;
4058 }
4059 return s_linuxDriver;
4060// end elif defined(RT_OS_LINUX)
4061#elif defined(RT_OS_DARWIN)
4062 return AudioDriverType_CoreAudio;
4063#elif defined(RT_OS_OS2)
4064 return AudioDriverType_MMP;
4065#elif defined(RT_OS_FREEBSD)
4066 return AudioDriverType_OSS;
4067#else
4068 return AudioDriverType_Null;
4069#endif
4070}
4071
4072/**
4073 * Called from write() before calling ConfigFileBase::createStubDocument().
4074 * This adjusts the settings version in m->sv if incompatible settings require
4075 * a settings bump, whereas otherwise we try to preserve the settings version
4076 * to avoid breaking compatibility with older versions.
4077 */
4078void MachineConfigFile::bumpSettingsVersionIfNeeded()
4079{
4080 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
4081 if ( m->sv < SettingsVersion_v1_4
4082 && hardwareMachine.strVersion != "1"
4083 )
4084 m->sv = SettingsVersion_v1_4;
4085
4086 // "accelerate 2d video" requires settings version 1.8
4087 if ( (m->sv < SettingsVersion_v1_8)
4088 && (hardwareMachine.fAccelerate2DVideo)
4089 )
4090 m->sv = SettingsVersion_v1_8;
4091
4092 // all the following require settings version 1.9
4093 if ( (m->sv < SettingsVersion_v1_9)
4094 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
4095 || (hardwareMachine.fHardwareVirtExclusive != HWVIRTEXCLUSIVEDEFAULT)
4096 || fTeleporterEnabled
4097 || uTeleporterPort
4098 || !strTeleporterAddress.isEmpty()
4099 || !strTeleporterPassword.isEmpty()
4100 || !hardwareMachine.uuid.isEmpty()
4101 )
4102 )
4103 m->sv = SettingsVersion_v1_9;
4104
4105 // settings version 1.9 is also required if there is not exactly one DVD
4106 // or more than one floppy drive present or the DVD is not at the secondary
4107 // master; this check is a bit more complicated
4108 //
4109 // settings version 1.10 is required if the host cache should be disabled
4110 if (m->sv < SettingsVersion_v1_10)
4111 {
4112 size_t cDVDs = 0;
4113 size_t cFloppies = 0;
4114
4115 // need to run thru all the storage controllers to figure this out
4116 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
4117 it != storageMachine.llStorageControllers.end();
4118 ++it)
4119 {
4120 const StorageController &sctl = *it;
4121 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
4122 it2 != sctl.llAttachedDevices.end();
4123 ++it2)
4124 {
4125 // we can only write the StorageController/@Instance attribute with v1.9
4126 if (sctl.ulInstance != 0)
4127 {
4128 if (m->sv < SettingsVersion_v1_9)
4129 m->sv = SettingsVersion_v1_9;
4130 }
4131
4132 const AttachedDevice &att = *it2;
4133 if (att.deviceType == DeviceType_DVD)
4134 {
4135 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
4136 || (att.lPort != 1) // DVDs not at secondary master?
4137 || (att.lDevice != 0)
4138 )
4139 {
4140 if (m->sv < SettingsVersion_v1_9)
4141 m->sv = SettingsVersion_v1_9;
4142 }
4143
4144 ++cDVDs;
4145 }
4146 else if (att.deviceType == DeviceType_Floppy)
4147 ++cFloppies;
4148
4149 // Disabling the host IO cache requires settings version 1.10
4150 if (!sctl.fUseHostIOCache)
4151 {
4152 m->sv = SettingsVersion_v1_10;
4153 break; /* abort the loop -- we will not raise the version further */
4154 }
4155 }
4156 }
4157
4158 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
4159 // so any deviation from that will require settings version 1.9
4160 if ( (m->sv < SettingsVersion_v1_9)
4161 && ( (cDVDs != 1)
4162 || (cFloppies > 1)
4163 )
4164 )
4165 m->sv = SettingsVersion_v1_9;
4166 }
4167
4168 // VirtualBox 3.2 adds support for CPU hotplug, RTC timezone control, HID type and HPET
4169 if ( m->sv < SettingsVersion_v1_10
4170 && ( fRTCUseUTC
4171 || hardwareMachine.fCpuHotPlug
4172 || hardwareMachine.pointingHidType != PointingHidType_PS2Mouse
4173 || hardwareMachine.keyboardHidType != KeyboardHidType_PS2Keyboard
4174 || hardwareMachine.fHpetEnabled
4175 )
4176 )
4177 m->sv = SettingsVersion_v1_10;
4178
4179 // VirtualBox 3.2 adds support for page fusion
4180 if ( m->sv < SettingsVersion_v1_10
4181 && hardwareMachine.fPageFusionEnabled
4182 )
4183 m->sv = SettingsVersion_v1_10;
4184
4185 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main.
4186 if (m->sv < SettingsVersion_v1_10)
4187 {
4188 NetworkAdaptersList::const_iterator netit;
4189 for (netit = hardwareMachine.llNetworkAdapters.begin();
4190 netit != hardwareMachine.llNetworkAdapters.end(); ++netit)
4191 {
4192 if (netit->ulBandwidthLimit)
4193 {
4194 /* New in VirtualBox 3.3 */
4195 m->sv = SettingsVersion_v1_11;
4196 break;
4197 }
4198
4199 if ( netit->fEnabled
4200 && netit->mode == NetworkAttachmentType_NAT
4201 && ( netit->nat.u32Mtu != 0
4202 || netit->nat.u32SockRcv != 0
4203 || netit->nat.u32SockSnd != 0
4204 || netit->nat.u32TcpRcv != 0
4205 || netit->nat.u32TcpSnd != 0
4206 || !netit->nat.fDnsPassDomain
4207 || netit->nat.fDnsProxy
4208 || netit->nat.fDnsUseHostResolver
4209 || netit->nat.fAliasLog
4210 || netit->nat.fAliasProxyOnly
4211 || netit->nat.fAliasUseSamePorts
4212 || netit->nat.strTftpPrefix.length()
4213 || netit->nat.strTftpBootFile.length()
4214 || netit->nat.strTftpNextServer.length()
4215 || netit->nat.llRules.size())
4216 )
4217 {
4218 m->sv = SettingsVersion_v1_10;
4219 break;
4220 }
4221 if ( netit->fEnabled
4222 && netit->ulBootPriority != 0)
4223 {
4224 m->sv = SettingsVersion_v1_10;
4225 break;
4226 }
4227 }
4228 }
4229 // VirtualBox 3.2: Check for non default I/O settings and bump the settings version.
4230 if (m->sv < SettingsVersion_v1_11)
4231 {
4232 if ( hardwareMachine.ioSettings.fIoCacheEnabled != true
4233 || hardwareMachine.ioSettings.ulIoCacheSize != 5)
4234 m->sv = SettingsVersion_v1_10;
4235 }
4236
4237 // VirtualBox 3.2 adds support for VRDP video channel
4238 if ( m->sv < SettingsVersion_v1_11
4239 && ( hardwareMachine.vrdpSettings.fVideoChannel
4240 )
4241 )
4242 m->sv = SettingsVersion_v1_10;
4243
4244 // VirtualBox 3.3 adds support for HD audio
4245 if ( m->sv < SettingsVersion_v1_11
4246 && ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
4247 )
4248 )
4249 m->sv = SettingsVersion_v1_11;
4250
4251 // VirtualBox 3.3 adds support for CPU priority
4252 if ( m->sv < SettingsVersion_v1_11
4253 && ( hardwareMachine.ulCpuPriority != 100
4254 )
4255 )
4256 m->sv = SettingsVersion_v1_10;
4257}
4258
4259/**
4260 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
4261 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
4262 * in particular if the file cannot be written.
4263 */
4264void MachineConfigFile::write(const com::Utf8Str &strFilename)
4265{
4266 try
4267 {
4268 // createStubDocument() sets the settings version to at least 1.7; however,
4269 // we might need to enfore a later settings version if incompatible settings
4270 // are present:
4271 bumpSettingsVersionIfNeeded();
4272
4273 m->strFilename = strFilename;
4274 createStubDocument();
4275
4276 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
4277 buildMachineXML(*pelmMachine,
4278 MachineConfigFile::BuildMachineXML_IncludeSnapshots,
4279 // but not BuildMachineXML_WriteVboxVersionAttribute
4280 NULL); /* pllElementsWithUuidAttributes */
4281
4282 // now go write the XML
4283 xml::XmlFileWriter writer(*m->pDoc);
4284 writer.write(m->strFilename.c_str(), true /*fSafe*/);
4285
4286 m->fFileExists = true;
4287 clearDocument();
4288 }
4289 catch (...)
4290 {
4291 clearDocument();
4292 throw;
4293 }
4294}
4295
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