VirtualBox

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

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

Main,VBoxManage,xml: Added a HardwareUUID property for reporting the original machine UUID after cloning or teleporting a VM.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 122.0 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 Utf8Str strType;
1865 if (!pelmController->getAttributeValue("type", strType))
1866 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
1867
1868 if (strType == "AHCI")
1869 {
1870 sctl.storageBus = StorageBus_SATA;
1871 sctl.controllerType = StorageControllerType_IntelAhci;
1872 }
1873 else if (strType == "LsiLogic")
1874 {
1875 sctl.storageBus = StorageBus_SCSI;
1876 sctl.controllerType = StorageControllerType_LsiLogic;
1877 }
1878 else if (strType == "BusLogic")
1879 {
1880 sctl.storageBus = StorageBus_SCSI;
1881 sctl.controllerType = StorageControllerType_BusLogic;
1882 }
1883 else if (strType == "PIIX3")
1884 {
1885 sctl.storageBus = StorageBus_IDE;
1886 sctl.controllerType = StorageControllerType_PIIX3;
1887 }
1888 else if (strType == "PIIX4")
1889 {
1890 sctl.storageBus = StorageBus_IDE;
1891 sctl.controllerType = StorageControllerType_PIIX4;
1892 }
1893 else if (strType == "ICH6")
1894 {
1895 sctl.storageBus = StorageBus_IDE;
1896 sctl.controllerType = StorageControllerType_ICH6;
1897 }
1898 else if ( (m->sv >= SettingsVersion_v1_9)
1899 && (strType == "I82078")
1900 )
1901 {
1902 sctl.storageBus = StorageBus_Floppy;
1903 sctl.controllerType = StorageControllerType_I82078;
1904 }
1905 else
1906 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
1907
1908 readStorageControllerAttributes(*pelmController, sctl);
1909
1910 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
1911 const xml::ElementNode *pelmAttached;
1912 while ((pelmAttached = nlAttached.forAllNodes()))
1913 {
1914 AttachedDevice att;
1915 Utf8Str strTemp;
1916 pelmAttached->getAttributeValue("type", strTemp);
1917
1918 if (strTemp == "HardDisk")
1919 att.deviceType = DeviceType_HardDisk;
1920 else if (m->sv >= SettingsVersion_v1_9)
1921 {
1922 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
1923 if (strTemp == "DVD")
1924 {
1925 att.deviceType = DeviceType_DVD;
1926 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
1927 }
1928 else if (strTemp == "Floppy")
1929 att.deviceType = DeviceType_Floppy;
1930 }
1931
1932 if (att.deviceType != DeviceType_Null)
1933 {
1934 const xml::ElementNode *pelmImage;
1935 // all types can have images attached, but for HardDisk it's required
1936 if (!(pelmImage = pelmAttached->findChildElement("Image")))
1937 {
1938 if (att.deviceType == DeviceType_HardDisk)
1939 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
1940 else
1941 {
1942 // DVDs and floppies can also have <HostDrive> instead of <Image>
1943 const xml::ElementNode *pelmHostDrive;
1944 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
1945 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
1946 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
1947 }
1948 }
1949 else
1950 {
1951 if (!pelmImage->getAttributeValue("uuid", strTemp))
1952 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
1953 parseUUID(att.uuid, strTemp);
1954 }
1955
1956 if (!pelmAttached->getAttributeValue("port", att.lPort))
1957 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
1958 if (!pelmAttached->getAttributeValue("device", att.lDevice))
1959 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
1960
1961 sctl.llAttachedDevices.push_back(att);
1962 }
1963 }
1964
1965 strg.llStorageControllers.push_back(sctl);
1966 }
1967}
1968
1969/**
1970 * This gets called for legacy pre-1.9 settings files after having parsed the
1971 * <Hardware> and <StorageControllers> sections to parse <Hardware> once more
1972 * for the <DVDDrive> and <FloppyDrive> sections.
1973 *
1974 * Before settings version 1.9, DVD and floppy drives were specified separately
1975 * under <Hardware>; we then need this extra loop to make sure the storage
1976 * controller structs are already set up so we can add stuff to them.
1977 *
1978 * @param elmHardware
1979 * @param strg
1980 */
1981void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
1982 Storage &strg)
1983{
1984 xml::NodesLoop nl1(elmHardware);
1985 const xml::ElementNode *pelmHwChild;
1986 while ((pelmHwChild = nl1.forAllNodes()))
1987 {
1988 if (pelmHwChild->nameEquals("DVDDrive"))
1989 {
1990 // create a DVD "attached device" and attach it to the existing IDE controller
1991 AttachedDevice att;
1992 att.deviceType = DeviceType_DVD;
1993 // legacy DVD drive is always secondary master (port 1, device 0)
1994 att.lPort = 1;
1995 att.lDevice = 0;
1996 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
1997
1998 const xml::ElementNode *pDriveChild;
1999 Utf8Str strTmp;
2000 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2001 && (pDriveChild->getAttributeValue("uuid", strTmp))
2002 )
2003 parseUUID(att.uuid, strTmp);
2004 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2005 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2006
2007 // find the IDE controller and attach the DVD drive
2008 bool fFound = false;
2009 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
2010 it != strg.llStorageControllers.end();
2011 ++it)
2012 {
2013 StorageController &sctl = *it;
2014 if (sctl.storageBus == StorageBus_IDE)
2015 {
2016 sctl.llAttachedDevices.push_back(att);
2017 fFound = true;
2018 break;
2019 }
2020 }
2021
2022 if (!fFound)
2023 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
2024 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
2025 // which should have gotten parsed in <StorageControllers> before this got called
2026 }
2027 else if (pelmHwChild->nameEquals("FloppyDrive"))
2028 {
2029 bool fEnabled;
2030 if ( (pelmHwChild->getAttributeValue("enabled", fEnabled))
2031 && (fEnabled)
2032 )
2033 {
2034 // create a new floppy controller and attach a floppy "attached device"
2035 StorageController sctl;
2036 sctl.strName = "Floppy Controller";
2037 sctl.storageBus = StorageBus_Floppy;
2038 sctl.controllerType = StorageControllerType_I82078;
2039 sctl.ulPortCount = 1;
2040
2041 AttachedDevice att;
2042 att.deviceType = DeviceType_Floppy;
2043 att.lPort = 0;
2044 att.lDevice = 0;
2045
2046 const xml::ElementNode *pDriveChild;
2047 Utf8Str strTmp;
2048 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2049 && (pDriveChild->getAttributeValue("uuid", strTmp))
2050 )
2051 parseUUID(att.uuid, strTmp);
2052 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2053 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2054
2055 // store attachment with controller
2056 sctl.llAttachedDevices.push_back(att);
2057 // store controller with storage
2058 strg.llStorageControllers.push_back(sctl);
2059 }
2060 }
2061 }
2062}
2063
2064/**
2065 * Called initially for the <Snapshot> element under <Machine>, if present,
2066 * to store the snapshot's data into the given Snapshot structure (which is
2067 * then the one in the Machine struct). This might then recurse if
2068 * a <Snapshots> (plural) element is found in the snapshot, which should
2069 * contain a list of child snapshots; such lists are maintained in the
2070 * Snapshot structure.
2071 *
2072 * @param elmSnapshot
2073 * @param snap
2074 */
2075void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
2076 Snapshot &snap)
2077{
2078 Utf8Str strTemp;
2079
2080 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
2081 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
2082 parseUUID(snap.uuid, strTemp);
2083
2084 if (!elmSnapshot.getAttributeValue("name", snap.strName))
2085 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
2086
2087 // earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
2088 elmSnapshot.getAttributeValue("Description", snap.strDescription);
2089
2090 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
2091 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
2092 parseTimestamp(snap.timestamp, strTemp);
2093
2094 elmSnapshot.getAttributeValue("stateFile", snap.strStateFile); // online snapshots only
2095
2096 // parse Hardware before the other elements because other things depend on it
2097 const xml::ElementNode *pelmHardware;
2098 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
2099 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
2100 readHardware(*pelmHardware, snap.hardware, snap.storage);
2101
2102 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
2103 const xml::ElementNode *pelmSnapshotChild;
2104 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
2105 {
2106 if (pelmSnapshotChild->nameEquals("Description"))
2107 snap.strDescription = pelmSnapshotChild->getValue();
2108 else if ( (m->sv < SettingsVersion_v1_7)
2109 && (pelmSnapshotChild->nameEquals("HardDiskAttachments"))
2110 )
2111 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.storage);
2112 else if ( (m->sv >= SettingsVersion_v1_7)
2113 && (pelmSnapshotChild->nameEquals("StorageControllers"))
2114 )
2115 readStorageControllers(*pelmSnapshotChild, snap.storage);
2116 else if (pelmSnapshotChild->nameEquals("Snapshots"))
2117 {
2118 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
2119 const xml::ElementNode *pelmChildSnapshot;
2120 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
2121 {
2122 if (pelmChildSnapshot->nameEquals("Snapshot"))
2123 {
2124 Snapshot child;
2125 readSnapshot(*pelmChildSnapshot, child);
2126 snap.llChildSnapshots.push_back(child);
2127 }
2128 }
2129 }
2130 }
2131
2132 if (m->sv < SettingsVersion_v1_9)
2133 // go through Hardware once more to repair the settings controller structures
2134 // with data from old DVDDrive and FloppyDrive elements
2135 readDVDAndFloppies_pre1_9(*pelmHardware, snap.storage);
2136}
2137
2138void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
2139{
2140 if (str == "unknown") str = "Other";
2141 else if (str == "dos") str = "DOS";
2142 else if (str == "win31") str = "Windows31";
2143 else if (str == "win95") str = "Windows95";
2144 else if (str == "win98") str = "Windows98";
2145 else if (str == "winme") str = "WindowsMe";
2146 else if (str == "winnt4") str = "WindowsNT4";
2147 else if (str == "win2k") str = "Windows2000";
2148 else if (str == "winxp") str = "WindowsXP";
2149 else if (str == "win2k3") str = "Windows2003";
2150 else if (str == "winvista") str = "WindowsVista";
2151 else if (str == "win2k8") str = "Windows2008";
2152 else if (str == "os2warp3") str = "OS2Warp3";
2153 else if (str == "os2warp4") str = "OS2Warp4";
2154 else if (str == "os2warp45") str = "OS2Warp45";
2155 else if (str == "ecs") str = "OS2eCS";
2156 else if (str == "linux22") str = "Linux22";
2157 else if (str == "linux24") str = "Linux24";
2158 else if (str == "linux26") str = "Linux26";
2159 else if (str == "archlinux") str = "ArchLinux";
2160 else if (str == "debian") str = "Debian";
2161 else if (str == "opensuse") str = "OpenSUSE";
2162 else if (str == "fedoracore") str = "Fedora";
2163 else if (str == "gentoo") str = "Gentoo";
2164 else if (str == "mandriva") str = "Mandriva";
2165 else if (str == "redhat") str = "RedHat";
2166 else if (str == "ubuntu") str = "Ubuntu";
2167 else if (str == "xandros") str = "Xandros";
2168 else if (str == "freebsd") str = "FreeBSD";
2169 else if (str == "openbsd") str = "OpenBSD";
2170 else if (str == "netbsd") str = "NetBSD";
2171 else if (str == "netware") str = "Netware";
2172 else if (str == "solaris") str = "Solaris";
2173 else if (str == "opensolaris") str = "OpenSolaris";
2174 else if (str == "l4") str = "L4";
2175}
2176
2177/**
2178 * Called from the constructor to actually read in the <Machine> element
2179 * of a machine config file.
2180 * @param elmMachine
2181 */
2182void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
2183{
2184 Utf8Str strUUID;
2185 if ( (elmMachine.getAttributeValue("uuid", strUUID))
2186 && (elmMachine.getAttributeValue("name", strName))
2187 )
2188 {
2189 parseUUID(uuid, strUUID);
2190
2191 if (!elmMachine.getAttributeValue("nameSync", fNameSync))
2192 fNameSync = true;
2193
2194 Utf8Str str;
2195 elmMachine.getAttributeValue("Description", strDescription);
2196
2197 elmMachine.getAttributeValue("OSType", strOsType);
2198 if (m->sv < SettingsVersion_v1_5)
2199 convertOldOSType_pre1_5(strOsType);
2200
2201 elmMachine.getAttributeValue("stateFile", strStateFile);
2202 if (elmMachine.getAttributeValue("currentSnapshot", str))
2203 parseUUID(uuidCurrentSnapshot, str);
2204 elmMachine.getAttributeValue("snapshotFolder", strSnapshotFolder);
2205 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
2206 fCurrentStateModified = true;
2207 if (elmMachine.getAttributeValue("lastStateChange", str))
2208 parseTimestamp(timeLastStateChange, str);
2209 // constructor has called RTTimeNow(&timeLastStateChange) before
2210
2211#if 1 /** @todo Teleportation: Obsolete. Remove in a couple of days. */
2212 if (!elmMachine.getAttributeValue("teleporterEnabled", fTeleporterEnabled)
2213 && !elmMachine.getAttributeValue("liveMigrationTarget", fTeleporterEnabled))
2214 fTeleporterEnabled = false;
2215 if (!elmMachine.getAttributeValue("teleporterPort", uTeleporterPort)
2216 && !elmMachine.getAttributeValue("liveMigrationPort", uTeleporterPort))
2217 uTeleporterPort = 0;
2218 if (!elmMachine.getAttributeValue("teleporterAddress", strTeleporterAddress))
2219 strTeleporterAddress = "";
2220 if (!elmMachine.getAttributeValue("teleporterPassword", strTeleporterPassword)
2221 && !elmMachine.getAttributeValue("liveMigrationPassword", strTeleporterPassword))
2222 strTeleporterPassword = "";
2223#endif
2224
2225 // parse Hardware before the other elements because other things depend on it
2226 const xml::ElementNode *pelmHardware;
2227 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
2228 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
2229 readHardware(*pelmHardware, hardwareMachine, storageMachine);
2230
2231 xml::NodesLoop nlRootChildren(elmMachine);
2232 const xml::ElementNode *pelmMachineChild;
2233 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
2234 {
2235 if (pelmMachineChild->nameEquals("ExtraData"))
2236 readExtraData(*pelmMachineChild,
2237 mapExtraDataItems);
2238 else if ( (m->sv < SettingsVersion_v1_7)
2239 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
2240 )
2241 readHardDiskAttachments_pre1_7(*pelmMachineChild, storageMachine);
2242 else if ( (m->sv >= SettingsVersion_v1_7)
2243 && (pelmMachineChild->nameEquals("StorageControllers"))
2244 )
2245 readStorageControllers(*pelmMachineChild, storageMachine);
2246 else if (pelmMachineChild->nameEquals("Snapshot"))
2247 {
2248 Snapshot snap;
2249 // this will recurse into child snapshots, if necessary
2250 readSnapshot(*pelmMachineChild, snap);
2251 llFirstSnapshot.push_back(snap);
2252 }
2253 else if (pelmMachineChild->nameEquals("Description"))
2254 strDescription = pelmMachineChild->getValue();
2255 else if (pelmMachineChild->nameEquals("Teleporter"))
2256 {
2257 if (!pelmMachineChild->getAttributeValue("enabled", fTeleporterEnabled))
2258 fTeleporterEnabled = false;
2259 if (!pelmMachineChild->getAttributeValue("port", uTeleporterPort))
2260 uTeleporterPort = 0;
2261 if (!pelmMachineChild->getAttributeValue("address", strTeleporterAddress))
2262 strTeleporterAddress = "";
2263 if (!pelmMachineChild->getAttributeValue("password", strTeleporterPassword))
2264 strTeleporterPassword = "";
2265 }
2266 }
2267
2268 if (m->sv < SettingsVersion_v1_9)
2269 // go through Hardware once more to repair the settings controller structures
2270 // with data from old DVDDrive and FloppyDrive elements
2271 readDVDAndFloppies_pre1_9(*pelmHardware, storageMachine);
2272 }
2273 else
2274 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
2275}
2276
2277////////////////////////////////////////////////////////////////////////////////
2278//
2279// MachineConfigFile
2280//
2281////////////////////////////////////////////////////////////////////////////////
2282
2283/**
2284 * Constructor.
2285 *
2286 * If pstrFilename is != NULL, this reads the given settings file into the member
2287 * variables and various substructures and lists. Otherwise, the member variables
2288 * are initialized with default values.
2289 *
2290 * Throws variants of xml::Error for I/O, XML and logical content errors, which
2291 * the caller should catch; if this constructor does not throw, then the member
2292 * variables contain meaningful values (either from the file or defaults).
2293 *
2294 * @param strFilename
2295 */
2296MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
2297 : ConfigFileBase(pstrFilename),
2298 fNameSync(true),
2299 fTeleporterEnabled(false),
2300 uTeleporterPort(0),
2301 fCurrentStateModified(true),
2302 fAborted(false)
2303{
2304 RTTimeNow(&timeLastStateChange);
2305
2306 if (pstrFilename)
2307 {
2308 // the ConfigFileBase constructor has loaded the XML file, so now
2309 // we need only analyze what is in there
2310
2311 xml::NodesLoop nlRootChildren(*m->pelmRoot);
2312 const xml::ElementNode *pelmRootChild;
2313 while ((pelmRootChild = nlRootChildren.forAllNodes()))
2314 {
2315 if (pelmRootChild->nameEquals("Machine"))
2316 readMachine(*pelmRootChild);
2317 }
2318
2319 // clean up memory allocated by XML engine
2320 clearDocument();
2321 }
2322}
2323
2324/**
2325 * Creates a <Hardware> node under elmParent and then writes out the XML
2326 * keys under that. Called for both the <Machine> node and for snapshots.
2327 * @param elmParent
2328 * @param st
2329 */
2330void MachineConfigFile::writeHardware(xml::ElementNode &elmParent,
2331 const Hardware &hw,
2332 const Storage &strg)
2333{
2334 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
2335
2336 if (hw.strVersion != "2")
2337 pelmHardware->setAttribute("version", hw.strVersion);
2338 if (!hw.uuid.isEmpty())
2339 pelmHardware->setAttribute("uuid", makeString(hw.uuid));
2340
2341 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
2342 xml::ElementNode *pelmHwVirtEx = pelmCPU->createChild("HardwareVirtEx");
2343 pelmHwVirtEx->setAttribute("enabled", hw.fHardwareVirt);
2344 pelmHwVirtEx->setAttribute("exclusive", hw.fHardwareVirtExclusive);
2345 if (hw.fNestedPaging)
2346 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
2347 if (hw.fVPID)
2348 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
2349 if (hw.fPAE)
2350 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
2351 if (hw.fSyntheticCpu)
2352 pelmCPU->createChild("SyntheticCpu")->setAttribute("enabled", hw.fSyntheticCpu);
2353 pelmCPU->setAttribute("count", hw.cCPUs);
2354
2355 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
2356 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
2357
2358 if ( (m->sv >= SettingsVersion_v1_9)
2359 && (hw.firmwareType == FirmwareType_EFI)
2360 )
2361 {
2362 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
2363 pelmFirmware->setAttribute("type", "EFI");
2364 }
2365
2366 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
2367 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
2368 it != hw.mapBootOrder.end();
2369 ++it)
2370 {
2371 uint32_t i = it->first;
2372 DeviceType_T type = it->second;
2373 const char *pcszDevice;
2374
2375 switch (type)
2376 {
2377 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
2378 case DeviceType_DVD: pcszDevice = "DVD"; break;
2379 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
2380 case DeviceType_Network: pcszDevice = "Network"; break;
2381 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
2382 }
2383
2384 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
2385 pelmOrder->setAttribute("position",
2386 i + 1); // XML is 1-based but internal data is 0-based
2387 pelmOrder->setAttribute("device", pcszDevice);
2388 }
2389
2390 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
2391 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
2392 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
2393 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
2394
2395 if (m->sv >= SettingsVersion_v1_8)
2396 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
2397
2398 xml::ElementNode *pelmVRDP = pelmHardware->createChild("RemoteDisplay");
2399 pelmVRDP->setAttribute("enabled", hw.vrdpSettings.fEnabled);
2400 pelmVRDP->setAttribute("port", hw.vrdpSettings.strPort);
2401 if (hw.vrdpSettings.strNetAddress.length())
2402 pelmVRDP->setAttribute("netAddress", hw.vrdpSettings.strNetAddress);
2403 const char *pcszAuthType;
2404 switch (hw.vrdpSettings.authType)
2405 {
2406 case VRDPAuthType_Guest: pcszAuthType = "Guest"; break;
2407 case VRDPAuthType_External: pcszAuthType = "External"; break;
2408 default: /*case VRDPAuthType_Null:*/ pcszAuthType = "Null"; break;
2409 }
2410 pelmVRDP->setAttribute("authType", pcszAuthType);
2411
2412 if (hw.vrdpSettings.ulAuthTimeout != 0)
2413 pelmVRDP->setAttribute("authTimeout", hw.vrdpSettings.ulAuthTimeout);
2414 if (hw.vrdpSettings.fAllowMultiConnection)
2415 pelmVRDP->setAttribute("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
2416 if (hw.vrdpSettings.fReuseSingleConnection)
2417 pelmVRDP->setAttribute("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
2418
2419 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
2420 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
2421 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
2422
2423 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
2424 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
2425 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
2426 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
2427 if (hw.biosSettings.strLogoImagePath.length())
2428 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
2429
2430 const char *pcszBootMenu;
2431 switch (hw.biosSettings.biosBootMenuMode)
2432 {
2433 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
2434 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
2435 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
2436 }
2437 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
2438 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
2439 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
2440
2441 if (m->sv < SettingsVersion_v1_9)
2442 {
2443 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
2444 // run thru the storage controllers to see if we have a DVD or floppy drives
2445 size_t cDVDs = 0;
2446 size_t cFloppies = 0;
2447
2448 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
2449 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
2450
2451 for (StorageControllersList::const_iterator it = strg.llStorageControllers.begin();
2452 it != strg.llStorageControllers.end();
2453 ++it)
2454 {
2455 const StorageController &sctl = *it;
2456 // in old settings format, the DVD drive could only have been under the IDE controller
2457 if (sctl.storageBus == StorageBus_IDE)
2458 {
2459 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
2460 it2 != sctl.llAttachedDevices.end();
2461 ++it2)
2462 {
2463 const AttachedDevice &att = *it2;
2464 if (att.deviceType == DeviceType_DVD)
2465 {
2466 if (cDVDs > 0)
2467 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
2468
2469 ++cDVDs;
2470
2471 pelmDVD->setAttribute("passthrough", att.fPassThrough);
2472 if (!att.uuid.isEmpty())
2473 pelmDVD->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2474 else if (att.strHostDriveSrc.length())
2475 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2476 }
2477 }
2478 }
2479 else if (sctl.storageBus == StorageBus_Floppy)
2480 {
2481 size_t cFloppiesHere = sctl.llAttachedDevices.size();
2482 if (cFloppiesHere > 1)
2483 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
2484 if (cFloppiesHere)
2485 {
2486 const AttachedDevice &att = sctl.llAttachedDevices.front();
2487 pelmFloppy->setAttribute("enabled", true);
2488 if (!att.uuid.isEmpty())
2489 pelmFloppy->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2490 else if (att.strHostDriveSrc.length())
2491 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2492 }
2493
2494 cFloppies += cFloppiesHere;
2495 }
2496 }
2497
2498 if (cFloppies == 0)
2499 pelmFloppy->setAttribute("enabled", false);
2500 else if (cFloppies > 1)
2501 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
2502 }
2503
2504 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
2505 pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
2506 pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
2507
2508 writeUSBDeviceFilters(*pelmUSB,
2509 hw.usbController.llDeviceFilters,
2510 false); // fHostMode
2511
2512 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
2513 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
2514 it != hw.llNetworkAdapters.end();
2515 ++it)
2516 {
2517 const NetworkAdapter &nic = *it;
2518
2519 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
2520 pelmAdapter->setAttribute("slot", nic.ulSlot);
2521 pelmAdapter->setAttribute("enabled", nic.fEnabled);
2522 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
2523 pelmAdapter->setAttribute("cable", nic.fCableConnected);
2524 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
2525 if (nic.fTraceEnabled)
2526 {
2527 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
2528 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
2529 }
2530
2531 const char *pcszType;
2532 switch (nic.type)
2533 {
2534 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
2535 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
2536 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
2537 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
2538 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
2539 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
2540 }
2541 pelmAdapter->setAttribute("type", pcszType);
2542
2543 xml::ElementNode *pelmNAT;
2544 switch (nic.mode)
2545 {
2546 case NetworkAttachmentType_NAT:
2547 pelmNAT = pelmAdapter->createChild("NAT");
2548 if (nic.strName.length())
2549 pelmNAT->setAttribute("network", nic.strName);
2550 break;
2551
2552 case NetworkAttachmentType_Bridged:
2553 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
2554 break;
2555
2556 case NetworkAttachmentType_Internal:
2557 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
2558 break;
2559
2560 case NetworkAttachmentType_HostOnly:
2561 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
2562 break;
2563
2564 default: /*case NetworkAttachmentType_Null:*/
2565 break;
2566 }
2567 }
2568
2569 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
2570 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
2571 it != hw.llSerialPorts.end();
2572 ++it)
2573 {
2574 const SerialPort &port = *it;
2575 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
2576 pelmPort->setAttribute("slot", port.ulSlot);
2577 pelmPort->setAttribute("enabled", port.fEnabled);
2578 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
2579 pelmPort->setAttribute("IRQ", port.ulIRQ);
2580
2581 const char *pcszHostMode;
2582 switch (port.portMode)
2583 {
2584 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
2585 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
2586 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
2587 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
2588 }
2589 switch (port.portMode)
2590 {
2591 case PortMode_HostPipe:
2592 pelmPort->setAttribute("server", port.fServer);
2593 /* no break */
2594 case PortMode_HostDevice:
2595 case PortMode_RawFile:
2596 pelmPort->setAttribute("path", port.strPath);
2597 break;
2598
2599 default:
2600 break;
2601 }
2602 pelmPort->setAttribute("hostMode", pcszHostMode);
2603 }
2604
2605 pelmPorts = pelmHardware->createChild("LPT");
2606 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
2607 it != hw.llParallelPorts.end();
2608 ++it)
2609 {
2610 const ParallelPort &port = *it;
2611 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
2612 pelmPort->setAttribute("slot", port.ulSlot);
2613 pelmPort->setAttribute("enabled", port.fEnabled);
2614 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
2615 pelmPort->setAttribute("IRQ", port.ulIRQ);
2616 if (port.strPath.length())
2617 pelmPort->setAttribute("path", port.strPath);
2618 }
2619
2620 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
2621 pelmAudio->setAttribute("controller", (hw.audioAdapter.controllerType == AudioControllerType_SB16) ? "SB16" : "AC97");
2622
2623 const char *pcszDriver;
2624 switch (hw.audioAdapter.driverType)
2625 {
2626 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
2627 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
2628 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
2629 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
2630 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
2631 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
2632 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
2633 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
2634 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
2635 }
2636 pelmAudio->setAttribute("driver", pcszDriver);
2637
2638 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
2639
2640 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
2641 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
2642 it != hw.llSharedFolders.end();
2643 ++it)
2644 {
2645 const SharedFolder &sf = *it;
2646 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
2647 pelmThis->setAttribute("name", sf.strName);
2648 pelmThis->setAttribute("hostPath", sf.strHostPath);
2649 pelmThis->setAttribute("writable", sf.fWritable);
2650 }
2651
2652 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
2653 const char *pcszClip;
2654 switch (hw.clipboardMode)
2655 {
2656 case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
2657 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
2658 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
2659 default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
2660 }
2661 pelmClip->setAttribute("mode", pcszClip);
2662
2663 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
2664 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
2665 pelmGuest->setAttribute("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
2666
2667 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
2668 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
2669 it != hw.llGuestProperties.end();
2670 ++it)
2671 {
2672 const GuestProperty &prop = *it;
2673 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
2674 pelmProp->setAttribute("name", prop.strName);
2675 pelmProp->setAttribute("value", prop.strValue);
2676 pelmProp->setAttribute("timestamp", prop.timestamp);
2677 pelmProp->setAttribute("flags", prop.strFlags);
2678 }
2679
2680 if (hw.strNotificationPatterns.length())
2681 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
2682}
2683
2684/**
2685 * Creates a <StorageControllers> node under elmParent and then writes out the XML
2686 * keys under that. Called for both the <Machine> node and for snapshots.
2687 * @param elmParent
2688 * @param st
2689 */
2690void MachineConfigFile::writeStorageControllers(xml::ElementNode &elmParent,
2691 const Storage &st)
2692{
2693 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
2694
2695 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
2696 it != st.llStorageControllers.end();
2697 ++it)
2698 {
2699 const StorageController &sc = *it;
2700
2701 if ( (m->sv < SettingsVersion_v1_9)
2702 && (sc.controllerType == StorageControllerType_I82078)
2703 )
2704 // floppy controller already got written into <Hardware>/<FloppyController> in writeHardware()
2705 // for pre-1.9 settings
2706 continue;
2707
2708 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
2709 com::Utf8Str name = sc.strName.raw();
2710 //
2711 if (m->sv < SettingsVersion_v1_8)
2712 {
2713 // pre-1.8 settings use shorter controller names, they are
2714 // expanded when reading the settings
2715 if (name == "IDE Controller")
2716 name = "IDE";
2717 else if (name == "SATA Controller")
2718 name = "SATA";
2719 }
2720 pelmController->setAttribute("name", sc.strName);
2721
2722 const char *pcszType;
2723 switch (sc.controllerType)
2724 {
2725 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
2726 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
2727 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
2728 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
2729 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
2730 case StorageControllerType_I82078: pcszType = "I82078"; break;
2731 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
2732 }
2733 pelmController->setAttribute("type", pcszType);
2734
2735 pelmController->setAttribute("PortCount", sc.ulPortCount);
2736
2737 if (sc.controllerType == StorageControllerType_IntelAhci)
2738 {
2739 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
2740 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
2741 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
2742 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
2743 }
2744
2745 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
2746 it2 != sc.llAttachedDevices.end();
2747 ++it2)
2748 {
2749 const AttachedDevice &att = *it2;
2750
2751 /* DVD/Floppy is handled already for settings version before 1.8 */
2752 if ( att.deviceType == DeviceType_DVD
2753 && m->sv <= SettingsVersion_v1_8)
2754 continue;
2755
2756 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
2757
2758 pcszType = NULL;
2759
2760 switch (att.deviceType)
2761 {
2762 case DeviceType_HardDisk:
2763 pcszType = "HardDisk";
2764 break;
2765
2766 case DeviceType_DVD:
2767 pcszType = "DVD";
2768 if (att.fPassThrough)
2769 pelmDevice->setAttribute("passthrough", att.fPassThrough);
2770 break;
2771
2772 case DeviceType_Floppy:
2773 pcszType = "Floppy";
2774 break;
2775 }
2776
2777 pelmDevice->setAttribute("type", pcszType);
2778
2779 pelmDevice->setAttribute("port", att.lPort);
2780 pelmDevice->setAttribute("device", att.lDevice);
2781
2782 if (!att.uuid.isEmpty())
2783 pelmDevice->createChild("Image")->setAttribute("uuid", makeString(att.uuid));
2784 else if ( (m->sv >= SettingsVersion_v1_9)
2785 && (att.strHostDriveSrc.length())
2786 )
2787 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
2788 }
2789 }
2790}
2791
2792/**
2793 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
2794 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
2795 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
2796 * @param elmParent
2797 * @param snap
2798 */
2799void MachineConfigFile::writeSnapshot(xml::ElementNode &elmParent,
2800 const Snapshot &snap)
2801{
2802 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
2803
2804 pelmSnapshot->setAttribute("uuid", makeString(snap.uuid));
2805 pelmSnapshot->setAttribute("name", snap.strName);
2806 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
2807
2808 if (snap.strStateFile.length())
2809 pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
2810
2811 if (snap.strDescription.length())
2812 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
2813
2814 writeHardware(*pelmSnapshot, snap.hardware, snap.storage);
2815 writeStorageControllers(*pelmSnapshot, snap.storage);
2816
2817 if (snap.llChildSnapshots.size())
2818 {
2819 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
2820 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
2821 it != snap.llChildSnapshots.end();
2822 ++it)
2823 {
2824 const Snapshot &child = *it;
2825 writeSnapshot(*pelmChildren, child);
2826 }
2827 }
2828}
2829
2830/**
2831 * Called from write() before calling ConfigFileBase::createStubDocument().
2832 * This adjusts the settings version in m->sv if incompatible settings require
2833 * a settings bump, whereas otherwise we try to preserve the settings version
2834 * to avoid breaking compatibility with older versions.
2835 */
2836void MachineConfigFile::bumpSettingsVersionIfNeeded()
2837{
2838 if (m->sv < SettingsVersion_v1_8)
2839 {
2840 // "accelerate 2d video" requires settings version 1.8
2841 if (hardwareMachine.fAccelerate2DVideo)
2842 m->sv = SettingsVersion_v1_8;
2843 }
2844
2845 if (m->sv < SettingsVersion_v1_9)
2846 {
2847 size_t cDVDs = 0;
2848 size_t cFloppies = 0;
2849
2850 // if there is more than one DVD or floppy or the DVD attachment is not
2851 // at the old IDE default, then we need 1.9
2852 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
2853 it != storageMachine.llStorageControllers.end()
2854 && m->sv < SettingsVersion_v1_9;
2855 ++it)
2856 {
2857 const StorageController &sctl = *it;
2858 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
2859 it2 != sctl.llAttachedDevices.end()
2860 && m->sv < SettingsVersion_v1_9;
2861 ++it2)
2862 {
2863 const AttachedDevice &att = *it2;
2864 if (att.deviceType == DeviceType_DVD)
2865 {
2866 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
2867 || (att.lPort != 1) // DVDs not at secondary master?
2868 || (att.lDevice != 0)
2869 )
2870 {
2871 m->sv = SettingsVersion_v1_9;
2872 break;
2873 }
2874
2875 ++cDVDs;
2876 }
2877 else if (att.deviceType == DeviceType_Floppy)
2878 ++cFloppies;
2879 }
2880 }
2881
2882 // VirtualBox before 3.1 had exactly one floppy and exactly one DVD,
2883 // so any deviation from that will require settings version 1.9
2884 if ( (m->sv < SettingsVersion_v1_9)
2885 && ( (cDVDs != 1)
2886 || (cFloppies > 1)
2887 )
2888 )
2889 m->sv = SettingsVersion_v1_9;
2890 }
2891
2892 if ( (m->sv < SettingsVersion_v1_9)
2893 && (hardwareMachine.firmwareType == FirmwareType_EFI)
2894 )
2895 {
2896 m->sv = SettingsVersion_v1_9;
2897 }
2898
2899 if ( m->sv < SettingsVersion_v1_9
2900 && ( fTeleporterEnabled
2901 || uTeleporterPort
2902 || !strTeleporterAddress.isEmpty()
2903 || !strTeleporterPassword.isEmpty()
2904 )
2905 )
2906 m->sv = SettingsVersion_v1_9;
2907
2908 if ( m->sv < SettingsVersion_v1_9
2909 && !hardwareMachine.uuid.isEmpty())
2910 m->sv = SettingsVersion_v1_9;
2911}
2912
2913/**
2914 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
2915 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
2916 * in particular if the file cannot be written.
2917 */
2918void MachineConfigFile::write(const com::Utf8Str &strFilename)
2919{
2920 try
2921 {
2922 // createStubDocument() sets the settings version to at least 1.7; however,
2923 // we might need to enfore a later settings version if incompatible settings
2924 // are present:
2925 bumpSettingsVersionIfNeeded();
2926
2927 m->strFilename = strFilename;
2928 createStubDocument();
2929
2930 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
2931
2932 pelmMachine->setAttribute("uuid", makeString(uuid));
2933 pelmMachine->setAttribute("name", strName);
2934 if (!fNameSync)
2935 pelmMachine->setAttribute("nameSync", fNameSync);
2936 if (strDescription.length())
2937 pelmMachine->createChild("Description")->addContent(strDescription);
2938 pelmMachine->setAttribute("OSType", strOsType);
2939 if (strStateFile.length())
2940 pelmMachine->setAttribute("stateFile", strStateFile);
2941 if (!uuidCurrentSnapshot.isEmpty())
2942 pelmMachine->setAttribute("currentSnapshot", makeString(uuidCurrentSnapshot));
2943 if (strSnapshotFolder.length())
2944 pelmMachine->setAttribute("snapshotFolder", strSnapshotFolder);
2945 if (!fCurrentStateModified)
2946 pelmMachine->setAttribute("currentStateModified", fCurrentStateModified);
2947 pelmMachine->setAttribute("lastStateChange", makeString(timeLastStateChange));
2948 if (fAborted)
2949 pelmMachine->setAttribute("aborted", fAborted);
2950 if ( m->sv >= SettingsVersion_v1_9
2951 && ( fTeleporterEnabled
2952 || uTeleporterPort
2953 || !strTeleporterAddress.isEmpty()
2954 || !strTeleporterPassword.isEmpty()
2955 )
2956 )
2957 {
2958 xml::ElementNode *pelmTeleporter = pelmMachine->createChild("Teleporter");
2959 pelmTeleporter->setAttribute("enabled", fTeleporterEnabled);
2960 pelmTeleporter->setAttribute("port", uTeleporterPort);
2961 pelmTeleporter->setAttribute("address", strTeleporterAddress);
2962 pelmTeleporter->setAttribute("password", strTeleporterPassword);
2963 }
2964
2965 writeExtraData(*pelmMachine, mapExtraDataItems);
2966
2967 if (llFirstSnapshot.size())
2968 writeSnapshot(*pelmMachine, llFirstSnapshot.front());
2969
2970 writeHardware(*pelmMachine, hardwareMachine, storageMachine);
2971 writeStorageControllers(*pelmMachine, storageMachine);
2972
2973 // now go write the XML
2974 xml::XmlFileWriter writer(*m->pDoc);
2975 writer.write(m->strFilename.c_str());
2976
2977 m->fFileExists = true;
2978 clearDocument();
2979 }
2980 catch (...)
2981 {
2982 clearDocument();
2983 throw;
2984 }
2985}
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