VirtualBox

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

Last change on this file since 23454 was 23334, checked in by vboxsync, 15 years ago

Main: be stricter with bumping settings version to 1.9 when incompatible floppy or DVD settings are detected

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 118.0 KB
Line 
1/** @file
2 * Settings File Manipulation API.
3 */
4
5/*
6 * Copyright (C) 2007 Sun Microsystems, Inc.
7 *
8 * This file is part of VirtualBox Open Source Edition (OSE), as
9 * available from http://www.virtualbox.org. This file is free software;
10 * you can redistribute it and/or modify it under the terms of the GNU
11 * General Public License (GPL) as published by the Free Software
12 * Foundation, in version 2 as it comes in the "COPYING" file of the
13 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
14 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
15 *
16 * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa
17 * Clara, CA 95054 USA or visit http://www.sun.com if you need
18 * additional information or have any questions.
19 */
20
21#include "VBox/com/string.h"
22#include "VBox/settings.h"
23#include <iprt/xml_cpp.h>
24#include <iprt/stream.h>
25#include <iprt/ctype.h>
26#include <iprt/file.h>
27
28// generated header
29#include "SchemaDefs.h"
30
31#include "Logging.h"
32
33using namespace com;
34using namespace settings;
35
36////////////////////////////////////////////////////////////////////////////////
37//
38// Defines
39//
40////////////////////////////////////////////////////////////////////////////////
41
42/** VirtualBox XML settings namespace */
43#define VBOX_XML_NAMESPACE "http://www.innotek.de/VirtualBox-settings"
44
45/** VirtualBox XML settings version number substring ("x.y") */
46#define VBOX_XML_VERSION "1.9"
47
48/** VirtualBox XML settings version platform substring */
49#if defined (RT_OS_DARWIN)
50# define VBOX_XML_PLATFORM "macosx"
51#elif defined (RT_OS_FREEBSD)
52# define VBOX_XML_PLATFORM "freebsd"
53#elif defined (RT_OS_LINUX)
54# define VBOX_XML_PLATFORM "linux"
55#elif defined (RT_OS_NETBSD)
56# define VBOX_XML_PLATFORM "netbsd"
57#elif defined (RT_OS_OPENBSD)
58# define VBOX_XML_PLATFORM "openbsd"
59#elif defined (RT_OS_OS2)
60# define VBOX_XML_PLATFORM "os2"
61#elif defined (RT_OS_SOLARIS)
62# define VBOX_XML_PLATFORM "solaris"
63#elif defined (RT_OS_WINDOWS)
64# define VBOX_XML_PLATFORM "windows"
65#else
66# error Unsupported platform!
67#endif
68
69/** VirtualBox XML settings full version string ("x.y-platform") */
70#define VBOX_XML_VERSION_FULL VBOX_XML_VERSION "-" VBOX_XML_PLATFORM
71
72////////////////////////////////////////////////////////////////////////////////
73//
74// Internal data
75//
76////////////////////////////////////////////////////////////////////////////////
77
78/**
79 * Opaque data structore for ConfigFileBase (only declared
80 * in header, defined only here).
81 */
82
83struct ConfigFileBase::Data
84{
85 Data()
86 : pParser(NULL),
87 pDoc(NULL),
88 pelmRoot(NULL),
89 sv(SettingsVersion_Null),
90 svRead(SettingsVersion_Null)
91 {}
92
93 ~Data()
94 {
95 cleanup();
96 }
97
98 iprt::MiniString strFilename;
99 bool fFileExists;
100
101 xml::XmlFileParser *pParser;
102 xml::Document *pDoc;
103 xml::ElementNode *pelmRoot;
104
105 com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux"
106 SettingsVersion_T sv; // e.g. SettingsVersion_v1_7
107
108 SettingsVersion_T svRead; // settings version that the original file had when it was read,
109 // or SettingsVersion_Null if none
110
111 void cleanup()
112 {
113 if (pDoc)
114 {
115 delete pDoc;
116 pDoc = NULL;
117 pelmRoot = NULL;
118 }
119
120 if (pParser)
121 {
122 delete pParser;
123 pParser = NULL;
124 }
125 }
126};
127
128/**
129 * Private exception class (not in the header file) that makes
130 * throwing xml::LogicError instances easier. That class is public
131 * and should be caught by client code.
132 */
133class settings::ConfigFileError : public xml::LogicError
134{
135public:
136 ConfigFileError(const ConfigFileBase *file,
137 const xml::Node *pNode,
138 const char *pcszFormat, ...)
139 : xml::LogicError()
140 {
141 va_list args;
142 va_start(args, pcszFormat);
143 Utf8StrFmtVA what(pcszFormat, args);
144 va_end(args);
145
146 Utf8Str strLine;
147 if (pNode)
148 strLine = Utf8StrFmt(" (line %RU32)", pNode->getLineNumber());
149
150 const char *pcsz = strLine.c_str();
151 Utf8StrFmt str(N_("Error in %s%s -- %s"),
152 file->m->strFilename.c_str(),
153 (pcsz) ? pcsz : "",
154 what.c_str());
155
156 setWhat(str.c_str());
157 }
158};
159
160////////////////////////////////////////////////////////////////////////////////
161//
162// ConfigFileBase
163//
164////////////////////////////////////////////////////////////////////////////////
165
166/**
167 * Constructor. Allocates the XML internals.
168 * @param strFilename
169 */
170ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
171 : m(new Data)
172{
173 Utf8Str strMajor;
174 Utf8Str strMinor;
175
176 m->fFileExists = false;
177
178 if (pstrFilename)
179 {
180 m->strFilename = *pstrFilename;
181
182 m->pParser = new xml::XmlFileParser;
183 m->pDoc = new xml::Document;
184 m->pParser->read(*pstrFilename,
185 *m->pDoc);
186
187 m->fFileExists = true;
188
189 m->pelmRoot = m->pDoc->getRootElement();
190 if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
191 throw ConfigFileError(this, NULL, N_("Root element in VirtualBox settings files must be \"VirtualBox\"."));
192
193 if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
194 throw ConfigFileError(this, m->pelmRoot, N_("Required VirtualBox/@version attribute is missing"));
195
196 LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
197
198 // parse settings version; allow future versions but fail if file is older than 1.6
199 m->sv = SettingsVersion_Null;
200 if (m->strSettingsVersionFull.length() > 3)
201 {
202 const char *pcsz = m->strSettingsVersionFull.c_str();
203 char c;
204
205 while ( (c = *pcsz)
206 && RT_C_IS_DIGIT(c)
207 )
208 {
209 strMajor.append(c);
210 ++pcsz;
211 }
212
213 if (*pcsz++ == '.')
214 {
215 while ( (c = *pcsz)
216 && RT_C_IS_DIGIT(c)
217 )
218 {
219 strMinor.append(c);
220 ++pcsz;
221 }
222 }
223
224 uint32_t ulMajor = RTStrToUInt32(strMajor.c_str());
225 uint32_t ulMinor = RTStrToUInt32(strMinor.c_str());
226
227 if (ulMajor == 1)
228 {
229 if (ulMinor == 3)
230 m->sv = SettingsVersion_v1_3;
231 else if (ulMinor == 4)
232 m->sv = SettingsVersion_v1_4;
233 else if (ulMinor == 5)
234 m->sv = SettingsVersion_v1_5;
235 else if (ulMinor == 6)
236 m->sv = SettingsVersion_v1_6;
237 else if (ulMinor == 7)
238 m->sv = SettingsVersion_v1_7;
239 else if (ulMinor == 8)
240 m->sv = SettingsVersion_v1_8;
241 else if (ulMinor == 9)
242 m->sv = SettingsVersion_v1_9;
243 else if (ulMinor > 9)
244 m->sv = SettingsVersion_Future;
245 }
246 else if (ulMajor > 1)
247 m->sv = SettingsVersion_Future;
248
249 LogRel(("Parsed settings version %d.%d to enum value %d\n", ulMajor, ulMinor, m->sv));
250 }
251
252 if (m->sv == SettingsVersion_Null)
253 throw ConfigFileError(this, m->pelmRoot, N_("Cannot handle settings version '%s'"), m->strSettingsVersionFull.c_str());
254
255 // remember the settings version we read in case it gets upgraded later,
256 // so we know when to make backups
257 m->svRead = m->sv;
258 }
259 else
260 {
261 m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
262 m->sv = SettingsVersion_v1_9;
263 }
264}
265
266/**
267 * Clean up.
268 */
269ConfigFileBase::~ConfigFileBase()
270{
271 if (m)
272 {
273 delete m;
274 m = NULL;
275 }
276}
277
278/**
279 * Helper function that parses a UUID in string form into
280 * a com::Guid item. Since that uses an IPRT function which
281 * does not accept "{}" characters around the UUID string,
282 * we handle that here. Throws on errors.
283 * @param guid
284 * @param strUUID
285 */
286void ConfigFileBase::parseUUID(Guid &guid,
287 const Utf8Str &strUUID) const
288{
289 // {5f102a55-a51b-48e3-b45a-b28d33469488}
290 // 01234567890123456789012345678901234567
291 // 1 2 3
292 if ( (strUUID[0] == '{')
293 && (strUUID[37] == '}')
294 )
295 guid = strUUID.substr(1, 36).c_str();
296 else
297 guid = strUUID.c_str();
298
299 if (guid.isEmpty())
300 throw ConfigFileError(this, NULL, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
301}
302
303/**
304 * Parses the given string in str and attempts to treat it as an ISO
305 * date/time stamp to put into timestamp. Throws on errors.
306 * @param timestamp
307 * @param str
308 */
309void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
310 const com::Utf8Str &str) const
311{
312 const char *pcsz = str.c_str();
313 // yyyy-mm-ddThh:mm:ss
314 // "2009-07-10T11:54:03Z"
315 // 01234567890123456789
316 // 1
317 if (str.length() > 19)
318 {
319 // timezone must either be unspecified or 'Z' for UTC
320 if ( (pcsz[19])
321 && (pcsz[19] != 'Z')
322 )
323 throw ConfigFileError(this, NULL, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
324
325 int32_t yyyy;
326 uint32_t mm, dd, hh, min, secs;
327 if ( (pcsz[4] == '-')
328 && (pcsz[7] == '-')
329 && (pcsz[10] == 'T')
330 && (pcsz[13] == ':')
331 && (pcsz[16] == ':')
332 )
333 {
334 int rc;
335 if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
336 // could theoretically be negative but let's assume that nobody
337 // created virtual machines before the Christian era
338 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
339 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
340 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
341 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
342 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
343 )
344 {
345 RTTIME time = { yyyy,
346 (uint8_t)mm,
347 0,
348 0,
349 (uint8_t)dd,
350 (uint8_t)hh,
351 (uint8_t)min,
352 (uint8_t)secs,
353 0,
354 RTTIME_FLAGS_TYPE_UTC };
355 if (RTTimeNormalize(&time))
356 if (RTTimeImplode(&timestamp, &time))
357 return;
358 }
359
360 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
361 }
362
363 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
364 }
365}
366
367/**
368 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
369 * @param stamp
370 * @return
371 */
372com::Utf8Str ConfigFileBase::makeString(const RTTIMESPEC &stamp)
373{
374 RTTIME time;
375 if (!RTTimeExplode(&time, &stamp))
376 throw ConfigFileError(this, NULL, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
377
378 return Utf8StrFmt("%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
379 time.i32Year,
380 (uint16_t)time.u8Month,
381 (uint16_t)time.u8MonthDay,
382 (uint16_t)time.u8Hour,
383 (uint16_t)time.u8Minute,
384 (uint16_t)time.u8Second);
385}
386
387/**
388 * Helper to create a string for a GUID.
389 * @param guid
390 * @return
391 */
392com::Utf8Str ConfigFileBase::makeString(const Guid &guid)
393{
394 Utf8Str str("{");
395 str.append(guid.toString());
396 str.append("}");
397 return str;
398}
399
400/**
401 * Helper method to read in an ExtraData subtree and stores its contents
402 * in the given map of extradata items. Used for both main and machine
403 * extradata (MainConfigFile and MachineConfigFile).
404 * @param elmExtraData
405 * @param map
406 */
407void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
408 ExtraDataItemsMap &map)
409{
410 xml::NodesLoop nlLevel4(elmExtraData);
411 const xml::ElementNode *pelmExtraDataItem;
412 while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
413 {
414 if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
415 {
416 // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
417 Utf8Str strName, strValue;
418 if ( ((pelmExtraDataItem->getAttributeValue("name", strName)))
419 && ((pelmExtraDataItem->getAttributeValue("value", strValue)))
420 )
421 map[strName] = strValue;
422 else
423 throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing"));
424 }
425 }
426}
427
428/**
429 * Reads <USBDeviceFilter> entries from under the given elmDeviceFilters node and
430 * stores them in the given linklist. This is in ConfigFileBase because it's used
431 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
432 * filters).
433 * @param elmDeviceFilters
434 * @param ll
435 */
436void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
437 USBDeviceFiltersList &ll)
438{
439 xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
440 const xml::ElementNode *pelmLevel4Child;
441 while ((pelmLevel4Child = nl1.forAllNodes()))
442 {
443 USBDeviceFilter flt;
444 flt.action = USBDeviceFilterAction_Ignore;
445 Utf8Str strAction;
446 if ( (pelmLevel4Child->getAttributeValue("name", flt.strName))
447 && (pelmLevel4Child->getAttributeValue("active", flt.fActive))
448 )
449 {
450 if (!pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId))
451 pelmLevel4Child->getAttributeValue("vendorid", flt.strVendorId); // used before 1.3
452 if (!pelmLevel4Child->getAttributeValue("productId", flt.strProductId))
453 pelmLevel4Child->getAttributeValue("productid", flt.strProductId); // used before 1.3
454 pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
455 pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
456 pelmLevel4Child->getAttributeValue("product", flt.strProduct);
457 if (!pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber))
458 pelmLevel4Child->getAttributeValue("serialnumber", flt.strSerialNumber); // used before 1.3
459 pelmLevel4Child->getAttributeValue("port", flt.strPort);
460
461 // the next 2 are irrelevant for host USB objects
462 pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
463 pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
464
465 // action is only used with host USB objects
466 if (pelmLevel4Child->getAttributeValue("action", strAction))
467 {
468 if (strAction == "Ignore")
469 flt.action = USBDeviceFilterAction_Ignore;
470 else if (strAction == "Hold")
471 flt.action = USBDeviceFilterAction_Hold;
472 else
473 throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str());
474 }
475
476 ll.push_back(flt);
477 }
478 }
479}
480
481/**
482 * Creates a new stub xml::Document in the m->pDoc member with the
483 * root "VirtualBox" element set up. This is used by both
484 * MainConfigFile and MachineConfigFile at the beginning of writing
485 * out their XML.
486 *
487 * Before calling this, it is the responsibility of the caller to
488 * set the "sv" member to the required settings version that is to
489 * be written. For newly created files, the settings version will be
490 * the latest (1.9); for files read in from disk earlier, it will be
491 * the settings version indicated in the file. However, this method
492 * will silently make sure that the settings version is always
493 * at least 1.7 and change it if necessary, since there is no write
494 * support for earlier settings versions.
495 */
496void ConfigFileBase::createStubDocument()
497{
498 Assert(m->pDoc == NULL);
499 m->pDoc = new xml::Document;
500
501 m->pelmRoot = m->pDoc->createRootElement("VirtualBox");
502 m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
503
504 const char *pcszVersion = NULL;
505 switch (m->sv)
506 {
507 case SettingsVersion_v1_8:
508 pcszVersion = "1.8";
509 break;
510
511 case SettingsVersion_v1_9:
512 case SettingsVersion_Future: // can be set if this code runs on XML files that were created by a future version of VBox;
513 // in that case, downgrade to current version when writing since we can't write future versions...
514 pcszVersion = "1.9";
515 m->sv = SettingsVersion_v1_9;
516 break;
517
518 default:
519 // silently upgrade if this is less than 1.7 because that's the oldest we can write
520 pcszVersion = "1.7";
521 m->sv = SettingsVersion_v1_7;
522 break;
523 }
524
525 m->pelmRoot->setAttribute("version", Utf8StrFmt("%s-%s",
526 pcszVersion,
527 VBOX_XML_PLATFORM)); // e.g. "linux"
528
529 // since this gets called before the XML document is actually written out
530 // do this, this is where we must check whether we're upgrading the settings
531 // version and need to make a backup, so the user can go back to an earlier
532 // VirtualBox version and recover his old settings files.
533 if ( (m->svRead != SettingsVersion_Null) // old file exists?
534 && (m->svRead < m->sv) // we're upgrading?
535 )
536 {
537 // compose new filename: strip off trailing ".xml"
538 Utf8Str strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 4);
539 // and append something likd "-1.3-linux.xml"
540 strFilenameNew.append("-");
541 strFilenameNew.append(m->strSettingsVersionFull); // e.g. "1.3-linux"
542 strFilenameNew.append(".xml");
543
544 RTFileMove(m->strFilename.c_str(),
545 strFilenameNew.c_str(),
546 0); // no RTFILEMOVE_FLAGS_REPLACE
547
548 // do this only once
549 m->svRead = SettingsVersion_Null;
550 }
551}
552
553/**
554 * Creates an <ExtraData> node under the given parent element with
555 * <ExtraDataItem> childern according to the contents of the given
556 * map.
557 * This is in ConfigFileBase because it's used in both MainConfigFile
558 * MachineConfigFile, which both can have extradata.
559 *
560 * @param elmParent
561 * @param me
562 */
563void ConfigFileBase::writeExtraData(xml::ElementNode &elmParent,
564 const ExtraDataItemsMap &me)
565{
566 if (me.size())
567 {
568 xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
569 for (ExtraDataItemsMap::const_iterator it = me.begin();
570 it != me.end();
571 ++it)
572 {
573 const Utf8Str &strName = it->first;
574 const Utf8Str &strValue = it->second;
575 xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
576 pelmThis->setAttribute("name", strName);
577 pelmThis->setAttribute("value", strValue);
578 }
579 }
580}
581
582/**
583 * Creates <DeviceFilter> nodes under the given parent element according to
584 * the contents of the given USBDeviceFiltersList. This is in ConfigFileBase
585 * because it's used in both MainConfigFile (for host filters) and
586 * MachineConfigFile (for machine filters).
587 *
588 * If fHostMode is true, this means that we're supposed to write filters
589 * for the IHost interface (respect "action", omit "strRemote" and
590 * "ulMaskedInterfaces" in struct USBDeviceFilter).
591 *
592 * @param elmParent
593 * @param ll
594 * @param fHostMode
595 */
596void ConfigFileBase::writeUSBDeviceFilters(xml::ElementNode &elmParent,
597 const USBDeviceFiltersList &ll,
598 bool fHostMode)
599{
600 for (USBDeviceFiltersList::const_iterator it = ll.begin();
601 it != ll.end();
602 ++it)
603 {
604 const USBDeviceFilter &flt = *it;
605 xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
606 pelmFilter->setAttribute("name", flt.strName);
607 pelmFilter->setAttribute("active", flt.fActive);
608 if (flt.strVendorId.length())
609 pelmFilter->setAttribute("vendorId", flt.strVendorId);
610 if (flt.strProductId.length())
611 pelmFilter->setAttribute("productId", flt.strProductId);
612 if (flt.strRevision.length())
613 pelmFilter->setAttribute("revision", flt.strRevision);
614 if (flt.strManufacturer.length())
615 pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
616 if (flt.strProduct.length())
617 pelmFilter->setAttribute("product", flt.strProduct);
618 if (flt.strSerialNumber.length())
619 pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
620 if (flt.strPort.length())
621 pelmFilter->setAttribute("port", flt.strPort);
622
623 if (fHostMode)
624 {
625 const char *pcsz =
626 (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
627 : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
628 pelmFilter->setAttribute("action", pcsz);
629 }
630 else
631 {
632 if (flt.strRemote.length())
633 pelmFilter->setAttribute("remote", flt.strRemote);
634 if (flt.ulMaskedInterfaces)
635 pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
636 }
637 }
638}
639
640/**
641 * Cleans up memory allocated by the internal XML parser. To be called by
642 * descendant classes when they're done analyzing the DOM tree to discard it.
643 */
644void ConfigFileBase::clearDocument()
645{
646 m->cleanup();
647}
648
649/**
650 * Returns true only if the underlying config file exists on disk;
651 * either because the file has been loaded from disk, or it's been written
652 * to disk, or both.
653 * @return
654 */
655bool ConfigFileBase::fileExists()
656{
657 return m->fFileExists;
658}
659
660
661////////////////////////////////////////////////////////////////////////////////
662//
663// MainConfigFile
664//
665////////////////////////////////////////////////////////////////////////////////
666
667/**
668 * Reads one <MachineEntry> from the main VirtualBox.xml file.
669 * @param elmMachineRegistry
670 */
671void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
672{
673 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
674 xml::NodesLoop nl1(elmMachineRegistry);
675 const xml::ElementNode *pelmChild1;
676 while ((pelmChild1 = nl1.forAllNodes()))
677 {
678 if (pelmChild1->nameEquals("MachineEntry"))
679 {
680 MachineRegistryEntry mre;
681 Utf8Str strUUID;
682 if ( ((pelmChild1->getAttributeValue("uuid", strUUID)))
683 && ((pelmChild1->getAttributeValue("src", mre.strSettingsFile)))
684 )
685 {
686 parseUUID(mre.uuid, strUUID);
687 llMachines.push_back(mre);
688 }
689 else
690 throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing"));
691 }
692 }
693}
694
695/**
696 * Reads a media registry entry from the main VirtualBox.xml file.
697 *
698 * Whereas the current media registry code is fairly straightforward, it was quite a mess
699 * with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements
700 * in the media registry were much more inconsistent, and different elements were used
701 * depending on the type of device and image.
702 *
703 * @param t
704 * @param elmMedium
705 * @param llMedia
706 */
707void MainConfigFile::readMedium(MediaType t,
708 const xml::ElementNode &elmMedium, // HardDisk node if root; if recursing,
709 // child HardDisk node or DiffHardDisk node for pre-1.4
710 MediaList &llMedia) // list to append medium to (root disk or child list)
711{
712 // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
713 settings::Medium med;
714 Utf8Str strUUID;
715 if (!(elmMedium.getAttributeValue("uuid", strUUID)))
716 throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName());
717
718 parseUUID(med.uuid, strUUID);
719
720 bool fNeedsLocation = true;
721
722 if (t == HardDisk)
723 {
724 if (m->sv < SettingsVersion_v1_4)
725 {
726 // here the system is:
727 // <HardDisk uuid="{....}" type="normal">
728 // <VirtualDiskImage filePath="/path/to/xxx.vdi"/>
729 // </HardDisk>
730
731 fNeedsLocation = false;
732 bool fNeedsFilePath = true;
733 const xml::ElementNode *pelmImage;
734 if ((pelmImage = elmMedium.findChildElement("VirtualDiskImage")))
735 med.strFormat = "VDI";
736 else if ((pelmImage = elmMedium.findChildElement("VMDKImage")))
737 med.strFormat = "VMDK";
738 else if ((pelmImage = elmMedium.findChildElement("VHDImage")))
739 med.strFormat = "VHD";
740 else if ((pelmImage = elmMedium.findChildElement("ISCSIHardDisk")))
741 {
742 med.strFormat = "iSCSI";
743
744 fNeedsFilePath = false;
745 // location is special here: current settings specify an "iscsi://user@server:port/target/lun"
746 // string for the location and also have several disk properties for these, whereas this used
747 // to be hidden in several sub-elements before 1.4, so compose a location string and set up
748 // the properties:
749 med.strLocation = "iscsi://";
750 Utf8Str strUser, strServer, strPort, strTarget, strLun;
751 if (pelmImage->getAttributeValue("userName", strUser))
752 {
753 med.strLocation.append(strUser);
754 med.strLocation.append("@");
755 }
756 Utf8Str strServerAndPort;
757 if (pelmImage->getAttributeValue("server", strServer))
758 {
759 strServerAndPort = strServer;
760 }
761 if (pelmImage->getAttributeValue("port", strPort))
762 {
763 if (strServerAndPort.length())
764 strServerAndPort.append(":");
765 strServerAndPort.append(strPort);
766 }
767 med.strLocation.append(strServerAndPort);
768 if (pelmImage->getAttributeValue("target", strTarget))
769 {
770 med.strLocation.append("/");
771 med.strLocation.append(strTarget);
772 }
773 if (pelmImage->getAttributeValue("lun", strLun))
774 {
775 med.strLocation.append("/");
776 med.strLocation.append(strLun);
777 }
778
779 if (strServer.length() && strPort.length())
780 med.properties["TargetAddress"] = strServerAndPort;
781 if (strTarget.length())
782 med.properties["TargetName"] = strTarget;
783 if (strUser.length())
784 med.properties["InitiatorUsername"] = strUser;
785 Utf8Str strPassword;
786 if (pelmImage->getAttributeValue("password", strPassword))
787 med.properties["InitiatorSecret"] = strPassword;
788 if (strLun.length())
789 med.properties["LUN"] = strLun;
790 }
791 else if ((pelmImage = elmMedium.findChildElement("CustomHardDisk")))
792 {
793 fNeedsFilePath = false;
794 fNeedsLocation = true;
795 // also requires @format attribute, which will be queried below
796 }
797 else
798 throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName());
799
800 if (fNeedsFilePath)
801 if (!(pelmImage->getAttributeValue("filePath", med.strLocation)))
802 throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName());
803 }
804
805 if (med.strFormat.isEmpty()) // not set with 1.4 format above, or 1.4 Custom format?
806 if (!(elmMedium.getAttributeValue("format", med.strFormat)))
807 throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName());
808
809 if (!(elmMedium.getAttributeValue("autoReset", med.fAutoReset)))
810 med.fAutoReset = false;
811
812 Utf8Str strType;
813 if ((elmMedium.getAttributeValue("type", strType)))
814 {
815 // pre-1.4 used lower case, so make this case-insensitive
816 strType.toUpper();
817 if (strType == "NORMAL")
818 med.hdType = MediumType_Normal;
819 else if (strType == "IMMUTABLE")
820 med.hdType = MediumType_Immutable;
821 else if (strType == "WRITETHROUGH")
822 med.hdType = MediumType_Writethrough;
823 else
824 throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable or Writethrough"));
825 }
826 }
827 else if (m->sv < SettingsVersion_v1_4)
828 {
829 // DVD and floppy images before 1.4 had "src" attribute instead of "location"
830 if (!(elmMedium.getAttributeValue("src", med.strLocation)))
831 throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName());
832
833 fNeedsLocation = false;
834 }
835
836 if (fNeedsLocation)
837 // current files and 1.4 CustomHardDisk elements must have a location attribute
838 if (!(elmMedium.getAttributeValue("location", med.strLocation)))
839 throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName());
840
841 elmMedium.getAttributeValue("Description", med.strDescription); // optional
842
843 // recurse to handle children
844 xml::NodesLoop nl2(elmMedium);
845 const xml::ElementNode *pelmHDChild;
846 while ((pelmHDChild = nl2.forAllNodes()))
847 {
848 if ( t == HardDisk
849 && ( pelmHDChild->nameEquals("HardDisk")
850 || ( (m->sv < SettingsVersion_v1_4)
851 && (pelmHDChild->nameEquals("DiffHardDisk"))
852 )
853 )
854 )
855 // recurse with this element and push the child onto our current children list
856 readMedium(t,
857 *pelmHDChild,
858 med.llChildren);
859 else if (pelmHDChild->nameEquals("Property"))
860 {
861 Utf8Str strPropName, strPropValue;
862 if ( (pelmHDChild->getAttributeValue("name", strPropName))
863 && (pelmHDChild->getAttributeValue("value", strPropValue))
864 )
865 med.properties[strPropName] = strPropValue;
866 else
867 throw ConfigFileError(this, pelmHDChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
868 }
869 }
870
871 llMedia.push_back(med);
872}
873
874/**
875 * Reads in the entire <MediaRegistry> chunk. For pre-1.4 files, this gets called
876 * with the <DiskRegistry> chunk instead.
877 * @param elmMediaRegistry
878 */
879void MainConfigFile::readMediaRegistry(const xml::ElementNode &elmMediaRegistry)
880{
881 xml::NodesLoop nl1(elmMediaRegistry);
882 const xml::ElementNode *pelmChild1;
883 while ((pelmChild1 = nl1.forAllNodes()))
884 {
885 MediaType t = Error;
886 if (pelmChild1->nameEquals("HardDisks"))
887 t = HardDisk;
888 else if (pelmChild1->nameEquals("DVDImages"))
889 t = DVDImage;
890 else if (pelmChild1->nameEquals("FloppyImages"))
891 t = FloppyImage;
892 else
893 continue;
894
895 xml::NodesLoop nl2(*pelmChild1);
896 const xml::ElementNode *pelmMedium;
897 while ((pelmMedium = nl2.forAllNodes()))
898 {
899 if ( t == HardDisk
900 && (pelmMedium->nameEquals("HardDisk"))
901 )
902 readMedium(t,
903 *pelmMedium,
904 llHardDisks); // list to append hard disk data to: the root list
905 else if ( t == DVDImage
906 && (pelmMedium->nameEquals("Image"))
907 )
908 readMedium(t,
909 *pelmMedium,
910 llDvdImages); // list to append dvd images to: the root list
911 else if ( t == FloppyImage
912 && (pelmMedium->nameEquals("Image"))
913 )
914 readMedium(t,
915 *pelmMedium,
916 llFloppyImages); // list to append floppy images to: the root list
917 }
918 }
919}
920
921/**
922 * Reads in the <DHCPServers> chunk.
923 * @param elmDHCPServers
924 */
925void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
926{
927 xml::NodesLoop nl1(elmDHCPServers);
928 const xml::ElementNode *pelmServer;
929 while ((pelmServer = nl1.forAllNodes()))
930 {
931 if (pelmServer->nameEquals("DHCPServer"))
932 {
933 DHCPServer srv;
934 if ( (pelmServer->getAttributeValue("networkName", srv.strNetworkName))
935 && (pelmServer->getAttributeValue("IPAddress", srv.strIPAddress))
936 && (pelmServer->getAttributeValue("networkMask", srv.strIPNetworkMask))
937 && (pelmServer->getAttributeValue("lowerIP", srv.strIPLower))
938 && (pelmServer->getAttributeValue("upperIP", srv.strIPUpper))
939 && (pelmServer->getAttributeValue("enabled", srv.fEnabled))
940 )
941 llDhcpServers.push_back(srv);
942 else
943 throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing"));
944 }
945 }
946}
947
948/**
949 * Constructor.
950 *
951 * If pstrFilename is != NULL, this reads the given settings file into the member
952 * variables and various substructures and lists. Otherwise, the member variables
953 * are initialized with default values.
954 *
955 * Throws variants of xml::Error for I/O, XML and logical content errors, which
956 * the caller should catch; if this constructor does not throw, then the member
957 * variables contain meaningful values (either from the file or defaults).
958 *
959 * @param strFilename
960 */
961MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
962 : ConfigFileBase(pstrFilename)
963{
964 if (pstrFilename)
965 {
966 // the ConfigFileBase constructor has loaded the XML file, so now
967 // we need only analyze what is in there
968 xml::NodesLoop nlRootChildren(*m->pelmRoot);
969 const xml::ElementNode *pelmRootChild;
970 while ((pelmRootChild = nlRootChildren.forAllNodes()))
971 {
972 if (pelmRootChild->nameEquals("Global"))
973 {
974 xml::NodesLoop nlGlobalChildren(*pelmRootChild);
975 const xml::ElementNode *pelmGlobalChild;
976 while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
977 {
978 if (pelmGlobalChild->nameEquals("SystemProperties"))
979 {
980 pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
981 if (!pelmGlobalChild->getAttributeValue("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder))
982 // pre-1.4 used @defaultVDIFolder instead
983 pelmGlobalChild->getAttributeValue("defaultVDIFolder", systemProperties.strDefaultHardDiskFolder);
984 pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
985 pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
986 pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
987 pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.ulLogHistoryCount);
988 }
989 else if (pelmGlobalChild->nameEquals("ExtraData"))
990 readExtraData(*pelmGlobalChild, mapExtraDataItems);
991 else if (pelmGlobalChild->nameEquals("MachineRegistry"))
992 readMachineRegistry(*pelmGlobalChild);
993 else if ( (pelmGlobalChild->nameEquals("MediaRegistry"))
994 || ( (m->sv < SettingsVersion_v1_4)
995 && (pelmGlobalChild->nameEquals("DiskRegistry"))
996 )
997 )
998 readMediaRegistry(*pelmGlobalChild);
999 else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
1000 {
1001 xml::NodesLoop nlLevel4(*pelmGlobalChild);
1002 const xml::ElementNode *pelmLevel4Child;
1003 while ((pelmLevel4Child = nlLevel4.forAllNodes()))
1004 {
1005 if (pelmLevel4Child->nameEquals("DHCPServers"))
1006 readDHCPServers(*pelmLevel4Child);
1007 }
1008 }
1009 else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
1010 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
1011 }
1012 } // end if (pelmRootChild->nameEquals("Global"))
1013 }
1014
1015 clearDocument();
1016 }
1017
1018 // DHCP servers were introduced with settings version 1.7; if we're loading
1019 // from an older version OR this is a fresh install, then add one DHCP server
1020 // with default settings
1021 if ( (!llDhcpServers.size())
1022 && ( (!pstrFilename) // empty VirtualBox.xml file
1023 || (m->sv < SettingsVersion_v1_7) // upgrading from before 1.7
1024 )
1025 )
1026 {
1027 DHCPServer srv;
1028 srv.strNetworkName =
1029#ifdef RT_OS_WINDOWS
1030 "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
1031#else
1032 "HostInterfaceNetworking-vboxnet0";
1033#endif
1034 srv.strIPAddress = "192.168.56.100";
1035 srv.strIPNetworkMask = "255.255.255.0";
1036 srv.strIPLower = "192.168.56.101";
1037 srv.strIPUpper = "192.168.56.254";
1038 srv.fEnabled = true;
1039 llDhcpServers.push_back(srv);
1040 }
1041}
1042
1043/**
1044 * Creates a single <HardDisk> element for the given Medium structure
1045 * and recurses to write the child hard disks underneath. Called from
1046 * MainConfigFile::write().
1047 *
1048 * @param elmMedium
1049 * @param m
1050 * @param level
1051 */
1052void MainConfigFile::writeHardDisk(xml::ElementNode &elmMedium,
1053 const Medium &m,
1054 uint32_t level) // 0 for "root" call, incremented with each recursion
1055{
1056 xml::ElementNode *pelmHardDisk = elmMedium.createChild("HardDisk");
1057 pelmHardDisk->setAttribute("uuid", makeString(m.uuid));
1058 pelmHardDisk->setAttribute("location", m.strLocation);
1059 pelmHardDisk->setAttribute("format", m.strFormat);
1060 if (m.fAutoReset)
1061 pelmHardDisk->setAttribute("autoReset", m.fAutoReset);
1062 if (m.strDescription.length())
1063 pelmHardDisk->setAttribute("Description", m.strDescription);
1064
1065 for (PropertiesMap::const_iterator it = m.properties.begin();
1066 it != m.properties.end();
1067 ++it)
1068 {
1069 xml::ElementNode *pelmProp = pelmHardDisk->createChild("Property");
1070 pelmProp->setAttribute("name", it->first);
1071 pelmProp->setAttribute("value", it->second);
1072 }
1073
1074 // only for base hard disks, save the type
1075 if (level == 0)
1076 {
1077 const char *pcszType =
1078 m.hdType == MediumType_Normal ? "Normal" :
1079 m.hdType == MediumType_Immutable ? "Immutable" :
1080 /*m.hdType == MediumType_Writethrough ?*/ "Writethrough";
1081 pelmHardDisk->setAttribute("type", pcszType);
1082 }
1083
1084 for (MediaList::const_iterator it = m.llChildren.begin();
1085 it != m.llChildren.end();
1086 ++it)
1087 {
1088 // recurse for children
1089 writeHardDisk(*pelmHardDisk, // parent
1090 *it, // settings::Medium
1091 ++level); // recursion level
1092 }
1093}
1094
1095/**
1096 * Called from the IVirtualBox interface to write out VirtualBox.xml. This
1097 * builds an XML DOM tree and writes it out to disk.
1098 */
1099void MainConfigFile::write(const com::Utf8Str strFilename)
1100{
1101 m->strFilename = strFilename;
1102 createStubDocument();
1103
1104 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
1105
1106 writeExtraData(*pelmGlobal, mapExtraDataItems);
1107
1108 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
1109 for (MachinesRegistry::const_iterator it = llMachines.begin();
1110 it != llMachines.end();
1111 ++it)
1112 {
1113 // <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"/>
1114 const MachineRegistryEntry &mre = *it;
1115 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
1116 pelmMachineEntry->setAttribute("uuid", makeString(mre.uuid));
1117 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
1118 }
1119
1120 xml::ElementNode *pelmMediaRegistry = pelmGlobal->createChild("MediaRegistry");
1121
1122 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
1123 for (MediaList::const_iterator it = llHardDisks.begin();
1124 it != llHardDisks.end();
1125 ++it)
1126 {
1127 writeHardDisk(*pelmHardDisks, *it, 0);
1128 }
1129
1130 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
1131 for (MediaList::const_iterator it = llDvdImages.begin();
1132 it != llDvdImages.end();
1133 ++it)
1134 {
1135 const Medium &m = *it;
1136 xml::ElementNode *pelmMedium = pelmDVDImages->createChild("Image");
1137 pelmMedium->setAttribute("uuid", makeString(m.uuid));
1138 pelmMedium->setAttribute("location", m.strLocation);
1139 if (m.strDescription.length())
1140 pelmMedium->setAttribute("Description", m.strDescription);
1141 }
1142
1143 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
1144 for (MediaList::const_iterator it = llFloppyImages.begin();
1145 it != llFloppyImages.end();
1146 ++it)
1147 {
1148 const Medium &m = *it;
1149 xml::ElementNode *pelmMedium = pelmFloppyImages->createChild("Image");
1150 pelmMedium->setAttribute("uuid", makeString(m.uuid));
1151 pelmMedium->setAttribute("location", m.strLocation);
1152 if (m.strDescription.length())
1153 pelmMedium->setAttribute("Description", m.strDescription);
1154 }
1155
1156 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
1157 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
1158 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
1159 it != llDhcpServers.end();
1160 ++it)
1161 {
1162 const DHCPServer &d = *it;
1163 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
1164 pelmThis->setAttribute("networkName", d.strNetworkName);
1165 pelmThis->setAttribute("IPAddress", d.strIPAddress);
1166 pelmThis->setAttribute("networkMask", d.strIPNetworkMask);
1167 pelmThis->setAttribute("lowerIP", d.strIPLower);
1168 pelmThis->setAttribute("upperIP", d.strIPUpper);
1169 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
1170 }
1171
1172 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
1173 if (systemProperties.strDefaultMachineFolder.length())
1174 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1175 if (systemProperties.strDefaultHardDiskFolder.length())
1176 pelmSysProps->setAttribute("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder);
1177 if (systemProperties.strDefaultHardDiskFormat.length())
1178 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1179 if (systemProperties.strRemoteDisplayAuthLibrary.length())
1180 pelmSysProps->setAttribute("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
1181 if (systemProperties.strWebServiceAuthLibrary.length())
1182 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1183 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.ulLogHistoryCount);
1184
1185 writeUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
1186 host.llUSBDeviceFilters,
1187 true); // fHostMode
1188
1189 // now go write the XML
1190 xml::XmlFileWriter writer(*m->pDoc);
1191 writer.write(m->strFilename.c_str());
1192
1193 m->fFileExists = true;
1194
1195 clearDocument();
1196}
1197
1198/**
1199 * Hardware struct constructor.
1200 */
1201Hardware::Hardware()
1202 : strVersion("2"),
1203 fHardwareVirt(true),
1204 fNestedPaging(false),
1205 fVPID(false),
1206 fPAE(false),
1207 cCPUs(1),
1208 ulMemorySizeMB((uint32_t)-1),
1209 ulVRAMSizeMB(8),
1210 cMonitors(1),
1211 fAccelerate3D(false),
1212 fAccelerate2DVideo(false),
1213 firmwareType(FirmwareType_BIOS),
1214 clipboardMode(ClipboardMode_Bidirectional),
1215 ulMemoryBalloonSize(0),
1216 ulStatisticsUpdateInterval(0)
1217{
1218 mapBootOrder[0] = DeviceType_Floppy;
1219 mapBootOrder[1] = DeviceType_DVD;
1220 mapBootOrder[2] = DeviceType_HardDisk;
1221}
1222
1223
1224/**
1225 * Called from MachineConfigFile::readHardware() to network information.
1226 * @param elmNetwork
1227 * @param ll
1228 */
1229void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
1230 NetworkAdaptersList &ll)
1231{
1232 xml::NodesLoop nl1(elmNetwork, "Adapter");
1233 const xml::ElementNode *pelmAdapter;
1234 while ((pelmAdapter = nl1.forAllNodes()))
1235 {
1236 NetworkAdapter nic;
1237
1238 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
1239 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
1240
1241 Utf8Str strTemp;
1242 if (pelmAdapter->getAttributeValue("type", strTemp))
1243 {
1244 if (strTemp == "Am79C970A")
1245 nic.type = NetworkAdapterType_Am79C970A;
1246 else if (strTemp == "Am79C973")
1247 nic.type = NetworkAdapterType_Am79C973;
1248 else if (strTemp == "82540EM")
1249 nic.type = NetworkAdapterType_I82540EM;
1250 else if (strTemp == "82543GC")
1251 nic.type = NetworkAdapterType_I82543GC;
1252 else if (strTemp == "82545EM")
1253 nic.type = NetworkAdapterType_I82545EM;
1254 else if (strTemp == "virtio")
1255 nic.type = NetworkAdapterType_Virtio;
1256 else
1257 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
1258 }
1259
1260 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
1261 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
1262 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
1263 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
1264 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
1265 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
1266
1267 const xml::ElementNode *pelmAdapterChild;
1268 if ((pelmAdapterChild = pelmAdapter->findChildElement("NAT")))
1269 {
1270 nic.mode = NetworkAttachmentType_NAT;
1271 pelmAdapterChild->getAttributeValue("name", nic.strName); // optional network name
1272 }
1273 else if ( ((pelmAdapterChild = pelmAdapter->findChildElement("HostInterface")))
1274 || ((pelmAdapterChild = pelmAdapter->findChildElement("BridgedInterface")))
1275 )
1276 {
1277 nic.mode = NetworkAttachmentType_Bridged;
1278 pelmAdapterChild->getAttributeValue("name", nic.strName); // optional host interface name
1279 }
1280 else if ((pelmAdapterChild = pelmAdapter->findChildElement("InternalNetwork")))
1281 {
1282 nic.mode = NetworkAttachmentType_Internal;
1283 if (!pelmAdapterChild->getAttributeValue("name", nic.strName)) // required network name
1284 throw ConfigFileError(this, pelmAdapterChild, N_("Required InternalNetwork/@name element is missing"));
1285 }
1286 else if ((pelmAdapterChild = pelmAdapter->findChildElement("HostOnlyInterface")))
1287 {
1288 nic.mode = NetworkAttachmentType_HostOnly;
1289 if (!pelmAdapterChild->getAttributeValue("name", nic.strName)) // required network name
1290 throw ConfigFileError(this, pelmAdapterChild, N_("Required HostOnlyInterface/@name element is missing"));
1291 }
1292 // else: default is NetworkAttachmentType_Null
1293
1294 ll.push_back(nic);
1295 }
1296}
1297
1298/**
1299 * Called from MachineConfigFile::readHardware() to read serial port information.
1300 * @param elmUART
1301 * @param ll
1302 */
1303void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
1304 SerialPortsList &ll)
1305{
1306 xml::NodesLoop nl1(elmUART, "Port");
1307 const xml::ElementNode *pelmPort;
1308 while ((pelmPort = nl1.forAllNodes()))
1309 {
1310 SerialPort port;
1311 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
1312 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
1313
1314 // slot must be unique
1315 for (SerialPortsList::const_iterator it = ll.begin();
1316 it != ll.end();
1317 ++it)
1318 if ((*it).ulSlot == port.ulSlot)
1319 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
1320
1321 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
1322 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
1323 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
1324 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
1325 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
1326 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
1327
1328 Utf8Str strPortMode;
1329 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
1330 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
1331 if (strPortMode == "RawFile")
1332 port.portMode = PortMode_RawFile;
1333 else if (strPortMode == "HostPipe")
1334 port.portMode = PortMode_HostPipe;
1335 else if (strPortMode == "HostDevice")
1336 port.portMode = PortMode_HostDevice;
1337 else if (strPortMode == "Disconnected")
1338 port.portMode = PortMode_Disconnected;
1339 else
1340 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
1341
1342 pelmPort->getAttributeValue("path", port.strPath);
1343 pelmPort->getAttributeValue("server", port.fServer);
1344
1345 ll.push_back(port);
1346 }
1347}
1348
1349/**
1350 * Called from MachineConfigFile::readHardware() to read parallel port information.
1351 * @param elmLPT
1352 * @param ll
1353 */
1354void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
1355 ParallelPortsList &ll)
1356{
1357 xml::NodesLoop nl1(elmLPT, "Port");
1358 const xml::ElementNode *pelmPort;
1359 while ((pelmPort = nl1.forAllNodes()))
1360 {
1361 ParallelPort port;
1362 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
1363 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
1364
1365 // slot must be unique
1366 for (ParallelPortsList::const_iterator it = ll.begin();
1367 it != ll.end();
1368 ++it)
1369 if ((*it).ulSlot == port.ulSlot)
1370 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
1371
1372 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
1373 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
1374 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
1375 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
1376 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
1377 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
1378
1379 pelmPort->getAttributeValue("path", port.strPath);
1380
1381 ll.push_back(port);
1382 }
1383}
1384
1385/**
1386 * Called from MachineConfigFile::readHardware() to read guest property information.
1387 * @param elmGuestProperties
1388 * @param hw
1389 */
1390void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
1391 Hardware &hw)
1392{
1393 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
1394 const xml::ElementNode *pelmProp;
1395 while ((pelmProp = nl1.forAllNodes()))
1396 {
1397 GuestProperty prop;
1398 pelmProp->getAttributeValue("name", prop.strName);
1399 pelmProp->getAttributeValue("value", prop.strValue);
1400
1401 pelmProp->getAttributeValue("timestamp", prop.timestamp);
1402 pelmProp->getAttributeValue("flags", prop.strFlags);
1403 hw.llGuestProperties.push_back(prop);
1404 }
1405
1406 elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
1407}
1408
1409/**
1410 * Helper function to read attributes that are common to <SATAController> (pre-1.7)
1411 * and <StorageController>.
1412 * @param elmStorageController
1413 * @param strg
1414 */
1415void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
1416 StorageController &sctl)
1417{
1418 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
1419 elmStorageController.getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
1420 elmStorageController.getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
1421 elmStorageController.getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
1422 elmStorageController.getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
1423}
1424
1425/**
1426 * Reads in a <Hardware> block and stores it in the given structure. Used
1427 * both directly from readMachine and from readSnapshot, since snapshots
1428 * have their own hardware sections.
1429 *
1430 * For legacy pre-1.7 settings we also need a storage structure because
1431 * the IDE and SATA controllers used to be defined under <Hardware>.
1432 *
1433 * @param elmHardware
1434 * @param hw
1435 */
1436void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
1437 Hardware &hw,
1438 Storage &strg)
1439{
1440 elmHardware.getAttributeValue("version", hw.strVersion);
1441 // defaults to 2 and is only written if != 2
1442
1443 xml::NodesLoop nl1(elmHardware);
1444 const xml::ElementNode *pelmHwChild;
1445 while ((pelmHwChild = nl1.forAllNodes()))
1446 {
1447 if (pelmHwChild->nameEquals("CPU"))
1448 {
1449 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
1450 {
1451 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
1452 const xml::ElementNode *pelmCPUChild;
1453 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
1454 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
1455 }
1456
1457 const xml::ElementNode *pelmCPUChild;
1458 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
1459 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
1460 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
1461 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
1462 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
1463 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
1464 if ((pelmCPUChild = pelmHwChild->findChildElement("PAE")))
1465 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
1466 }
1467 else if (pelmHwChild->nameEquals("Memory"))
1468 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
1469 else if (pelmHwChild->nameEquals("Firmware"))
1470 {
1471 Utf8Str strFirmwareType;
1472 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
1473 {
1474 if ( (strFirmwareType == "BIOS")
1475 || (strFirmwareType == "1") // some trunk builds used the number here
1476 )
1477 hw.firmwareType = FirmwareType_BIOS;
1478 else if ( (strFirmwareType == "EFI")
1479 || (strFirmwareType == "2") // some trunk builds used the number here
1480 )
1481 hw.firmwareType = FirmwareType_EFI;
1482 else
1483 throw ConfigFileError(this,
1484 pelmHwChild,
1485 N_("Invalid value '%s' in Boot/Firmware/@type"),
1486 strFirmwareType.c_str());
1487 }
1488 }
1489 else if (pelmHwChild->nameEquals("Boot"))
1490 {
1491 hw.mapBootOrder.clear();
1492
1493 xml::NodesLoop nl2(*pelmHwChild, "Order");
1494 const xml::ElementNode *pelmOrder;
1495 while ((pelmOrder = nl2.forAllNodes()))
1496 {
1497 uint32_t ulPos;
1498 Utf8Str strDevice;
1499 if (!pelmOrder->getAttributeValue("position", ulPos))
1500 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
1501
1502 if ( ulPos < 1
1503 || ulPos > SchemaDefs::MaxBootPosition
1504 )
1505 throw ConfigFileError(this,
1506 pelmOrder,
1507 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
1508 ulPos,
1509 SchemaDefs::MaxBootPosition + 1);
1510 // XML is 1-based but internal data is 0-based
1511 --ulPos;
1512
1513 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
1514 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
1515
1516 if (!pelmOrder->getAttributeValue("device", strDevice))
1517 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
1518
1519 DeviceType_T type;
1520 if (strDevice == "None")
1521 type = DeviceType_Null;
1522 else if (strDevice == "Floppy")
1523 type = DeviceType_Floppy;
1524 else if (strDevice == "DVD")
1525 type = DeviceType_DVD;
1526 else if (strDevice == "HardDisk")
1527 type = DeviceType_HardDisk;
1528 else if (strDevice == "Network")
1529 type = DeviceType_Network;
1530 else
1531 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
1532 hw.mapBootOrder[ulPos] = type;
1533 }
1534 }
1535 else if (pelmHwChild->nameEquals("Display"))
1536 {
1537 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
1538 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
1539 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
1540 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
1541 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
1542 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
1543 }
1544 else if (pelmHwChild->nameEquals("RemoteDisplay"))
1545 {
1546 pelmHwChild->getAttributeValue("enabled", hw.vrdpSettings.fEnabled);
1547 pelmHwChild->getAttributeValue("port", hw.vrdpSettings.ulPort);
1548 pelmHwChild->getAttributeValue("netAddress", hw.vrdpSettings.strNetAddress);
1549
1550 Utf8Str strAuthType;
1551 if (pelmHwChild->getAttributeValue("authType", strAuthType))
1552 {
1553 // settings before 1.3 used lower case so make sure this is case-insensitive
1554 strAuthType.toUpper();
1555 if (strAuthType == "NULL")
1556 hw.vrdpSettings.authType = VRDPAuthType_Null;
1557 else if (strAuthType == "GUEST")
1558 hw.vrdpSettings.authType = VRDPAuthType_Guest;
1559 else if (strAuthType == "EXTERNAL")
1560 hw.vrdpSettings.authType = VRDPAuthType_External;
1561 else
1562 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
1563 }
1564
1565 pelmHwChild->getAttributeValue("authTimeout", hw.vrdpSettings.ulAuthTimeout);
1566 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
1567 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
1568 }
1569 else if (pelmHwChild->nameEquals("BIOS"))
1570 {
1571 const xml::ElementNode *pelmBIOSChild;
1572 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
1573 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
1574 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
1575 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
1576 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
1577 {
1578 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
1579 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
1580 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
1581 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
1582 }
1583 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
1584 {
1585 Utf8Str strBootMenuMode;
1586 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
1587 {
1588 // settings before 1.3 used lower case so make sure this is case-insensitive
1589 strBootMenuMode.toUpper();
1590 if (strBootMenuMode == "DISABLED")
1591 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
1592 else if (strBootMenuMode == "MENUONLY")
1593 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
1594 else if (strBootMenuMode == "MESSAGEANDMENU")
1595 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
1596 else
1597 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
1598 }
1599 }
1600 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
1601 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
1602 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
1603 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
1604
1605 // legacy BIOS/IDEController (pre 1.7)
1606 if ( (m->sv < SettingsVersion_v1_7)
1607 && ((pelmBIOSChild = pelmHwChild->findChildElement("IDEController")))
1608 )
1609 {
1610 StorageController sctl;
1611 sctl.strName = "IDE Controller";
1612 sctl.storageBus = StorageBus_IDE;
1613
1614 Utf8Str strType;
1615 if (pelmBIOSChild->getAttributeValue("type", strType))
1616 {
1617 if (strType == "PIIX3")
1618 sctl.controllerType = StorageControllerType_PIIX3;
1619 else if (strType == "PIIX4")
1620 sctl.controllerType = StorageControllerType_PIIX4;
1621 else if (strType == "ICH6")
1622 sctl.controllerType = StorageControllerType_ICH6;
1623 else
1624 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
1625 }
1626 sctl.ulPortCount = 2;
1627 strg.llStorageControllers.push_back(sctl);
1628 }
1629 }
1630 else if (pelmHwChild->nameEquals("USBController"))
1631 {
1632 pelmHwChild->getAttributeValue("enabled", hw.usbController.fEnabled);
1633 pelmHwChild->getAttributeValue("enabledEhci", hw.usbController.fEnabledEHCI);
1634
1635 readUSBDeviceFilters(*pelmHwChild,
1636 hw.usbController.llDeviceFilters);
1637 }
1638 else if ( (m->sv < SettingsVersion_v1_7)
1639 && (pelmHwChild->nameEquals("SATAController"))
1640 )
1641 {
1642 bool f;
1643 if ( (pelmHwChild->getAttributeValue("enabled", f))
1644 && (f)
1645 )
1646 {
1647 StorageController sctl;
1648 sctl.strName = "SATA";
1649 sctl.storageBus = StorageBus_SATA;
1650 sctl.controllerType = StorageControllerType_IntelAhci;
1651
1652 readStorageControllerAttributes(*pelmHwChild, sctl);
1653
1654 strg.llStorageControllers.push_back(sctl);
1655 }
1656 }
1657 else if (pelmHwChild->nameEquals("Network"))
1658 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
1659 else if ( (pelmHwChild->nameEquals("UART"))
1660 || (pelmHwChild->nameEquals("Uart")) // used before 1.3
1661 )
1662 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
1663 else if ( (pelmHwChild->nameEquals("LPT"))
1664 || (pelmHwChild->nameEquals("Lpt")) // used before 1.3
1665 )
1666 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
1667 else if (pelmHwChild->nameEquals("AudioAdapter"))
1668 {
1669 pelmHwChild->getAttributeValue("enabled", hw.audioAdapter.fEnabled);
1670
1671 Utf8Str strTemp;
1672 if (pelmHwChild->getAttributeValue("controller", strTemp))
1673 {
1674 if (strTemp == "SB16")
1675 hw.audioAdapter.controllerType = AudioControllerType_SB16;
1676 else if (strTemp == "AC97")
1677 hw.audioAdapter.controllerType = AudioControllerType_AC97;
1678 else
1679 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
1680 }
1681 if (pelmHwChild->getAttributeValue("driver", strTemp))
1682 {
1683 // settings before 1.3 used lower case so make sure this is case-insensitive
1684 strTemp.toUpper();
1685 if (strTemp == "NULL")
1686 hw.audioAdapter.driverType = AudioDriverType_Null;
1687 else if (strTemp == "WINMM")
1688 hw.audioAdapter.driverType = AudioDriverType_WinMM;
1689 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
1690 hw.audioAdapter.driverType = AudioDriverType_DirectSound;
1691 else if (strTemp == "SOLAUDIO")
1692 hw.audioAdapter.driverType = AudioDriverType_SolAudio;
1693 else if (strTemp == "ALSA")
1694 hw.audioAdapter.driverType = AudioDriverType_ALSA;
1695 else if (strTemp == "PULSE")
1696 hw.audioAdapter.driverType = AudioDriverType_Pulse;
1697 else if (strTemp == "OSS")
1698 hw.audioAdapter.driverType = AudioDriverType_OSS;
1699 else if (strTemp == "COREAUDIO")
1700 hw.audioAdapter.driverType = AudioDriverType_CoreAudio;
1701 else if (strTemp == "MMPM")
1702 hw.audioAdapter.driverType = AudioDriverType_MMPM;
1703 else
1704 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
1705 }
1706 }
1707 else if (pelmHwChild->nameEquals("SharedFolders"))
1708 {
1709 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
1710 const xml::ElementNode *pelmFolder;
1711 while ((pelmFolder = nl2.forAllNodes()))
1712 {
1713 SharedFolder sf;
1714 pelmFolder->getAttributeValue("name", sf.strName);
1715 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
1716 pelmFolder->getAttributeValue("writable", sf.fWritable);
1717 hw.llSharedFolders.push_back(sf);
1718 }
1719 }
1720 else if (pelmHwChild->nameEquals("Clipboard"))
1721 {
1722 Utf8Str strTemp;
1723 if (pelmHwChild->getAttributeValue("mode", strTemp))
1724 {
1725 if (strTemp == "Disabled")
1726 hw.clipboardMode = ClipboardMode_Disabled;
1727 else if (strTemp == "HostToGuest")
1728 hw.clipboardMode = ClipboardMode_HostToGuest;
1729 else if (strTemp == "GuestToHost")
1730 hw.clipboardMode = ClipboardMode_GuestToHost;
1731 else if (strTemp == "Bidirectional")
1732 hw.clipboardMode = ClipboardMode_Bidirectional;
1733 else
1734 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipbord/@mode attribute"), strTemp.c_str());
1735 }
1736 }
1737 else if (pelmHwChild->nameEquals("Guest"))
1738 {
1739 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
1740 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
1741 if (!pelmHwChild->getAttributeValue("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval))
1742 pelmHwChild->getAttributeValue("StatisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
1743 }
1744 else if (pelmHwChild->nameEquals("GuestProperties"))
1745 readGuestProperties(*pelmHwChild, hw);
1746 }
1747
1748 if (hw.ulMemorySizeMB == (uint32_t)-1)
1749 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
1750}
1751
1752/**
1753 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
1754 * files which have a <HardDiskAttachments> node and storage controller settings
1755 * hidden in the <Hardware> settings. We set the StorageControllers fields just the
1756 * same, just from different sources.
1757 * @param elmHardware <Hardware> XML node.
1758 * @param elmHardDiskAttachments <HardDiskAttachments> XML node.
1759 * @param strg
1760 */
1761void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
1762 Storage &strg)
1763{
1764 StorageController *pIDEController = NULL;
1765 StorageController *pSATAController = NULL;
1766
1767 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
1768 it != strg.llStorageControllers.end();
1769 ++it)
1770 {
1771 StorageController &s = *it;
1772 if (s.storageBus == StorageBus_IDE)
1773 pIDEController = &s;
1774 else if (s.storageBus == StorageBus_SATA)
1775 pSATAController = &s;
1776 }
1777
1778 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
1779 const xml::ElementNode *pelmAttachment;
1780 while ((pelmAttachment = nl1.forAllNodes()))
1781 {
1782 AttachedDevice att;
1783 Utf8Str strUUID, strBus;
1784
1785 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
1786 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
1787 parseUUID(att.uuid, strUUID);
1788
1789 if (!pelmAttachment->getAttributeValue("bus", strBus))
1790 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
1791 // pre-1.7 'channel' is now port
1792 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
1793 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
1794 // pre-1.7 'device' is still device
1795 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
1796 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
1797
1798 att.deviceType = DeviceType_HardDisk;
1799
1800 if (strBus == "IDE")
1801 {
1802 if (!pIDEController)
1803 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
1804 pIDEController->llAttachedDevices.push_back(att);
1805 }
1806 else if (strBus == "SATA")
1807 {
1808 if (!pSATAController)
1809 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
1810 pSATAController->llAttachedDevices.push_back(att);
1811 }
1812 else
1813 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
1814 }
1815}
1816
1817/**
1818 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
1819 * Used both directly from readMachine and from readSnapshot, since snapshots
1820 * have their own storage controllers sections.
1821 *
1822 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
1823 * for earlier versions.
1824 *
1825 * @param elmStorageControllers
1826 */
1827void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
1828 Storage &strg)
1829{
1830 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
1831 const xml::ElementNode *pelmController;
1832 while ((pelmController = nlStorageControllers.forAllNodes()))
1833 {
1834 StorageController sctl;
1835
1836 if (!pelmController->getAttributeValue("name", sctl.strName))
1837 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
1838 Utf8Str strType;
1839 if (!pelmController->getAttributeValue("type", strType))
1840 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
1841
1842 if (strType == "AHCI")
1843 {
1844 sctl.storageBus = StorageBus_SATA;
1845 sctl.controllerType = StorageControllerType_IntelAhci;
1846 }
1847 else if (strType == "LsiLogic")
1848 {
1849 sctl.storageBus = StorageBus_SCSI;
1850 sctl.controllerType = StorageControllerType_LsiLogic;
1851 }
1852 else if (strType == "BusLogic")
1853 {
1854 sctl.storageBus = StorageBus_SCSI;
1855 sctl.controllerType = StorageControllerType_BusLogic;
1856 }
1857 else if (strType == "PIIX3")
1858 {
1859 sctl.storageBus = StorageBus_IDE;
1860 sctl.controllerType = StorageControllerType_PIIX3;
1861 }
1862 else if (strType == "PIIX4")
1863 {
1864 sctl.storageBus = StorageBus_IDE;
1865 sctl.controllerType = StorageControllerType_PIIX4;
1866 }
1867 else if (strType == "ICH6")
1868 {
1869 sctl.storageBus = StorageBus_IDE;
1870 sctl.controllerType = StorageControllerType_ICH6;
1871 }
1872 else if ( (m->sv >= SettingsVersion_v1_9)
1873 && (strType == "I82078")
1874 )
1875 {
1876 sctl.storageBus = StorageBus_Floppy;
1877 sctl.controllerType = StorageControllerType_I82078;
1878 }
1879 else
1880 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
1881
1882 readStorageControllerAttributes(*pelmController, sctl);
1883
1884 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
1885 const xml::ElementNode *pelmAttached;
1886 while ((pelmAttached = nlAttached.forAllNodes()))
1887 {
1888 AttachedDevice att;
1889 Utf8Str strTemp;
1890 pelmAttached->getAttributeValue("type", strTemp);
1891
1892 if (strTemp == "HardDisk")
1893 att.deviceType = DeviceType_HardDisk;
1894 else if (m->sv >= SettingsVersion_v1_9)
1895 {
1896 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
1897 if (strTemp == "DVD")
1898 {
1899 att.deviceType = DeviceType_DVD;
1900 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
1901 }
1902 else if (strTemp == "Floppy")
1903 att.deviceType = DeviceType_Floppy;
1904 }
1905
1906 if (att.deviceType != DeviceType_Null)
1907 {
1908 const xml::ElementNode *pelmImage;
1909 // all types can have images attached, but for HardDisk it's required
1910 if (!(pelmImage = pelmAttached->findChildElement("Image")))
1911 {
1912 if (att.deviceType == DeviceType_HardDisk)
1913 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
1914 else
1915 {
1916 // DVDs and floppies can also have <HostDrive> instead of <Image>
1917 const xml::ElementNode *pelmHostDrive;
1918 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
1919 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
1920 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
1921 }
1922 }
1923 else
1924 {
1925 if (!pelmImage->getAttributeValue("uuid", strTemp))
1926 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
1927 parseUUID(att.uuid, strTemp);
1928 }
1929
1930 if (!pelmAttached->getAttributeValue("port", att.lPort))
1931 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
1932 if (!pelmAttached->getAttributeValue("device", att.lDevice))
1933 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
1934
1935 sctl.llAttachedDevices.push_back(att);
1936 }
1937 }
1938
1939 strg.llStorageControllers.push_back(sctl);
1940 }
1941}
1942
1943/**
1944 * This gets called for legacy pre-1.9 settings files after having parsed the
1945 * <Hardware> and <StorageControllers> sections to parse <Hardware> once more
1946 * for the <DVDDrive> and <FloppyDrive> sections.
1947 *
1948 * Before settings version 1.9, DVD and floppy drives were specified separately
1949 * under <Hardware>; we then need this extra loop to make sure the storage
1950 * controller structs are already set up so we can add stuff to them.
1951 *
1952 * @param elmHardware
1953 * @param strg
1954 */
1955void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
1956 Storage &strg)
1957{
1958 xml::NodesLoop nl1(elmHardware);
1959 const xml::ElementNode *pelmHwChild;
1960 while ((pelmHwChild = nl1.forAllNodes()))
1961 {
1962 if (pelmHwChild->nameEquals("DVDDrive"))
1963 {
1964 // create a DVD "attached device" and attach it to the existing IDE controller
1965 AttachedDevice att;
1966 att.deviceType = DeviceType_DVD;
1967 // legacy DVD drive is always secondary master (port 1, device 0)
1968 att.lPort = 1;
1969 att.lDevice = 0;
1970 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
1971
1972 const xml::ElementNode *pDriveChild;
1973 Utf8Str strTmp;
1974 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
1975 && (pDriveChild->getAttributeValue("uuid", strTmp))
1976 )
1977 parseUUID(att.uuid, strTmp);
1978 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
1979 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
1980
1981 // find the IDE controller and attach the DVD drive
1982 bool fFound = false;
1983 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
1984 it != strg.llStorageControllers.end();
1985 ++it)
1986 {
1987 StorageController &sctl = *it;
1988 if (sctl.storageBus == StorageBus_IDE)
1989 {
1990 sctl.llAttachedDevices.push_back(att);
1991 fFound = true;
1992 break;
1993 }
1994 }
1995
1996 if (!fFound)
1997 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
1998 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
1999 // which should have gotten parsed in <StorageControllers> before this got called
2000 }
2001 else if (pelmHwChild->nameEquals("FloppyDrive"))
2002 {
2003 bool fEnabled;
2004 if ( (pelmHwChild->getAttributeValue("enabled", fEnabled))
2005 && (fEnabled)
2006 )
2007 {
2008 // create a new floppy controller and attach a floppy "attached device"
2009 StorageController sctl;
2010 sctl.strName = "Floppy Controller";
2011 sctl.storageBus = StorageBus_Floppy;
2012 sctl.controllerType = StorageControllerType_I82078;
2013 sctl.ulPortCount = 1;
2014
2015 AttachedDevice att;
2016 att.deviceType = DeviceType_Floppy;
2017 att.lPort = 0;
2018 att.lDevice = 0;
2019
2020 const xml::ElementNode *pDriveChild;
2021 Utf8Str strTmp;
2022 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2023 && (pDriveChild->getAttributeValue("uuid", strTmp))
2024 )
2025 parseUUID(att.uuid, strTmp);
2026 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2027 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2028
2029 // store attachment with controller
2030 sctl.llAttachedDevices.push_back(att);
2031 // store controller with storage
2032 strg.llStorageControllers.push_back(sctl);
2033 }
2034 }
2035 }
2036}
2037
2038/**
2039 * Called initially for the <Snapshot> element under <Machine>, if present,
2040 * to store the snapshot's data into the given Snapshot structure (which is
2041 * then the one in the Machine struct). This might then recurse if
2042 * a <Snapshots> (plural) element is found in the snapshot, which should
2043 * contain a list of child snapshots; such lists are maintained in the
2044 * Snapshot structure.
2045 *
2046 * @param elmSnapshot
2047 * @param snap
2048 */
2049void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
2050 Snapshot &snap)
2051{
2052 Utf8Str strTemp;
2053
2054 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
2055 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
2056 parseUUID(snap.uuid, strTemp);
2057
2058 if (!elmSnapshot.getAttributeValue("name", snap.strName))
2059 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
2060
2061 // earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
2062 elmSnapshot.getAttributeValue("Description", snap.strDescription);
2063
2064 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
2065 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
2066 parseTimestamp(snap.timestamp, strTemp);
2067
2068 elmSnapshot.getAttributeValue("stateFile", snap.strStateFile); // online snapshots only
2069
2070 // parse Hardware before the other elements because other things depend on it
2071 const xml::ElementNode *pelmHardware;
2072 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
2073 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
2074 readHardware(*pelmHardware, snap.hardware, snap.storage);
2075
2076 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
2077 const xml::ElementNode *pelmSnapshotChild;
2078 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
2079 {
2080 if (pelmSnapshotChild->nameEquals("Description"))
2081 snap.strDescription = pelmSnapshotChild->getValue();
2082 else if ( (m->sv < SettingsVersion_v1_7)
2083 && (pelmSnapshotChild->nameEquals("HardDiskAttachments"))
2084 )
2085 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.storage);
2086 else if ( (m->sv >= SettingsVersion_v1_7)
2087 && (pelmSnapshotChild->nameEquals("StorageControllers"))
2088 )
2089 readStorageControllers(*pelmSnapshotChild, snap.storage);
2090 else if (pelmSnapshotChild->nameEquals("Snapshots"))
2091 {
2092 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
2093 const xml::ElementNode *pelmChildSnapshot;
2094 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
2095 {
2096 if (pelmChildSnapshot->nameEquals("Snapshot"))
2097 {
2098 Snapshot child;
2099 readSnapshot(*pelmChildSnapshot, child);
2100 snap.llChildSnapshots.push_back(child);
2101 }
2102 }
2103 }
2104 }
2105
2106 if (m->sv < SettingsVersion_v1_9)
2107 // go through Hardware once more to repair the settings controller structures
2108 // with data from old DVDDrive and FloppyDrive elements
2109 readDVDAndFloppies_pre1_9(*pelmHardware, snap.storage);
2110}
2111
2112void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
2113{
2114 if (str == "unknown") str = "Other";
2115 else if (str == "dos") str = "DOS";
2116 else if (str == "win31") str = "Windows31";
2117 else if (str == "win95") str = "Windows95";
2118 else if (str == "win98") str = "Windows98";
2119 else if (str == "winme") str = "WindowsMe";
2120 else if (str == "winnt4") str = "WindowsNT4";
2121 else if (str == "win2k") str = "Windows2000";
2122 else if (str == "winxp") str = "WindowsXP";
2123 else if (str == "win2k3") str = "Windows2003";
2124 else if (str == "winvista") str = "WindowsVista";
2125 else if (str == "win2k8") str = "Windows2008";
2126 else if (str == "os2warp3") str = "OS2Warp3";
2127 else if (str == "os2warp4") str = "OS2Warp4";
2128 else if (str == "os2warp45") str = "OS2Warp45";
2129 else if (str == "ecs") str = "OS2eCS";
2130 else if (str == "linux22") str = "Linux22";
2131 else if (str == "linux24") str = "Linux24";
2132 else if (str == "linux26") str = "Linux26";
2133 else if (str == "archlinux") str = "ArchLinux";
2134 else if (str == "debian") str = "Debian";
2135 else if (str == "opensuse") str = "OpenSUSE";
2136 else if (str == "fedoracore") str = "Fedora";
2137 else if (str == "gentoo") str = "Gentoo";
2138 else if (str == "mandriva") str = "Mandriva";
2139 else if (str == "redhat") str = "RedHat";
2140 else if (str == "ubuntu") str = "Ubuntu";
2141 else if (str == "xandros") str = "Xandros";
2142 else if (str == "freebsd") str = "FreeBSD";
2143 else if (str == "openbsd") str = "OpenBSD";
2144 else if (str == "netbsd") str = "NetBSD";
2145 else if (str == "netware") str = "Netware";
2146 else if (str == "solaris") str = "Solaris";
2147 else if (str == "opensolaris") str = "OpenSolaris";
2148 else if (str == "l4") str = "L4";
2149}
2150
2151/**
2152 * Called from the constructor to actually read in the <Machine> element
2153 * of a machine config file.
2154 * @param elmMachine
2155 */
2156void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
2157{
2158 Utf8Str strUUID;
2159 if ( (elmMachine.getAttributeValue("uuid", strUUID))
2160 && (elmMachine.getAttributeValue("name", strName))
2161 )
2162 {
2163 parseUUID(uuid, strUUID);
2164
2165 if (!elmMachine.getAttributeValue("nameSync", fNameSync))
2166 fNameSync = true;
2167
2168 Utf8Str str;
2169 elmMachine.getAttributeValue("Description", strDescription);
2170
2171 elmMachine.getAttributeValue("OSType", strOsType);
2172 if (m->sv < SettingsVersion_v1_5)
2173 convertOldOSType_pre1_5(strOsType);
2174
2175 elmMachine.getAttributeValue("stateFile", strStateFile);
2176 if (elmMachine.getAttributeValue("currentSnapshot", str))
2177 parseUUID(uuidCurrentSnapshot, str);
2178 elmMachine.getAttributeValue("snapshotFolder", strSnapshotFolder);
2179 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
2180 fCurrentStateModified = true;
2181 if (elmMachine.getAttributeValue("lastStateChange", str))
2182 parseTimestamp(timeLastStateChange, str);
2183 // constructor has called RTTimeNow(&timeLastStateChange) before
2184
2185 // parse Hardware before the other elements because other things depend on it
2186 const xml::ElementNode *pelmHardware;
2187 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
2188 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
2189 readHardware(*pelmHardware, hardwareMachine, storageMachine);
2190
2191 xml::NodesLoop nlRootChildren(elmMachine);
2192 const xml::ElementNode *pelmMachineChild;
2193 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
2194 {
2195 if (pelmMachineChild->nameEquals("ExtraData"))
2196 readExtraData(*pelmMachineChild,
2197 mapExtraDataItems);
2198 else if ( (m->sv < SettingsVersion_v1_7)
2199 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
2200 )
2201 readHardDiskAttachments_pre1_7(*pelmMachineChild, storageMachine);
2202 else if ( (m->sv >= SettingsVersion_v1_7)
2203 && (pelmMachineChild->nameEquals("StorageControllers"))
2204 )
2205 readStorageControllers(*pelmMachineChild, storageMachine);
2206 else if (pelmMachineChild->nameEquals("Snapshot"))
2207 {
2208 Snapshot snap;
2209 // this will recurse into child snapshots, if necessary
2210 readSnapshot(*pelmMachineChild, snap);
2211 llFirstSnapshot.push_back(snap);
2212 }
2213 else if (pelmMachineChild->nameEquals("Description"))
2214 strDescription = pelmMachineChild->getValue();
2215 }
2216
2217 if (m->sv < SettingsVersion_v1_9)
2218 // go through Hardware once more to repair the settings controller structures
2219 // with data from old DVDDrive and FloppyDrive elements
2220 readDVDAndFloppies_pre1_9(*pelmHardware, storageMachine);
2221 }
2222 else
2223 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
2224}
2225
2226////////////////////////////////////////////////////////////////////////////////
2227//
2228// MachineConfigFile
2229//
2230////////////////////////////////////////////////////////////////////////////////
2231
2232/**
2233 * Constructor.
2234 *
2235 * If pstrFilename is != NULL, this reads the given settings file into the member
2236 * variables and various substructures and lists. Otherwise, the member variables
2237 * are initialized with default values.
2238 *
2239 * Throws variants of xml::Error for I/O, XML and logical content errors, which
2240 * the caller should catch; if this constructor does not throw, then the member
2241 * variables contain meaningful values (either from the file or defaults).
2242 *
2243 * @param strFilename
2244 */
2245MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
2246 : ConfigFileBase(pstrFilename),
2247 fNameSync(true),
2248 fCurrentStateModified(true),
2249 fAborted(false)
2250{
2251 RTTimeNow(&timeLastStateChange);
2252
2253 if (pstrFilename)
2254 {
2255 // the ConfigFileBase constructor has loaded the XML file, so now
2256 // we need only analyze what is in there
2257
2258 xml::NodesLoop nlRootChildren(*m->pelmRoot);
2259 const xml::ElementNode *pelmRootChild;
2260 while ((pelmRootChild = nlRootChildren.forAllNodes()))
2261 {
2262 if (pelmRootChild->nameEquals("Machine"))
2263 readMachine(*pelmRootChild);
2264 }
2265
2266 // clean up memory allocated by XML engine
2267 clearDocument();
2268 }
2269}
2270
2271/**
2272 * Creates a <Hardware> node under elmParent and then writes out the XML
2273 * keys under that. Called for both the <Machine> node and for snapshots.
2274 * @param elmParent
2275 * @param st
2276 */
2277void MachineConfigFile::writeHardware(xml::ElementNode &elmParent,
2278 const Hardware &hw,
2279 const Storage &strg)
2280{
2281 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
2282
2283 if (hw.strVersion != "2")
2284 pelmHardware->setAttribute("version", hw.strVersion);
2285
2286 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
2287 pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
2288 if (hw.fNestedPaging)
2289 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
2290 if (hw.fVPID)
2291 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
2292 if (hw.fPAE)
2293 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
2294 pelmCPU->setAttribute("count", hw.cCPUs);
2295
2296 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
2297 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
2298
2299 if ( (m->sv >= SettingsVersion_v1_9)
2300 && (hw.firmwareType == FirmwareType_EFI)
2301 )
2302 {
2303 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
2304 pelmFirmware->setAttribute("type", "EFI");
2305 }
2306
2307 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
2308 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
2309 it != hw.mapBootOrder.end();
2310 ++it)
2311 {
2312 uint32_t i = it->first;
2313 DeviceType_T type = it->second;
2314 const char *pcszDevice;
2315
2316 switch (type)
2317 {
2318 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
2319 case DeviceType_DVD: pcszDevice = "DVD"; break;
2320 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
2321 case DeviceType_Network: pcszDevice = "Network"; break;
2322 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
2323 }
2324
2325 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
2326 pelmOrder->setAttribute("position",
2327 i + 1); // XML is 1-based but internal data is 0-based
2328 pelmOrder->setAttribute("device", pcszDevice);
2329 }
2330
2331 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
2332 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
2333 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
2334 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
2335
2336 if (m->sv >= SettingsVersion_v1_8)
2337 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
2338
2339 xml::ElementNode *pelmVRDP = pelmHardware->createChild("RemoteDisplay");
2340 pelmVRDP->setAttribute("enabled", hw.vrdpSettings.fEnabled);
2341 pelmVRDP->setAttribute("port", hw.vrdpSettings.ulPort);
2342 if (hw.vrdpSettings.strNetAddress.length())
2343 pelmVRDP->setAttribute("netAddress", hw.vrdpSettings.strNetAddress);
2344 const char *pcszAuthType;
2345 switch (hw.vrdpSettings.authType)
2346 {
2347 case VRDPAuthType_Guest: pcszAuthType = "Guest"; break;
2348 case VRDPAuthType_External: pcszAuthType = "External"; break;
2349 default: /*case VRDPAuthType_Null:*/ pcszAuthType = "Null"; break;
2350 }
2351 pelmVRDP->setAttribute("authType", pcszAuthType);
2352
2353 if (hw.vrdpSettings.ulAuthTimeout != 0)
2354 pelmVRDP->setAttribute("authTimeout", hw.vrdpSettings.ulAuthTimeout);
2355 if (hw.vrdpSettings.fAllowMultiConnection)
2356 pelmVRDP->setAttribute("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
2357 if (hw.vrdpSettings.fReuseSingleConnection)
2358 pelmVRDP->setAttribute("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
2359
2360 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
2361 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
2362 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
2363
2364 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
2365 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
2366 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
2367 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
2368 if (hw.biosSettings.strLogoImagePath.length())
2369 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
2370
2371 const char *pcszBootMenu;
2372 switch (hw.biosSettings.biosBootMenuMode)
2373 {
2374 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
2375 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
2376 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
2377 }
2378 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
2379 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
2380 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
2381
2382 if (m->sv < SettingsVersion_v1_9)
2383 {
2384 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
2385 // run thru the storage controllers to see if we have a DVD or floppy drives
2386 size_t cDVDs = 0;
2387 size_t cFloppies = 0;
2388
2389 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
2390 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
2391
2392 for (StorageControllersList::const_iterator it = strg.llStorageControllers.begin();
2393 it != strg.llStorageControllers.end();
2394 ++it)
2395 {
2396 const StorageController &sctl = *it;
2397 // in old settings format, the DVD drive could only have been under the IDE controller
2398 if (sctl.storageBus == StorageBus_IDE)
2399 {
2400 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
2401 it2 != sctl.llAttachedDevices.end();
2402 ++it2)
2403 {
2404 const AttachedDevice &att = *it2;
2405 if (att.deviceType == DeviceType_DVD)
2406 {
2407 if (cDVDs > 0)
2408 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
2409
2410 ++cDVDs;
2411
2412 pelmDVD->setAttribute("passthrough", att.fPassThrough);
2413 if (!att.uuid.isEmpty())
2414 pelmDVD->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2415 else if (att.strHostDriveSrc.length())
2416 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2417 }
2418 }
2419 }
2420 else if (sctl.storageBus == StorageBus_Floppy)
2421 {
2422 size_t cFloppiesHere = sctl.llAttachedDevices.size();
2423 if (cFloppiesHere > 1)
2424 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
2425 if (cFloppiesHere)
2426 {
2427 const AttachedDevice &att = sctl.llAttachedDevices.front();
2428 pelmFloppy->setAttribute("enabled", true);
2429 if (!att.uuid.isEmpty())
2430 pelmFloppy->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2431 else if (att.strHostDriveSrc.length())
2432 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2433 }
2434
2435 cFloppies += cFloppiesHere;
2436 }
2437 }
2438
2439 if (cFloppies == 0)
2440 pelmFloppy->setAttribute("enabled", false);
2441 else if (cFloppies > 1)
2442 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
2443 }
2444
2445 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
2446 pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
2447 pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
2448
2449 writeUSBDeviceFilters(*pelmUSB,
2450 hw.usbController.llDeviceFilters,
2451 false); // fHostMode
2452
2453 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
2454 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
2455 it != hw.llNetworkAdapters.end();
2456 ++it)
2457 {
2458 const NetworkAdapter &nic = *it;
2459
2460 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
2461 pelmAdapter->setAttribute("slot", nic.ulSlot);
2462 pelmAdapter->setAttribute("enabled", nic.fEnabled);
2463 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
2464 pelmAdapter->setAttribute("cable", nic.fCableConnected);
2465 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
2466 if (nic.fTraceEnabled)
2467 {
2468 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
2469 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
2470 }
2471
2472 const char *pcszType;
2473 switch (nic.type)
2474 {
2475 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
2476 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
2477 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
2478 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
2479 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
2480 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
2481 }
2482 pelmAdapter->setAttribute("type", pcszType);
2483
2484 xml::ElementNode *pelmNAT;
2485 switch (nic.mode)
2486 {
2487 case NetworkAttachmentType_NAT:
2488 pelmNAT = pelmAdapter->createChild("NAT");
2489 if (nic.strName.length())
2490 pelmNAT->setAttribute("network", nic.strName);
2491 break;
2492
2493 case NetworkAttachmentType_Bridged:
2494 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
2495 break;
2496
2497 case NetworkAttachmentType_Internal:
2498 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
2499 break;
2500
2501 case NetworkAttachmentType_HostOnly:
2502 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
2503 break;
2504
2505 default: /*case NetworkAttachmentType_Null:*/
2506 break;
2507 }
2508 }
2509
2510 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
2511 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
2512 it != hw.llSerialPorts.end();
2513 ++it)
2514 {
2515 const SerialPort &port = *it;
2516 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
2517 pelmPort->setAttribute("slot", port.ulSlot);
2518 pelmPort->setAttribute("enabled", port.fEnabled);
2519 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
2520 pelmPort->setAttribute("IRQ", port.ulIRQ);
2521
2522 const char *pcszHostMode;
2523 switch (port.portMode)
2524 {
2525 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
2526 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
2527 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
2528 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
2529 }
2530 switch (port.portMode)
2531 {
2532 case PortMode_HostPipe:
2533 pelmPort->setAttribute("server", port.fServer);
2534 /* no break */
2535 case PortMode_HostDevice:
2536 case PortMode_RawFile:
2537 pelmPort->setAttribute("path", port.strPath);
2538 break;
2539
2540 default:
2541 break;
2542 }
2543 pelmPort->setAttribute("hostMode", pcszHostMode);
2544 }
2545
2546 pelmPorts = pelmHardware->createChild("LPT");
2547 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
2548 it != hw.llParallelPorts.end();
2549 ++it)
2550 {
2551 const ParallelPort &port = *it;
2552 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
2553 pelmPort->setAttribute("slot", port.ulSlot);
2554 pelmPort->setAttribute("enabled", port.fEnabled);
2555 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
2556 pelmPort->setAttribute("IRQ", port.ulIRQ);
2557 if (port.strPath.length())
2558 pelmPort->setAttribute("path", port.strPath);
2559 }
2560
2561 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
2562 pelmAudio->setAttribute("controller", (hw.audioAdapter.controllerType == AudioControllerType_SB16) ? "SB16" : "AC97");
2563
2564 const char *pcszDriver;
2565 switch (hw.audioAdapter.driverType)
2566 {
2567 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
2568 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
2569 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
2570 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
2571 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
2572 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
2573 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
2574 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
2575 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
2576 }
2577 pelmAudio->setAttribute("driver", pcszDriver);
2578
2579 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
2580
2581 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
2582 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
2583 it != hw.llSharedFolders.end();
2584 ++it)
2585 {
2586 const SharedFolder &sf = *it;
2587 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
2588 pelmThis->setAttribute("name", sf.strName);
2589 pelmThis->setAttribute("hostPath", sf.strHostPath);
2590 pelmThis->setAttribute("writable", sf.fWritable);
2591 }
2592
2593 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
2594 const char *pcszClip;
2595 switch (hw.clipboardMode)
2596 {
2597 case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
2598 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
2599 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
2600 default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
2601 }
2602 pelmClip->setAttribute("mode", pcszClip);
2603
2604 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
2605 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
2606 pelmGuest->setAttribute("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
2607
2608 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
2609 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
2610 it != hw.llGuestProperties.end();
2611 ++it)
2612 {
2613 const GuestProperty &prop = *it;
2614 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
2615 pelmProp->setAttribute("name", prop.strName);
2616 pelmProp->setAttribute("value", prop.strValue);
2617 pelmProp->setAttribute("timestamp", prop.timestamp);
2618 pelmProp->setAttribute("flags", prop.strFlags);
2619 }
2620
2621 if (hw.strNotificationPatterns.length())
2622 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
2623}
2624
2625/**
2626 * Creates a <StorageControllers> node under elmParent and then writes out the XML
2627 * keys under that. Called for both the <Machine> node and for snapshots.
2628 * @param elmParent
2629 * @param st
2630 */
2631void MachineConfigFile::writeStorageControllers(xml::ElementNode &elmParent,
2632 const Storage &st)
2633{
2634 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
2635
2636 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
2637 it != st.llStorageControllers.end();
2638 ++it)
2639 {
2640 const StorageController &sc = *it;
2641
2642 if ( (m->sv < SettingsVersion_v1_9)
2643 && (sc.controllerType == StorageControllerType_I82078)
2644 )
2645 // floppy controller already got written into <Hardware>/<FloppyController> in writeHardware()
2646 // for pre-1.9 settings
2647 continue;
2648
2649 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
2650 pelmController->setAttribute("name", sc.strName);
2651
2652 const char *pcszType;
2653 switch (sc.controllerType)
2654 {
2655 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
2656 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
2657 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
2658 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
2659 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
2660 case StorageControllerType_I82078: pcszType = "I82078"; break;
2661 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
2662 }
2663 pelmController->setAttribute("type", pcszType);
2664
2665 pelmController->setAttribute("PortCount", sc.ulPortCount);
2666
2667 if (sc.controllerType == StorageControllerType_IntelAhci)
2668 {
2669 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
2670 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
2671 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
2672 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
2673 }
2674
2675 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
2676 it2 != sc.llAttachedDevices.end();
2677 ++it2)
2678 {
2679 const AttachedDevice &att = *it2;
2680
2681 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
2682
2683 pcszType = NULL;
2684
2685 switch (att.deviceType)
2686 {
2687 case DeviceType_HardDisk:
2688 pcszType = "HardDisk";
2689 break;
2690
2691 case DeviceType_DVD: // settings format 1.9
2692 if (m->sv >= SettingsVersion_v1_9)
2693 {
2694 pcszType = "DVD";
2695 if (att.fPassThrough)
2696 pelmDevice->setAttribute("passthrough", att.fPassThrough);
2697 }
2698 break;
2699
2700 case DeviceType_Floppy:
2701 if (m->sv >= SettingsVersion_v1_9)
2702 pcszType = "Floppy";
2703 break;
2704 }
2705
2706 if (pcszType) // can be NULL for pre-1.9 settings that shouldn't be written here
2707 {
2708 pelmDevice->setAttribute("type", pcszType);
2709
2710 pelmDevice->setAttribute("port", att.lPort);
2711 pelmDevice->setAttribute("device", att.lDevice);
2712
2713 if (!att.uuid.isEmpty())
2714 pelmDevice->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2715 else if ( (m->sv >= SettingsVersion_v1_9)
2716 && (att.strHostDriveSrc.length())
2717 )
2718 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2719 }
2720 }
2721 }
2722}
2723
2724/**
2725 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
2726 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
2727 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
2728 * @param elmParent
2729 * @param snap
2730 */
2731void MachineConfigFile::writeSnapshot(xml::ElementNode &elmParent,
2732 const Snapshot &snap)
2733{
2734 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
2735
2736 pelmSnapshot->setAttribute("uuid", makeString(snap.uuid));
2737 pelmSnapshot->setAttribute("name", snap.strName);
2738 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
2739
2740 if (snap.strStateFile.length())
2741 pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
2742
2743 if (snap.strDescription.length())
2744 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
2745
2746 writeHardware(*pelmSnapshot, snap.hardware, snap.storage);
2747 writeStorageControllers(*pelmSnapshot, snap.storage);
2748
2749 if (snap.llChildSnapshots.size())
2750 {
2751 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
2752 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
2753 it != snap.llChildSnapshots.end();
2754 ++it)
2755 {
2756 const Snapshot &child = *it;
2757 writeSnapshot(*pelmChildren, child);
2758 }
2759 }
2760}
2761
2762/**
2763 * Called from write() before calling ConfigFileBase::createStubDocument().
2764 * This adjusts the settings version in m->sv if incompatible settings require
2765 * a settings bump, whereas otherwise we try to preserve the settings version
2766 * to avoid breaking compatibility with older versions.
2767 */
2768void MachineConfigFile::bumpSettingsVersionIfNeeded()
2769{
2770 if (m->sv < SettingsVersion_v1_8)
2771 {
2772 // "accelerate 2d video" requires settings version 1.8
2773 if (hardwareMachine.fAccelerate2DVideo)
2774 m->sv = SettingsVersion_v1_8;
2775 }
2776
2777 if (m->sv < SettingsVersion_v1_9)
2778 {
2779 size_t cDVDs = 0;
2780 size_t cFloppies = 0;
2781
2782 // if there is more than one DVD or floppy or the DVD attachment is not
2783 // at the old IDE default, then we need 1.9
2784 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
2785 it != storageMachine.llStorageControllers.end()
2786 && m->sv < SettingsVersion_v1_9;
2787 ++it)
2788 {
2789 const StorageController &sctl = *it;
2790 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
2791 it2 != sctl.llAttachedDevices.end()
2792 && m->sv < SettingsVersion_v1_9;
2793 ++it2)
2794 {
2795 const AttachedDevice &att = *it2;
2796 if (att.deviceType == DeviceType_DVD)
2797 {
2798 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
2799 || (att.lPort != 1) // DVDs not at secondary master?
2800 || (att.lDevice != 0)
2801 )
2802 {
2803 m->sv = SettingsVersion_v1_9;
2804 break;
2805 }
2806
2807 ++cDVDs;
2808 }
2809 else if (att.deviceType == DeviceType_Floppy)
2810 ++cFloppies;
2811 }
2812 }
2813
2814 // VirtualBox before 3.1 had exactly one floppy and exactly one DVD,
2815 // so any deviation from that will require settings version 1.9
2816 if ( (m->sv < SettingsVersion_v1_9)
2817 && ( (cDVDs != 1)
2818 || (cFloppies > 1)
2819 )
2820 )
2821 m->sv = SettingsVersion_v1_9;
2822 }
2823
2824 if ( (m->sv < SettingsVersion_v1_9)
2825 && (hardwareMachine.firmwareType == FirmwareType_EFI)
2826 )
2827 {
2828 m->sv = SettingsVersion_v1_9;
2829 }
2830}
2831
2832/**
2833 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
2834 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
2835 * in particular if the file cannot be written.
2836 */
2837void MachineConfigFile::write(const com::Utf8Str &strFilename)
2838{
2839 try
2840 {
2841 // createStubDocument() sets the settings version to at least 1.7; however,
2842 // we might need to enfore a later settings version if incompatible settings
2843 // are present:
2844 bumpSettingsVersionIfNeeded();
2845
2846 m->strFilename = strFilename;
2847 createStubDocument();
2848
2849 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
2850
2851 pelmMachine->setAttribute("uuid", makeString(uuid));
2852 pelmMachine->setAttribute("name", strName);
2853 if (!fNameSync)
2854 pelmMachine->setAttribute("nameSync", fNameSync);
2855 if (strDescription.length())
2856 pelmMachine->createChild("Description")->addContent(strDescription);
2857 pelmMachine->setAttribute("OSType", strOsType);
2858 if (strStateFile.length())
2859 pelmMachine->setAttribute("stateFile", strStateFile);
2860 if (!uuidCurrentSnapshot.isEmpty())
2861 pelmMachine->setAttribute("currentSnapshot", makeString(uuidCurrentSnapshot));
2862 if (strSnapshotFolder.length())
2863 pelmMachine->setAttribute("snapshotFolder", strSnapshotFolder);
2864 if (!fCurrentStateModified)
2865 pelmMachine->setAttribute("currentStateModified", fCurrentStateModified);
2866 pelmMachine->setAttribute("lastStateChange", makeString(timeLastStateChange));
2867 if (fAborted)
2868 pelmMachine->setAttribute("aborted", fAborted);
2869
2870 writeExtraData(*pelmMachine, mapExtraDataItems);
2871
2872 if (llFirstSnapshot.size())
2873 writeSnapshot(*pelmMachine, llFirstSnapshot.front());
2874
2875 writeHardware(*pelmMachine, hardwareMachine, storageMachine);
2876 writeStorageControllers(*pelmMachine, storageMachine);
2877
2878 // now go write the XML
2879 xml::XmlFileWriter writer(*m->pDoc);
2880 writer.write(m->strFilename.c_str());
2881
2882 m->fFileExists = true;
2883 clearDocument();
2884 }
2885 catch (...)
2886 {
2887 clearDocument();
2888 throw;
2889 }
2890}
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