VirtualBox

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

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

StorageController: added support for Instance number

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