VirtualBox

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

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

API/Settings: fix writing old settings files (empty attachments were written)

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 121.7 KB
Line 
1/** @file
2 * Settings File Manipulation API.
3 */
4
5/*
6 * Copyright (C) 2007-2009 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#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
1205 fHardwareVirtExclusive(false),
1206#else
1207 fHardwareVirtExclusive(true),
1208#endif
1209 fNestedPaging(false),
1210 fVPID(false),
1211 fSyntheticCpu(false),
1212 fPAE(false),
1213 cCPUs(1),
1214 ulMemorySizeMB((uint32_t)-1),
1215 ulVRAMSizeMB(8),
1216 cMonitors(1),
1217 fAccelerate3D(false),
1218 fAccelerate2DVideo(false),
1219 firmwareType(FirmwareType_BIOS),
1220 clipboardMode(ClipboardMode_Bidirectional),
1221 ulMemoryBalloonSize(0),
1222 ulStatisticsUpdateInterval(0)
1223{
1224 mapBootOrder[0] = DeviceType_Floppy;
1225 mapBootOrder[1] = DeviceType_DVD;
1226 mapBootOrder[2] = DeviceType_HardDisk;
1227}
1228
1229
1230/**
1231 * Called from MachineConfigFile::readHardware() to network information.
1232 * @param elmNetwork
1233 * @param ll
1234 */
1235void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
1236 NetworkAdaptersList &ll)
1237{
1238 xml::NodesLoop nl1(elmNetwork, "Adapter");
1239 const xml::ElementNode *pelmAdapter;
1240 while ((pelmAdapter = nl1.forAllNodes()))
1241 {
1242 NetworkAdapter nic;
1243
1244 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
1245 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
1246
1247 Utf8Str strTemp;
1248 if (pelmAdapter->getAttributeValue("type", strTemp))
1249 {
1250 if (strTemp == "Am79C970A")
1251 nic.type = NetworkAdapterType_Am79C970A;
1252 else if (strTemp == "Am79C973")
1253 nic.type = NetworkAdapterType_Am79C973;
1254 else if (strTemp == "82540EM")
1255 nic.type = NetworkAdapterType_I82540EM;
1256 else if (strTemp == "82543GC")
1257 nic.type = NetworkAdapterType_I82543GC;
1258 else if (strTemp == "82545EM")
1259 nic.type = NetworkAdapterType_I82545EM;
1260 else if (strTemp == "virtio")
1261 nic.type = NetworkAdapterType_Virtio;
1262 else
1263 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
1264 }
1265
1266 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
1267 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
1268 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
1269 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
1270 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
1271 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
1272
1273 const xml::ElementNode *pelmAdapterChild;
1274 if ((pelmAdapterChild = pelmAdapter->findChildElement("NAT")))
1275 {
1276 nic.mode = NetworkAttachmentType_NAT;
1277 pelmAdapterChild->getAttributeValue("name", nic.strName); // optional network name
1278 }
1279 else if ( ((pelmAdapterChild = pelmAdapter->findChildElement("HostInterface")))
1280 || ((pelmAdapterChild = pelmAdapter->findChildElement("BridgedInterface")))
1281 )
1282 {
1283 nic.mode = NetworkAttachmentType_Bridged;
1284 pelmAdapterChild->getAttributeValue("name", nic.strName); // optional host interface name
1285 }
1286 else if ((pelmAdapterChild = pelmAdapter->findChildElement("InternalNetwork")))
1287 {
1288 nic.mode = NetworkAttachmentType_Internal;
1289 if (!pelmAdapterChild->getAttributeValue("name", nic.strName)) // required network name
1290 throw ConfigFileError(this, pelmAdapterChild, N_("Required InternalNetwork/@name element is missing"));
1291 }
1292 else if ((pelmAdapterChild = pelmAdapter->findChildElement("HostOnlyInterface")))
1293 {
1294 nic.mode = NetworkAttachmentType_HostOnly;
1295 if (!pelmAdapterChild->getAttributeValue("name", nic.strName)) // required network name
1296 throw ConfigFileError(this, pelmAdapterChild, N_("Required HostOnlyInterface/@name element is missing"));
1297 }
1298 // else: default is NetworkAttachmentType_Null
1299
1300 ll.push_back(nic);
1301 }
1302}
1303
1304/**
1305 * Called from MachineConfigFile::readHardware() to read serial port information.
1306 * @param elmUART
1307 * @param ll
1308 */
1309void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
1310 SerialPortsList &ll)
1311{
1312 xml::NodesLoop nl1(elmUART, "Port");
1313 const xml::ElementNode *pelmPort;
1314 while ((pelmPort = nl1.forAllNodes()))
1315 {
1316 SerialPort port;
1317 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
1318 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
1319
1320 // slot must be unique
1321 for (SerialPortsList::const_iterator it = ll.begin();
1322 it != ll.end();
1323 ++it)
1324 if ((*it).ulSlot == port.ulSlot)
1325 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
1326
1327 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
1328 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
1329 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
1330 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
1331 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
1332 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
1333
1334 Utf8Str strPortMode;
1335 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
1336 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
1337 if (strPortMode == "RawFile")
1338 port.portMode = PortMode_RawFile;
1339 else if (strPortMode == "HostPipe")
1340 port.portMode = PortMode_HostPipe;
1341 else if (strPortMode == "HostDevice")
1342 port.portMode = PortMode_HostDevice;
1343 else if (strPortMode == "Disconnected")
1344 port.portMode = PortMode_Disconnected;
1345 else
1346 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
1347
1348 pelmPort->getAttributeValue("path", port.strPath);
1349 pelmPort->getAttributeValue("server", port.fServer);
1350
1351 ll.push_back(port);
1352 }
1353}
1354
1355/**
1356 * Called from MachineConfigFile::readHardware() to read parallel port information.
1357 * @param elmLPT
1358 * @param ll
1359 */
1360void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
1361 ParallelPortsList &ll)
1362{
1363 xml::NodesLoop nl1(elmLPT, "Port");
1364 const xml::ElementNode *pelmPort;
1365 while ((pelmPort = nl1.forAllNodes()))
1366 {
1367 ParallelPort port;
1368 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
1369 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
1370
1371 // slot must be unique
1372 for (ParallelPortsList::const_iterator it = ll.begin();
1373 it != ll.end();
1374 ++it)
1375 if ((*it).ulSlot == port.ulSlot)
1376 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
1377
1378 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
1379 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
1380 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
1381 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
1382 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
1383 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
1384
1385 pelmPort->getAttributeValue("path", port.strPath);
1386
1387 ll.push_back(port);
1388 }
1389}
1390
1391/**
1392 * Called from MachineConfigFile::readHardware() to read guest property information.
1393 * @param elmGuestProperties
1394 * @param hw
1395 */
1396void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
1397 Hardware &hw)
1398{
1399 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
1400 const xml::ElementNode *pelmProp;
1401 while ((pelmProp = nl1.forAllNodes()))
1402 {
1403 GuestProperty prop;
1404 pelmProp->getAttributeValue("name", prop.strName);
1405 pelmProp->getAttributeValue("value", prop.strValue);
1406
1407 pelmProp->getAttributeValue("timestamp", prop.timestamp);
1408 pelmProp->getAttributeValue("flags", prop.strFlags);
1409 hw.llGuestProperties.push_back(prop);
1410 }
1411
1412 elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
1413}
1414
1415/**
1416 * Helper function to read attributes that are common to <SATAController> (pre-1.7)
1417 * and <StorageController>.
1418 * @param elmStorageController
1419 * @param strg
1420 */
1421void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
1422 StorageController &sctl)
1423{
1424 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
1425 elmStorageController.getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
1426 elmStorageController.getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
1427 elmStorageController.getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
1428 elmStorageController.getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
1429}
1430
1431/**
1432 * Reads in a <Hardware> block and stores it in the given structure. Used
1433 * both directly from readMachine and from readSnapshot, since snapshots
1434 * have their own hardware sections.
1435 *
1436 * For legacy pre-1.7 settings we also need a storage structure because
1437 * the IDE and SATA controllers used to be defined under <Hardware>.
1438 *
1439 * @param elmHardware
1440 * @param hw
1441 */
1442void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
1443 Hardware &hw,
1444 Storage &strg)
1445{
1446 elmHardware.getAttributeValue("version", hw.strVersion);
1447 // defaults to 2 and is only written if != 2
1448
1449 xml::NodesLoop nl1(elmHardware);
1450 const xml::ElementNode *pelmHwChild;
1451 while ((pelmHwChild = nl1.forAllNodes()))
1452 {
1453 if (pelmHwChild->nameEquals("CPU"))
1454 {
1455 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
1456 {
1457 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
1458 const xml::ElementNode *pelmCPUChild;
1459 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
1460 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
1461 }
1462
1463 const xml::ElementNode *pelmCPUChild;
1464 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
1465 {
1466 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
1467 pelmCPUChild->getAttributeValue("exclusive", hw.fHardwareVirtExclusive);
1468 }
1469 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
1470 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
1471 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
1472 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
1473 if ((pelmCPUChild = pelmHwChild->findChildElement("PAE")))
1474 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
1475 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
1476 pelmCPUChild->getAttributeValue("enabled", hw.fSyntheticCpu);
1477 }
1478 else if (pelmHwChild->nameEquals("Memory"))
1479 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
1480 else if (pelmHwChild->nameEquals("Firmware"))
1481 {
1482 Utf8Str strFirmwareType;
1483 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
1484 {
1485 if ( (strFirmwareType == "BIOS")
1486 || (strFirmwareType == "1") // some trunk builds used the number here
1487 )
1488 hw.firmwareType = FirmwareType_BIOS;
1489 else if ( (strFirmwareType == "EFI")
1490 || (strFirmwareType == "2") // some trunk builds used the number here
1491 )
1492 hw.firmwareType = FirmwareType_EFI;
1493 else
1494 throw ConfigFileError(this,
1495 pelmHwChild,
1496 N_("Invalid value '%s' in Boot/Firmware/@type"),
1497 strFirmwareType.c_str());
1498 }
1499 }
1500 else if (pelmHwChild->nameEquals("Boot"))
1501 {
1502 hw.mapBootOrder.clear();
1503
1504 xml::NodesLoop nl2(*pelmHwChild, "Order");
1505 const xml::ElementNode *pelmOrder;
1506 while ((pelmOrder = nl2.forAllNodes()))
1507 {
1508 uint32_t ulPos;
1509 Utf8Str strDevice;
1510 if (!pelmOrder->getAttributeValue("position", ulPos))
1511 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
1512
1513 if ( ulPos < 1
1514 || ulPos > SchemaDefs::MaxBootPosition
1515 )
1516 throw ConfigFileError(this,
1517 pelmOrder,
1518 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
1519 ulPos,
1520 SchemaDefs::MaxBootPosition + 1);
1521 // XML is 1-based but internal data is 0-based
1522 --ulPos;
1523
1524 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
1525 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
1526
1527 if (!pelmOrder->getAttributeValue("device", strDevice))
1528 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
1529
1530 DeviceType_T type;
1531 if (strDevice == "None")
1532 type = DeviceType_Null;
1533 else if (strDevice == "Floppy")
1534 type = DeviceType_Floppy;
1535 else if (strDevice == "DVD")
1536 type = DeviceType_DVD;
1537 else if (strDevice == "HardDisk")
1538 type = DeviceType_HardDisk;
1539 else if (strDevice == "Network")
1540 type = DeviceType_Network;
1541 else
1542 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
1543 hw.mapBootOrder[ulPos] = type;
1544 }
1545 }
1546 else if (pelmHwChild->nameEquals("Display"))
1547 {
1548 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
1549 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
1550 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
1551 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
1552 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
1553 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
1554 }
1555 else if (pelmHwChild->nameEquals("RemoteDisplay"))
1556 {
1557 pelmHwChild->getAttributeValue("enabled", hw.vrdpSettings.fEnabled);
1558 pelmHwChild->getAttributeValue("port", hw.vrdpSettings.strPort);
1559 pelmHwChild->getAttributeValue("netAddress", hw.vrdpSettings.strNetAddress);
1560
1561 Utf8Str strAuthType;
1562 if (pelmHwChild->getAttributeValue("authType", strAuthType))
1563 {
1564 // settings before 1.3 used lower case so make sure this is case-insensitive
1565 strAuthType.toUpper();
1566 if (strAuthType == "NULL")
1567 hw.vrdpSettings.authType = VRDPAuthType_Null;
1568 else if (strAuthType == "GUEST")
1569 hw.vrdpSettings.authType = VRDPAuthType_Guest;
1570 else if (strAuthType == "EXTERNAL")
1571 hw.vrdpSettings.authType = VRDPAuthType_External;
1572 else
1573 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
1574 }
1575
1576 pelmHwChild->getAttributeValue("authTimeout", hw.vrdpSettings.ulAuthTimeout);
1577 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
1578 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
1579 }
1580 else if (pelmHwChild->nameEquals("BIOS"))
1581 {
1582 const xml::ElementNode *pelmBIOSChild;
1583 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
1584 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
1585 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
1586 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
1587 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
1588 {
1589 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
1590 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
1591 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
1592 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
1593 }
1594 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
1595 {
1596 Utf8Str strBootMenuMode;
1597 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
1598 {
1599 // settings before 1.3 used lower case so make sure this is case-insensitive
1600 strBootMenuMode.toUpper();
1601 if (strBootMenuMode == "DISABLED")
1602 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
1603 else if (strBootMenuMode == "MENUONLY")
1604 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
1605 else if (strBootMenuMode == "MESSAGEANDMENU")
1606 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
1607 else
1608 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
1609 }
1610 }
1611 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
1612 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
1613 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
1614 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
1615
1616 // legacy BIOS/IDEController (pre 1.7)
1617 if ( (m->sv < SettingsVersion_v1_7)
1618 && ((pelmBIOSChild = pelmHwChild->findChildElement("IDEController")))
1619 )
1620 {
1621 StorageController sctl;
1622 sctl.strName = "IDE Controller";
1623 sctl.storageBus = StorageBus_IDE;
1624
1625 Utf8Str strType;
1626 if (pelmBIOSChild->getAttributeValue("type", strType))
1627 {
1628 if (strType == "PIIX3")
1629 sctl.controllerType = StorageControllerType_PIIX3;
1630 else if (strType == "PIIX4")
1631 sctl.controllerType = StorageControllerType_PIIX4;
1632 else if (strType == "ICH6")
1633 sctl.controllerType = StorageControllerType_ICH6;
1634 else
1635 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
1636 }
1637 sctl.ulPortCount = 2;
1638 strg.llStorageControllers.push_back(sctl);
1639 }
1640 }
1641 else if (pelmHwChild->nameEquals("USBController"))
1642 {
1643 pelmHwChild->getAttributeValue("enabled", hw.usbController.fEnabled);
1644 pelmHwChild->getAttributeValue("enabledEhci", hw.usbController.fEnabledEHCI);
1645
1646 readUSBDeviceFilters(*pelmHwChild,
1647 hw.usbController.llDeviceFilters);
1648 }
1649 else if ( (m->sv < SettingsVersion_v1_7)
1650 && (pelmHwChild->nameEquals("SATAController"))
1651 )
1652 {
1653 bool f;
1654 if ( (pelmHwChild->getAttributeValue("enabled", f))
1655 && (f)
1656 )
1657 {
1658 StorageController sctl;
1659 sctl.strName = "SATA Controller";
1660 sctl.storageBus = StorageBus_SATA;
1661 sctl.controllerType = StorageControllerType_IntelAhci;
1662
1663 readStorageControllerAttributes(*pelmHwChild, sctl);
1664
1665 strg.llStorageControllers.push_back(sctl);
1666 }
1667 }
1668 else if (pelmHwChild->nameEquals("Network"))
1669 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
1670 else if ( (pelmHwChild->nameEquals("UART"))
1671 || (pelmHwChild->nameEquals("Uart")) // used before 1.3
1672 )
1673 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
1674 else if ( (pelmHwChild->nameEquals("LPT"))
1675 || (pelmHwChild->nameEquals("Lpt")) // used before 1.3
1676 )
1677 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
1678 else if (pelmHwChild->nameEquals("AudioAdapter"))
1679 {
1680 pelmHwChild->getAttributeValue("enabled", hw.audioAdapter.fEnabled);
1681
1682 Utf8Str strTemp;
1683 if (pelmHwChild->getAttributeValue("controller", strTemp))
1684 {
1685 if (strTemp == "SB16")
1686 hw.audioAdapter.controllerType = AudioControllerType_SB16;
1687 else if (strTemp == "AC97")
1688 hw.audioAdapter.controllerType = AudioControllerType_AC97;
1689 else
1690 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
1691 }
1692 if (pelmHwChild->getAttributeValue("driver", strTemp))
1693 {
1694 // settings before 1.3 used lower case so make sure this is case-insensitive
1695 strTemp.toUpper();
1696 if (strTemp == "NULL")
1697 hw.audioAdapter.driverType = AudioDriverType_Null;
1698 else if (strTemp == "WINMM")
1699 hw.audioAdapter.driverType = AudioDriverType_WinMM;
1700 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
1701 hw.audioAdapter.driverType = AudioDriverType_DirectSound;
1702 else if (strTemp == "SOLAUDIO")
1703 hw.audioAdapter.driverType = AudioDriverType_SolAudio;
1704 else if (strTemp == "ALSA")
1705 hw.audioAdapter.driverType = AudioDriverType_ALSA;
1706 else if (strTemp == "PULSE")
1707 hw.audioAdapter.driverType = AudioDriverType_Pulse;
1708 else if (strTemp == "OSS")
1709 hw.audioAdapter.driverType = AudioDriverType_OSS;
1710 else if (strTemp == "COREAUDIO")
1711 hw.audioAdapter.driverType = AudioDriverType_CoreAudio;
1712 else if (strTemp == "MMPM")
1713 hw.audioAdapter.driverType = AudioDriverType_MMPM;
1714 else
1715 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
1716 }
1717 }
1718 else if (pelmHwChild->nameEquals("SharedFolders"))
1719 {
1720 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
1721 const xml::ElementNode *pelmFolder;
1722 while ((pelmFolder = nl2.forAllNodes()))
1723 {
1724 SharedFolder sf;
1725 pelmFolder->getAttributeValue("name", sf.strName);
1726 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
1727 pelmFolder->getAttributeValue("writable", sf.fWritable);
1728 hw.llSharedFolders.push_back(sf);
1729 }
1730 }
1731 else if (pelmHwChild->nameEquals("Clipboard"))
1732 {
1733 Utf8Str strTemp;
1734 if (pelmHwChild->getAttributeValue("mode", strTemp))
1735 {
1736 if (strTemp == "Disabled")
1737 hw.clipboardMode = ClipboardMode_Disabled;
1738 else if (strTemp == "HostToGuest")
1739 hw.clipboardMode = ClipboardMode_HostToGuest;
1740 else if (strTemp == "GuestToHost")
1741 hw.clipboardMode = ClipboardMode_GuestToHost;
1742 else if (strTemp == "Bidirectional")
1743 hw.clipboardMode = ClipboardMode_Bidirectional;
1744 else
1745 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipbord/@mode attribute"), strTemp.c_str());
1746 }
1747 }
1748 else if (pelmHwChild->nameEquals("Guest"))
1749 {
1750 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
1751 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
1752 if (!pelmHwChild->getAttributeValue("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval))
1753 pelmHwChild->getAttributeValue("StatisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
1754 }
1755 else if (pelmHwChild->nameEquals("GuestProperties"))
1756 readGuestProperties(*pelmHwChild, hw);
1757 }
1758
1759 if (hw.ulMemorySizeMB == (uint32_t)-1)
1760 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
1761}
1762
1763/**
1764 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
1765 * files which have a <HardDiskAttachments> node and storage controller settings
1766 * hidden in the <Hardware> settings. We set the StorageControllers fields just the
1767 * same, just from different sources.
1768 * @param elmHardware <Hardware> XML node.
1769 * @param elmHardDiskAttachments <HardDiskAttachments> XML node.
1770 * @param strg
1771 */
1772void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
1773 Storage &strg)
1774{
1775 StorageController *pIDEController = NULL;
1776 StorageController *pSATAController = NULL;
1777
1778 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
1779 it != strg.llStorageControllers.end();
1780 ++it)
1781 {
1782 StorageController &s = *it;
1783 if (s.storageBus == StorageBus_IDE)
1784 pIDEController = &s;
1785 else if (s.storageBus == StorageBus_SATA)
1786 pSATAController = &s;
1787 }
1788
1789 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
1790 const xml::ElementNode *pelmAttachment;
1791 while ((pelmAttachment = nl1.forAllNodes()))
1792 {
1793 AttachedDevice att;
1794 Utf8Str strUUID, strBus;
1795
1796 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
1797 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
1798 parseUUID(att.uuid, strUUID);
1799
1800 if (!pelmAttachment->getAttributeValue("bus", strBus))
1801 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
1802 // pre-1.7 'channel' is now port
1803 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
1804 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
1805 // pre-1.7 'device' is still device
1806 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
1807 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
1808
1809 att.deviceType = DeviceType_HardDisk;
1810
1811 if (strBus == "IDE")
1812 {
1813 if (!pIDEController)
1814 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
1815 pIDEController->llAttachedDevices.push_back(att);
1816 }
1817 else if (strBus == "SATA")
1818 {
1819 if (!pSATAController)
1820 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
1821 pSATAController->llAttachedDevices.push_back(att);
1822 }
1823 else
1824 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
1825 }
1826}
1827
1828/**
1829 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
1830 * Used both directly from readMachine and from readSnapshot, since snapshots
1831 * have their own storage controllers sections.
1832 *
1833 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
1834 * for earlier versions.
1835 *
1836 * @param elmStorageControllers
1837 */
1838void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
1839 Storage &strg)
1840{
1841 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
1842 const xml::ElementNode *pelmController;
1843 while ((pelmController = nlStorageControllers.forAllNodes()))
1844 {
1845 StorageController sctl;
1846
1847 if (!pelmController->getAttributeValue("name", sctl.strName))
1848 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
1849 // canonicalize storage controller names for configs in the switchover
1850 // period.
1851 if (m->sv <= SettingsVersion_v1_9)
1852 {
1853 if (sctl.strName == "IDE")
1854 sctl.strName = "IDE Controller";
1855 else if (sctl.strName == "SATA")
1856 sctl.strName = "SATA Controller";
1857 }
1858 Utf8Str strType;
1859 if (!pelmController->getAttributeValue("type", strType))
1860 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
1861
1862 if (strType == "AHCI")
1863 {
1864 sctl.storageBus = StorageBus_SATA;
1865 sctl.controllerType = StorageControllerType_IntelAhci;
1866 }
1867 else if (strType == "LsiLogic")
1868 {
1869 sctl.storageBus = StorageBus_SCSI;
1870 sctl.controllerType = StorageControllerType_LsiLogic;
1871 }
1872 else if (strType == "BusLogic")
1873 {
1874 sctl.storageBus = StorageBus_SCSI;
1875 sctl.controllerType = StorageControllerType_BusLogic;
1876 }
1877 else if (strType == "PIIX3")
1878 {
1879 sctl.storageBus = StorageBus_IDE;
1880 sctl.controllerType = StorageControllerType_PIIX3;
1881 }
1882 else if (strType == "PIIX4")
1883 {
1884 sctl.storageBus = StorageBus_IDE;
1885 sctl.controllerType = StorageControllerType_PIIX4;
1886 }
1887 else if (strType == "ICH6")
1888 {
1889 sctl.storageBus = StorageBus_IDE;
1890 sctl.controllerType = StorageControllerType_ICH6;
1891 }
1892 else if ( (m->sv >= SettingsVersion_v1_9)
1893 && (strType == "I82078")
1894 )
1895 {
1896 sctl.storageBus = StorageBus_Floppy;
1897 sctl.controllerType = StorageControllerType_I82078;
1898 }
1899 else
1900 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
1901
1902 readStorageControllerAttributes(*pelmController, sctl);
1903
1904 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
1905 const xml::ElementNode *pelmAttached;
1906 while ((pelmAttached = nlAttached.forAllNodes()))
1907 {
1908 AttachedDevice att;
1909 Utf8Str strTemp;
1910 pelmAttached->getAttributeValue("type", strTemp);
1911
1912 if (strTemp == "HardDisk")
1913 att.deviceType = DeviceType_HardDisk;
1914 else if (m->sv >= SettingsVersion_v1_9)
1915 {
1916 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
1917 if (strTemp == "DVD")
1918 {
1919 att.deviceType = DeviceType_DVD;
1920 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
1921 }
1922 else if (strTemp == "Floppy")
1923 att.deviceType = DeviceType_Floppy;
1924 }
1925
1926 if (att.deviceType != DeviceType_Null)
1927 {
1928 const xml::ElementNode *pelmImage;
1929 // all types can have images attached, but for HardDisk it's required
1930 if (!(pelmImage = pelmAttached->findChildElement("Image")))
1931 {
1932 if (att.deviceType == DeviceType_HardDisk)
1933 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
1934 else
1935 {
1936 // DVDs and floppies can also have <HostDrive> instead of <Image>
1937 const xml::ElementNode *pelmHostDrive;
1938 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
1939 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
1940 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
1941 }
1942 }
1943 else
1944 {
1945 if (!pelmImage->getAttributeValue("uuid", strTemp))
1946 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
1947 parseUUID(att.uuid, strTemp);
1948 }
1949
1950 if (!pelmAttached->getAttributeValue("port", att.lPort))
1951 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
1952 if (!pelmAttached->getAttributeValue("device", att.lDevice))
1953 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
1954
1955 sctl.llAttachedDevices.push_back(att);
1956 }
1957 }
1958
1959 strg.llStorageControllers.push_back(sctl);
1960 }
1961}
1962
1963/**
1964 * This gets called for legacy pre-1.9 settings files after having parsed the
1965 * <Hardware> and <StorageControllers> sections to parse <Hardware> once more
1966 * for the <DVDDrive> and <FloppyDrive> sections.
1967 *
1968 * Before settings version 1.9, DVD and floppy drives were specified separately
1969 * under <Hardware>; we then need this extra loop to make sure the storage
1970 * controller structs are already set up so we can add stuff to them.
1971 *
1972 * @param elmHardware
1973 * @param strg
1974 */
1975void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
1976 Storage &strg)
1977{
1978 xml::NodesLoop nl1(elmHardware);
1979 const xml::ElementNode *pelmHwChild;
1980 while ((pelmHwChild = nl1.forAllNodes()))
1981 {
1982 if (pelmHwChild->nameEquals("DVDDrive"))
1983 {
1984 // create a DVD "attached device" and attach it to the existing IDE controller
1985 AttachedDevice att;
1986 att.deviceType = DeviceType_DVD;
1987 // legacy DVD drive is always secondary master (port 1, device 0)
1988 att.lPort = 1;
1989 att.lDevice = 0;
1990 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
1991
1992 const xml::ElementNode *pDriveChild;
1993 Utf8Str strTmp;
1994 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
1995 && (pDriveChild->getAttributeValue("uuid", strTmp))
1996 )
1997 parseUUID(att.uuid, strTmp);
1998 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
1999 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2000
2001 // find the IDE controller and attach the DVD drive
2002 bool fFound = false;
2003 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
2004 it != strg.llStorageControllers.end();
2005 ++it)
2006 {
2007 StorageController &sctl = *it;
2008 if (sctl.storageBus == StorageBus_IDE)
2009 {
2010 sctl.llAttachedDevices.push_back(att);
2011 fFound = true;
2012 break;
2013 }
2014 }
2015
2016 if (!fFound)
2017 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
2018 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
2019 // which should have gotten parsed in <StorageControllers> before this got called
2020 }
2021 else if (pelmHwChild->nameEquals("FloppyDrive"))
2022 {
2023 bool fEnabled;
2024 if ( (pelmHwChild->getAttributeValue("enabled", fEnabled))
2025 && (fEnabled)
2026 )
2027 {
2028 // create a new floppy controller and attach a floppy "attached device"
2029 StorageController sctl;
2030 sctl.strName = "Floppy Controller";
2031 sctl.storageBus = StorageBus_Floppy;
2032 sctl.controllerType = StorageControllerType_I82078;
2033 sctl.ulPortCount = 1;
2034
2035 AttachedDevice att;
2036 att.deviceType = DeviceType_Floppy;
2037 att.lPort = 0;
2038 att.lDevice = 0;
2039
2040 const xml::ElementNode *pDriveChild;
2041 Utf8Str strTmp;
2042 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2043 && (pDriveChild->getAttributeValue("uuid", strTmp))
2044 )
2045 parseUUID(att.uuid, strTmp);
2046 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2047 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2048
2049 // store attachment with controller
2050 sctl.llAttachedDevices.push_back(att);
2051 // store controller with storage
2052 strg.llStorageControllers.push_back(sctl);
2053 }
2054 }
2055 }
2056}
2057
2058/**
2059 * Called initially for the <Snapshot> element under <Machine>, if present,
2060 * to store the snapshot's data into the given Snapshot structure (which is
2061 * then the one in the Machine struct). This might then recurse if
2062 * a <Snapshots> (plural) element is found in the snapshot, which should
2063 * contain a list of child snapshots; such lists are maintained in the
2064 * Snapshot structure.
2065 *
2066 * @param elmSnapshot
2067 * @param snap
2068 */
2069void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
2070 Snapshot &snap)
2071{
2072 Utf8Str strTemp;
2073
2074 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
2075 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
2076 parseUUID(snap.uuid, strTemp);
2077
2078 if (!elmSnapshot.getAttributeValue("name", snap.strName))
2079 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
2080
2081 // earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
2082 elmSnapshot.getAttributeValue("Description", snap.strDescription);
2083
2084 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
2085 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
2086 parseTimestamp(snap.timestamp, strTemp);
2087
2088 elmSnapshot.getAttributeValue("stateFile", snap.strStateFile); // online snapshots only
2089
2090 // parse Hardware before the other elements because other things depend on it
2091 const xml::ElementNode *pelmHardware;
2092 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
2093 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
2094 readHardware(*pelmHardware, snap.hardware, snap.storage);
2095
2096 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
2097 const xml::ElementNode *pelmSnapshotChild;
2098 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
2099 {
2100 if (pelmSnapshotChild->nameEquals("Description"))
2101 snap.strDescription = pelmSnapshotChild->getValue();
2102 else if ( (m->sv < SettingsVersion_v1_7)
2103 && (pelmSnapshotChild->nameEquals("HardDiskAttachments"))
2104 )
2105 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.storage);
2106 else if ( (m->sv >= SettingsVersion_v1_7)
2107 && (pelmSnapshotChild->nameEquals("StorageControllers"))
2108 )
2109 readStorageControllers(*pelmSnapshotChild, snap.storage);
2110 else if (pelmSnapshotChild->nameEquals("Snapshots"))
2111 {
2112 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
2113 const xml::ElementNode *pelmChildSnapshot;
2114 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
2115 {
2116 if (pelmChildSnapshot->nameEquals("Snapshot"))
2117 {
2118 Snapshot child;
2119 readSnapshot(*pelmChildSnapshot, child);
2120 snap.llChildSnapshots.push_back(child);
2121 }
2122 }
2123 }
2124 }
2125
2126 if (m->sv < SettingsVersion_v1_9)
2127 // go through Hardware once more to repair the settings controller structures
2128 // with data from old DVDDrive and FloppyDrive elements
2129 readDVDAndFloppies_pre1_9(*pelmHardware, snap.storage);
2130}
2131
2132void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
2133{
2134 if (str == "unknown") str = "Other";
2135 else if (str == "dos") str = "DOS";
2136 else if (str == "win31") str = "Windows31";
2137 else if (str == "win95") str = "Windows95";
2138 else if (str == "win98") str = "Windows98";
2139 else if (str == "winme") str = "WindowsMe";
2140 else if (str == "winnt4") str = "WindowsNT4";
2141 else if (str == "win2k") str = "Windows2000";
2142 else if (str == "winxp") str = "WindowsXP";
2143 else if (str == "win2k3") str = "Windows2003";
2144 else if (str == "winvista") str = "WindowsVista";
2145 else if (str == "win2k8") str = "Windows2008";
2146 else if (str == "os2warp3") str = "OS2Warp3";
2147 else if (str == "os2warp4") str = "OS2Warp4";
2148 else if (str == "os2warp45") str = "OS2Warp45";
2149 else if (str == "ecs") str = "OS2eCS";
2150 else if (str == "linux22") str = "Linux22";
2151 else if (str == "linux24") str = "Linux24";
2152 else if (str == "linux26") str = "Linux26";
2153 else if (str == "archlinux") str = "ArchLinux";
2154 else if (str == "debian") str = "Debian";
2155 else if (str == "opensuse") str = "OpenSUSE";
2156 else if (str == "fedoracore") str = "Fedora";
2157 else if (str == "gentoo") str = "Gentoo";
2158 else if (str == "mandriva") str = "Mandriva";
2159 else if (str == "redhat") str = "RedHat";
2160 else if (str == "ubuntu") str = "Ubuntu";
2161 else if (str == "xandros") str = "Xandros";
2162 else if (str == "freebsd") str = "FreeBSD";
2163 else if (str == "openbsd") str = "OpenBSD";
2164 else if (str == "netbsd") str = "NetBSD";
2165 else if (str == "netware") str = "Netware";
2166 else if (str == "solaris") str = "Solaris";
2167 else if (str == "opensolaris") str = "OpenSolaris";
2168 else if (str == "l4") str = "L4";
2169}
2170
2171/**
2172 * Called from the constructor to actually read in the <Machine> element
2173 * of a machine config file.
2174 * @param elmMachine
2175 */
2176void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
2177{
2178 Utf8Str strUUID;
2179 if ( (elmMachine.getAttributeValue("uuid", strUUID))
2180 && (elmMachine.getAttributeValue("name", strName))
2181 )
2182 {
2183 parseUUID(uuid, strUUID);
2184
2185 if (!elmMachine.getAttributeValue("nameSync", fNameSync))
2186 fNameSync = true;
2187
2188 Utf8Str str;
2189 elmMachine.getAttributeValue("Description", strDescription);
2190
2191 elmMachine.getAttributeValue("OSType", strOsType);
2192 if (m->sv < SettingsVersion_v1_5)
2193 convertOldOSType_pre1_5(strOsType);
2194
2195 elmMachine.getAttributeValue("stateFile", strStateFile);
2196 if (elmMachine.getAttributeValue("currentSnapshot", str))
2197 parseUUID(uuidCurrentSnapshot, str);
2198 elmMachine.getAttributeValue("snapshotFolder", strSnapshotFolder);
2199 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
2200 fCurrentStateModified = true;
2201 if (elmMachine.getAttributeValue("lastStateChange", str))
2202 parseTimestamp(timeLastStateChange, str);
2203 // constructor has called RTTimeNow(&timeLastStateChange) before
2204
2205#if 1 /** @todo Teleportation: Obsolete. Remove in a couple of days. */
2206 if (!elmMachine.getAttributeValue("teleporterEnabled", fTeleporterEnabled)
2207 && !elmMachine.getAttributeValue("liveMigrationTarget", fTeleporterEnabled))
2208 fTeleporterEnabled = false;
2209 if (!elmMachine.getAttributeValue("teleporterPort", uTeleporterPort)
2210 && !elmMachine.getAttributeValue("liveMigrationPort", uTeleporterPort))
2211 uTeleporterPort = 0;
2212 if (!elmMachine.getAttributeValue("teleporterAddress", strTeleporterAddress))
2213 strTeleporterAddress = "";
2214 if (!elmMachine.getAttributeValue("teleporterPassword", strTeleporterPassword)
2215 && !elmMachine.getAttributeValue("liveMigrationPassword", strTeleporterPassword))
2216 strTeleporterPassword = "";
2217#endif
2218
2219 // parse Hardware before the other elements because other things depend on it
2220 const xml::ElementNode *pelmHardware;
2221 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
2222 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
2223 readHardware(*pelmHardware, hardwareMachine, storageMachine);
2224
2225 xml::NodesLoop nlRootChildren(elmMachine);
2226 const xml::ElementNode *pelmMachineChild;
2227 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
2228 {
2229 if (pelmMachineChild->nameEquals("ExtraData"))
2230 readExtraData(*pelmMachineChild,
2231 mapExtraDataItems);
2232 else if ( (m->sv < SettingsVersion_v1_7)
2233 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
2234 )
2235 readHardDiskAttachments_pre1_7(*pelmMachineChild, storageMachine);
2236 else if ( (m->sv >= SettingsVersion_v1_7)
2237 && (pelmMachineChild->nameEquals("StorageControllers"))
2238 )
2239 readStorageControllers(*pelmMachineChild, storageMachine);
2240 else if (pelmMachineChild->nameEquals("Snapshot"))
2241 {
2242 Snapshot snap;
2243 // this will recurse into child snapshots, if necessary
2244 readSnapshot(*pelmMachineChild, snap);
2245 llFirstSnapshot.push_back(snap);
2246 }
2247 else if (pelmMachineChild->nameEquals("Description"))
2248 strDescription = pelmMachineChild->getValue();
2249 else if (pelmMachineChild->nameEquals("Teleporter"))
2250 {
2251 if (!pelmMachineChild->getAttributeValue("enabled", fTeleporterEnabled))
2252 fTeleporterEnabled = false;
2253 if (!pelmMachineChild->getAttributeValue("port", uTeleporterPort))
2254 uTeleporterPort = 0;
2255 if (!pelmMachineChild->getAttributeValue("address", strTeleporterAddress))
2256 strTeleporterAddress = "";
2257 if (!pelmMachineChild->getAttributeValue("password", strTeleporterPassword))
2258 strTeleporterPassword = "";
2259 }
2260 }
2261
2262 if (m->sv < SettingsVersion_v1_9)
2263 // go through Hardware once more to repair the settings controller structures
2264 // with data from old DVDDrive and FloppyDrive elements
2265 readDVDAndFloppies_pre1_9(*pelmHardware, storageMachine);
2266 }
2267 else
2268 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
2269}
2270
2271////////////////////////////////////////////////////////////////////////////////
2272//
2273// MachineConfigFile
2274//
2275////////////////////////////////////////////////////////////////////////////////
2276
2277/**
2278 * Constructor.
2279 *
2280 * If pstrFilename is != NULL, this reads the given settings file into the member
2281 * variables and various substructures and lists. Otherwise, the member variables
2282 * are initialized with default values.
2283 *
2284 * Throws variants of xml::Error for I/O, XML and logical content errors, which
2285 * the caller should catch; if this constructor does not throw, then the member
2286 * variables contain meaningful values (either from the file or defaults).
2287 *
2288 * @param strFilename
2289 */
2290MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
2291 : ConfigFileBase(pstrFilename),
2292 fNameSync(true),
2293 fTeleporterEnabled(false),
2294 uTeleporterPort(0),
2295 fCurrentStateModified(true),
2296 fAborted(false)
2297{
2298 RTTimeNow(&timeLastStateChange);
2299
2300 if (pstrFilename)
2301 {
2302 // the ConfigFileBase constructor has loaded the XML file, so now
2303 // we need only analyze what is in there
2304
2305 xml::NodesLoop nlRootChildren(*m->pelmRoot);
2306 const xml::ElementNode *pelmRootChild;
2307 while ((pelmRootChild = nlRootChildren.forAllNodes()))
2308 {
2309 if (pelmRootChild->nameEquals("Machine"))
2310 readMachine(*pelmRootChild);
2311 }
2312
2313 // clean up memory allocated by XML engine
2314 clearDocument();
2315 }
2316}
2317
2318/**
2319 * Creates a <Hardware> node under elmParent and then writes out the XML
2320 * keys under that. Called for both the <Machine> node and for snapshots.
2321 * @param elmParent
2322 * @param st
2323 */
2324void MachineConfigFile::writeHardware(xml::ElementNode &elmParent,
2325 const Hardware &hw,
2326 const Storage &strg)
2327{
2328 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
2329
2330 if (hw.strVersion != "2")
2331 pelmHardware->setAttribute("version", hw.strVersion);
2332
2333 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
2334 xml::ElementNode *pelmHwVirtEx = pelmCPU->createChild("HardwareVirtEx");
2335 pelmHwVirtEx->setAttribute("enabled", hw.fHardwareVirt);
2336 pelmHwVirtEx->setAttribute("exclusive", hw.fHardwareVirtExclusive);
2337 if (hw.fNestedPaging)
2338 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
2339 if (hw.fVPID)
2340 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
2341 if (hw.fPAE)
2342 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
2343 if (hw.fSyntheticCpu)
2344 pelmCPU->createChild("SyntheticCpu")->setAttribute("enabled", hw.fSyntheticCpu);
2345 pelmCPU->setAttribute("count", hw.cCPUs);
2346
2347 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
2348 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
2349
2350 if ( (m->sv >= SettingsVersion_v1_9)
2351 && (hw.firmwareType == FirmwareType_EFI)
2352 )
2353 {
2354 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
2355 pelmFirmware->setAttribute("type", "EFI");
2356 }
2357
2358 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
2359 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
2360 it != hw.mapBootOrder.end();
2361 ++it)
2362 {
2363 uint32_t i = it->first;
2364 DeviceType_T type = it->second;
2365 const char *pcszDevice;
2366
2367 switch (type)
2368 {
2369 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
2370 case DeviceType_DVD: pcszDevice = "DVD"; break;
2371 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
2372 case DeviceType_Network: pcszDevice = "Network"; break;
2373 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
2374 }
2375
2376 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
2377 pelmOrder->setAttribute("position",
2378 i + 1); // XML is 1-based but internal data is 0-based
2379 pelmOrder->setAttribute("device", pcszDevice);
2380 }
2381
2382 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
2383 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
2384 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
2385 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
2386
2387 if (m->sv >= SettingsVersion_v1_8)
2388 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
2389
2390 xml::ElementNode *pelmVRDP = pelmHardware->createChild("RemoteDisplay");
2391 pelmVRDP->setAttribute("enabled", hw.vrdpSettings.fEnabled);
2392 pelmVRDP->setAttribute("port", hw.vrdpSettings.strPort);
2393 if (hw.vrdpSettings.strNetAddress.length())
2394 pelmVRDP->setAttribute("netAddress", hw.vrdpSettings.strNetAddress);
2395 const char *pcszAuthType;
2396 switch (hw.vrdpSettings.authType)
2397 {
2398 case VRDPAuthType_Guest: pcszAuthType = "Guest"; break;
2399 case VRDPAuthType_External: pcszAuthType = "External"; break;
2400 default: /*case VRDPAuthType_Null:*/ pcszAuthType = "Null"; break;
2401 }
2402 pelmVRDP->setAttribute("authType", pcszAuthType);
2403
2404 if (hw.vrdpSettings.ulAuthTimeout != 0)
2405 pelmVRDP->setAttribute("authTimeout", hw.vrdpSettings.ulAuthTimeout);
2406 if (hw.vrdpSettings.fAllowMultiConnection)
2407 pelmVRDP->setAttribute("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
2408 if (hw.vrdpSettings.fReuseSingleConnection)
2409 pelmVRDP->setAttribute("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
2410
2411 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
2412 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
2413 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
2414
2415 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
2416 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
2417 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
2418 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
2419 if (hw.biosSettings.strLogoImagePath.length())
2420 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
2421
2422 const char *pcszBootMenu;
2423 switch (hw.biosSettings.biosBootMenuMode)
2424 {
2425 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
2426 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
2427 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
2428 }
2429 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
2430 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
2431 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
2432
2433 if (m->sv < SettingsVersion_v1_9)
2434 {
2435 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
2436 // run thru the storage controllers to see if we have a DVD or floppy drives
2437 size_t cDVDs = 0;
2438 size_t cFloppies = 0;
2439
2440 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
2441 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
2442
2443 for (StorageControllersList::const_iterator it = strg.llStorageControllers.begin();
2444 it != strg.llStorageControllers.end();
2445 ++it)
2446 {
2447 const StorageController &sctl = *it;
2448 // in old settings format, the DVD drive could only have been under the IDE controller
2449 if (sctl.storageBus == StorageBus_IDE)
2450 {
2451 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
2452 it2 != sctl.llAttachedDevices.end();
2453 ++it2)
2454 {
2455 const AttachedDevice &att = *it2;
2456 if (att.deviceType == DeviceType_DVD)
2457 {
2458 if (cDVDs > 0)
2459 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
2460
2461 ++cDVDs;
2462
2463 pelmDVD->setAttribute("passthrough", att.fPassThrough);
2464 if (!att.uuid.isEmpty())
2465 pelmDVD->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2466 else if (att.strHostDriveSrc.length())
2467 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2468 }
2469 }
2470 }
2471 else if (sctl.storageBus == StorageBus_Floppy)
2472 {
2473 size_t cFloppiesHere = sctl.llAttachedDevices.size();
2474 if (cFloppiesHere > 1)
2475 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
2476 if (cFloppiesHere)
2477 {
2478 const AttachedDevice &att = sctl.llAttachedDevices.front();
2479 pelmFloppy->setAttribute("enabled", true);
2480 if (!att.uuid.isEmpty())
2481 pelmFloppy->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2482 else if (att.strHostDriveSrc.length())
2483 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2484 }
2485
2486 cFloppies += cFloppiesHere;
2487 }
2488 }
2489
2490 if (cFloppies == 0)
2491 pelmFloppy->setAttribute("enabled", false);
2492 else if (cFloppies > 1)
2493 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
2494 }
2495
2496 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
2497 pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
2498 pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
2499
2500 writeUSBDeviceFilters(*pelmUSB,
2501 hw.usbController.llDeviceFilters,
2502 false); // fHostMode
2503
2504 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
2505 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
2506 it != hw.llNetworkAdapters.end();
2507 ++it)
2508 {
2509 const NetworkAdapter &nic = *it;
2510
2511 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
2512 pelmAdapter->setAttribute("slot", nic.ulSlot);
2513 pelmAdapter->setAttribute("enabled", nic.fEnabled);
2514 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
2515 pelmAdapter->setAttribute("cable", nic.fCableConnected);
2516 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
2517 if (nic.fTraceEnabled)
2518 {
2519 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
2520 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
2521 }
2522
2523 const char *pcszType;
2524 switch (nic.type)
2525 {
2526 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
2527 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
2528 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
2529 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
2530 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
2531 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
2532 }
2533 pelmAdapter->setAttribute("type", pcszType);
2534
2535 xml::ElementNode *pelmNAT;
2536 switch (nic.mode)
2537 {
2538 case NetworkAttachmentType_NAT:
2539 pelmNAT = pelmAdapter->createChild("NAT");
2540 if (nic.strName.length())
2541 pelmNAT->setAttribute("network", nic.strName);
2542 break;
2543
2544 case NetworkAttachmentType_Bridged:
2545 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
2546 break;
2547
2548 case NetworkAttachmentType_Internal:
2549 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
2550 break;
2551
2552 case NetworkAttachmentType_HostOnly:
2553 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
2554 break;
2555
2556 default: /*case NetworkAttachmentType_Null:*/
2557 break;
2558 }
2559 }
2560
2561 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
2562 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
2563 it != hw.llSerialPorts.end();
2564 ++it)
2565 {
2566 const SerialPort &port = *it;
2567 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
2568 pelmPort->setAttribute("slot", port.ulSlot);
2569 pelmPort->setAttribute("enabled", port.fEnabled);
2570 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
2571 pelmPort->setAttribute("IRQ", port.ulIRQ);
2572
2573 const char *pcszHostMode;
2574 switch (port.portMode)
2575 {
2576 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
2577 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
2578 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
2579 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
2580 }
2581 switch (port.portMode)
2582 {
2583 case PortMode_HostPipe:
2584 pelmPort->setAttribute("server", port.fServer);
2585 /* no break */
2586 case PortMode_HostDevice:
2587 case PortMode_RawFile:
2588 pelmPort->setAttribute("path", port.strPath);
2589 break;
2590
2591 default:
2592 break;
2593 }
2594 pelmPort->setAttribute("hostMode", pcszHostMode);
2595 }
2596
2597 pelmPorts = pelmHardware->createChild("LPT");
2598 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
2599 it != hw.llParallelPorts.end();
2600 ++it)
2601 {
2602 const ParallelPort &port = *it;
2603 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
2604 pelmPort->setAttribute("slot", port.ulSlot);
2605 pelmPort->setAttribute("enabled", port.fEnabled);
2606 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
2607 pelmPort->setAttribute("IRQ", port.ulIRQ);
2608 if (port.strPath.length())
2609 pelmPort->setAttribute("path", port.strPath);
2610 }
2611
2612 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
2613 pelmAudio->setAttribute("controller", (hw.audioAdapter.controllerType == AudioControllerType_SB16) ? "SB16" : "AC97");
2614
2615 const char *pcszDriver;
2616 switch (hw.audioAdapter.driverType)
2617 {
2618 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
2619 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
2620 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
2621 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
2622 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
2623 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
2624 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
2625 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
2626 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
2627 }
2628 pelmAudio->setAttribute("driver", pcszDriver);
2629
2630 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
2631
2632 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
2633 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
2634 it != hw.llSharedFolders.end();
2635 ++it)
2636 {
2637 const SharedFolder &sf = *it;
2638 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
2639 pelmThis->setAttribute("name", sf.strName);
2640 pelmThis->setAttribute("hostPath", sf.strHostPath);
2641 pelmThis->setAttribute("writable", sf.fWritable);
2642 }
2643
2644 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
2645 const char *pcszClip;
2646 switch (hw.clipboardMode)
2647 {
2648 case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
2649 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
2650 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
2651 default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
2652 }
2653 pelmClip->setAttribute("mode", pcszClip);
2654
2655 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
2656 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
2657 pelmGuest->setAttribute("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
2658
2659 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
2660 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
2661 it != hw.llGuestProperties.end();
2662 ++it)
2663 {
2664 const GuestProperty &prop = *it;
2665 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
2666 pelmProp->setAttribute("name", prop.strName);
2667 pelmProp->setAttribute("value", prop.strValue);
2668 pelmProp->setAttribute("timestamp", prop.timestamp);
2669 pelmProp->setAttribute("flags", prop.strFlags);
2670 }
2671
2672 if (hw.strNotificationPatterns.length())
2673 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
2674}
2675
2676/**
2677 * Creates a <StorageControllers> node under elmParent and then writes out the XML
2678 * keys under that. Called for both the <Machine> node and for snapshots.
2679 * @param elmParent
2680 * @param st
2681 */
2682void MachineConfigFile::writeStorageControllers(xml::ElementNode &elmParent,
2683 const Storage &st)
2684{
2685 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
2686
2687 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
2688 it != st.llStorageControllers.end();
2689 ++it)
2690 {
2691 const StorageController &sc = *it;
2692
2693 if ( (m->sv < SettingsVersion_v1_9)
2694 && (sc.controllerType == StorageControllerType_I82078)
2695 )
2696 // floppy controller already got written into <Hardware>/<FloppyController> in writeHardware()
2697 // for pre-1.9 settings
2698 continue;
2699
2700 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
2701 com::Utf8Str name = sc.strName.raw();
2702 //
2703 if (m->sv < SettingsVersion_v1_8)
2704 {
2705 // pre-1.8 settings use shorter controller names, they are
2706 // expanded when reading the settings
2707 if (name == "IDE Controller")
2708 name = "IDE";
2709 else if (name == "SATA Controller")
2710 name = "SATA";
2711 }
2712 pelmController->setAttribute("name", sc.strName);
2713
2714 const char *pcszType;
2715 switch (sc.controllerType)
2716 {
2717 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
2718 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
2719 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
2720 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
2721 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
2722 case StorageControllerType_I82078: pcszType = "I82078"; break;
2723 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
2724 }
2725 pelmController->setAttribute("type", pcszType);
2726
2727 pelmController->setAttribute("PortCount", sc.ulPortCount);
2728
2729 if (sc.controllerType == StorageControllerType_IntelAhci)
2730 {
2731 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
2732 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
2733 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
2734 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
2735 }
2736
2737 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
2738 it2 != sc.llAttachedDevices.end();
2739 ++it2)
2740 {
2741 const AttachedDevice &att = *it2;
2742
2743 /* DVD/Floppy is handled already for settings version before 1.8 */
2744 if ( att.deviceType == DeviceType_DVD
2745 && m->sv <= SettingsVersion_v1_8)
2746 continue;
2747
2748 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
2749
2750 pcszType = NULL;
2751
2752 switch (att.deviceType)
2753 {
2754 case DeviceType_HardDisk:
2755 pcszType = "HardDisk";
2756 break;
2757
2758 case DeviceType_DVD:
2759 pcszType = "DVD";
2760 if (att.fPassThrough)
2761 pelmDevice->setAttribute("passthrough", att.fPassThrough);
2762 break;
2763
2764 case DeviceType_Floppy:
2765 pcszType = "Floppy";
2766 break;
2767 }
2768
2769 pelmDevice->setAttribute("type", pcszType);
2770
2771 pelmDevice->setAttribute("port", att.lPort);
2772 pelmDevice->setAttribute("device", att.lDevice);
2773
2774 if (!att.uuid.isEmpty())
2775 pelmDevice->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2776 else if ( (m->sv >= SettingsVersion_v1_9)
2777 && (att.strHostDriveSrc.length())
2778 )
2779 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2780 }
2781 }
2782}
2783
2784/**
2785 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
2786 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
2787 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
2788 * @param elmParent
2789 * @param snap
2790 */
2791void MachineConfigFile::writeSnapshot(xml::ElementNode &elmParent,
2792 const Snapshot &snap)
2793{
2794 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
2795
2796 pelmSnapshot->setAttribute("uuid", makeString(snap.uuid));
2797 pelmSnapshot->setAttribute("name", snap.strName);
2798 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
2799
2800 if (snap.strStateFile.length())
2801 pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
2802
2803 if (snap.strDescription.length())
2804 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
2805
2806 writeHardware(*pelmSnapshot, snap.hardware, snap.storage);
2807 writeStorageControllers(*pelmSnapshot, snap.storage);
2808
2809 if (snap.llChildSnapshots.size())
2810 {
2811 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
2812 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
2813 it != snap.llChildSnapshots.end();
2814 ++it)
2815 {
2816 const Snapshot &child = *it;
2817 writeSnapshot(*pelmChildren, child);
2818 }
2819 }
2820}
2821
2822/**
2823 * Called from write() before calling ConfigFileBase::createStubDocument().
2824 * This adjusts the settings version in m->sv if incompatible settings require
2825 * a settings bump, whereas otherwise we try to preserve the settings version
2826 * to avoid breaking compatibility with older versions.
2827 */
2828void MachineConfigFile::bumpSettingsVersionIfNeeded()
2829{
2830 if (m->sv < SettingsVersion_v1_8)
2831 {
2832 // "accelerate 2d video" requires settings version 1.8
2833 if (hardwareMachine.fAccelerate2DVideo)
2834 m->sv = SettingsVersion_v1_8;
2835 }
2836
2837 if (m->sv < SettingsVersion_v1_9)
2838 {
2839 size_t cDVDs = 0;
2840 size_t cFloppies = 0;
2841
2842 // if there is more than one DVD or floppy or the DVD attachment is not
2843 // at the old IDE default, then we need 1.9
2844 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
2845 it != storageMachine.llStorageControllers.end()
2846 && m->sv < SettingsVersion_v1_9;
2847 ++it)
2848 {
2849 const StorageController &sctl = *it;
2850 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
2851 it2 != sctl.llAttachedDevices.end()
2852 && m->sv < SettingsVersion_v1_9;
2853 ++it2)
2854 {
2855 const AttachedDevice &att = *it2;
2856 if (att.deviceType == DeviceType_DVD)
2857 {
2858 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
2859 || (att.lPort != 1) // DVDs not at secondary master?
2860 || (att.lDevice != 0)
2861 )
2862 {
2863 m->sv = SettingsVersion_v1_9;
2864 break;
2865 }
2866
2867 ++cDVDs;
2868 }
2869 else if (att.deviceType == DeviceType_Floppy)
2870 ++cFloppies;
2871 }
2872 }
2873
2874 // VirtualBox before 3.1 had exactly one floppy and exactly one DVD,
2875 // so any deviation from that will require settings version 1.9
2876 if ( (m->sv < SettingsVersion_v1_9)
2877 && ( (cDVDs != 1)
2878 || (cFloppies > 1)
2879 )
2880 )
2881 m->sv = SettingsVersion_v1_9;
2882 }
2883
2884 if ( (m->sv < SettingsVersion_v1_9)
2885 && (hardwareMachine.firmwareType == FirmwareType_EFI)
2886 )
2887 {
2888 m->sv = SettingsVersion_v1_9;
2889 }
2890
2891 if ( m->sv < SettingsVersion_v1_9
2892 && ( fTeleporterEnabled
2893 || uTeleporterPort
2894 || !strTeleporterAddress.isEmpty()
2895 || !strTeleporterPassword.isEmpty()
2896 )
2897 )
2898 m->sv = SettingsVersion_v1_9;
2899}
2900
2901/**
2902 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
2903 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
2904 * in particular if the file cannot be written.
2905 */
2906void MachineConfigFile::write(const com::Utf8Str &strFilename)
2907{
2908 try
2909 {
2910 // createStubDocument() sets the settings version to at least 1.7; however,
2911 // we might need to enfore a later settings version if incompatible settings
2912 // are present:
2913 bumpSettingsVersionIfNeeded();
2914
2915 m->strFilename = strFilename;
2916 createStubDocument();
2917
2918 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
2919
2920 pelmMachine->setAttribute("uuid", makeString(uuid));
2921 pelmMachine->setAttribute("name", strName);
2922 if (!fNameSync)
2923 pelmMachine->setAttribute("nameSync", fNameSync);
2924 if (strDescription.length())
2925 pelmMachine->createChild("Description")->addContent(strDescription);
2926 pelmMachine->setAttribute("OSType", strOsType);
2927 if (strStateFile.length())
2928 pelmMachine->setAttribute("stateFile", strStateFile);
2929 if (!uuidCurrentSnapshot.isEmpty())
2930 pelmMachine->setAttribute("currentSnapshot", makeString(uuidCurrentSnapshot));
2931 if (strSnapshotFolder.length())
2932 pelmMachine->setAttribute("snapshotFolder", strSnapshotFolder);
2933 if (!fCurrentStateModified)
2934 pelmMachine->setAttribute("currentStateModified", fCurrentStateModified);
2935 pelmMachine->setAttribute("lastStateChange", makeString(timeLastStateChange));
2936 if (fAborted)
2937 pelmMachine->setAttribute("aborted", fAborted);
2938 if ( m->sv >= SettingsVersion_v1_9
2939 && ( fTeleporterEnabled
2940 || uTeleporterPort
2941 || !strTeleporterAddress.isEmpty()
2942 || !strTeleporterPassword.isEmpty()
2943 )
2944 )
2945 {
2946 xml::ElementNode *pelmTeleporter = pelmMachine->createChild("Teleporter");
2947 pelmTeleporter->setAttribute("enabled", fTeleporterEnabled);
2948 pelmTeleporter->setAttribute("port", uTeleporterPort);
2949 pelmTeleporter->setAttribute("address", strTeleporterAddress);
2950 pelmTeleporter->setAttribute("password", strTeleporterPassword);
2951 }
2952
2953 writeExtraData(*pelmMachine, mapExtraDataItems);
2954
2955 if (llFirstSnapshot.size())
2956 writeSnapshot(*pelmMachine, llFirstSnapshot.front());
2957
2958 writeHardware(*pelmMachine, hardwareMachine, storageMachine);
2959 writeStorageControllers(*pelmMachine, storageMachine);
2960
2961 // now go write the XML
2962 xml::XmlFileWriter writer(*m->pDoc);
2963 writer.write(m->strFilename.c_str());
2964
2965 m->fFileExists = true;
2966 clearDocument();
2967 }
2968 catch (...)
2969 {
2970 clearDocument();
2971 throw;
2972 }
2973}
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