VirtualBox

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

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

Main/Settings: Fix crash when opening pre 1.7 config files. Attached storage devices didn't had the deviceType member set

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