VirtualBox

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

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

Main: fix crash if VirtualBox.xml is not present.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 80.9 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
29using namespace com;
30using namespace settings;
31
32/**
33 * Opaque data structore for ConfigFileBase (only declared
34 * in header, defined only here).
35 */
36
37struct ConfigFileBase::Data
38{
39 Data()
40 : pParser(NULL),
41 pDoc(NULL),
42 pelmRoot(NULL)
43 {}
44
45 ~Data()
46 {
47 cleanup();
48 }
49
50 iprt::MiniString strFilename;
51 bool fFileExists;
52
53 xml::XmlFileParser *pParser;
54 xml::Document *pDoc;
55 xml::ElementNode *pelmRoot;
56 com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux"
57 SettingsVersion_T sv; // e.g. SETTINGS_VERSION_1_7
58
59 void cleanup()
60 {
61 if (pDoc)
62 {
63 delete pDoc;
64 pDoc = NULL;
65 pelmRoot = NULL;
66 }
67
68 if (pParser)
69 {
70 delete pParser;
71 pParser = NULL;
72 }
73 }
74};
75
76/**
77 * Private exception class (not in the header file) that makes
78 * throwing xml::LogicError instances easier. That class is public
79 * and should be caught by client code.
80 */
81class settings::ConfigFileError : public xml::LogicError
82{
83public:
84 ConfigFileError(const ConfigFileBase *file, const char *pcszFormat, ...)
85 : xml::LogicError()
86 {
87 va_list args;
88 va_start(args, pcszFormat);
89 Utf8StrFmtVA what(pcszFormat, args);
90 va_end(args);
91
92 Utf8StrFmt str("Error reading %s. %s", file->m->strFilename.c_str(), what.c_str());
93
94 setWhat(str.c_str());
95 }
96};
97
98/**
99 * Constructor. Allocates the XML internals.
100 * @param strFilename
101 */
102ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
103 : m(new Data)
104{
105 m->fFileExists = false;
106
107 if (pstrFilename)
108 {
109 m->strFilename = *pstrFilename;
110
111 m->pParser = new xml::XmlFileParser;
112 m->pDoc = new xml::Document;
113 m->pParser->read(*pstrFilename,
114 *m->pDoc);
115
116 m->fFileExists = true;
117
118 m->pelmRoot = m->pDoc->getRootElement();
119 if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
120 throw ConfigFileError(this, N_("Root element in VirtualBox settings files must be \"VirtualBox\"."));
121
122 if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
123 throw ConfigFileError(this, N_("Required VirtualBox/version attribute is missing"));
124
125 m->sv = SettingsVersion_Null;
126 if (m->strSettingsVersionFull.length() > 3)
127 {
128 const char *pcsz = m->strSettingsVersionFull.c_str();
129 if ( (pcsz[0] == '1')
130 && (pcsz[1] == '.')
131 && (pcsz[3] == '-')
132 )
133 {
134 if (pcsz[2] == '7')
135 m->sv = SettingsVersion_v1_7;
136 else if (pcsz[2] == '8')
137 m->sv = SettingsVersion_v1_8;
138 }
139 }
140
141 if (m->sv == SettingsVersion_Null)
142 throw ConfigFileError(this, N_("Cannot handle settings version '%s'"), m->strSettingsVersionFull.c_str());
143 }
144 else
145 {
146 m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
147 m->sv = SettingsVersion_v1_8;
148 }
149}
150
151/**
152 * Clean up.
153 */
154ConfigFileBase::~ConfigFileBase()
155{
156 if (m)
157 {
158 delete m;
159 m = NULL;
160 }
161}
162
163/**
164 * Helper function that parses a UUID in string form into
165 * a com::Guid item. Since that uses an IPRT function which
166 * does not accept "{}" characters around the UUID string,
167 * we handle that here. Throws on errors.
168 * @param guid
169 * @param strUUID
170 */
171void ConfigFileBase::parseUUID(Guid &guid,
172 const Utf8Str &strUUID) const
173{
174 // {5f102a55-a51b-48e3-b45a-b28d33469488}
175 // 01234567890123456789012345678901234567
176 // 1 2 3
177 if ( (strUUID[0] == '{')
178 && (strUUID[37] == '}')
179 )
180 guid = strUUID.substr(1, 36).c_str();
181 else
182 guid = strUUID.c_str();
183
184 if (guid.isEmpty())
185 throw ConfigFileError(this, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
186}
187
188/**
189 * Parses the given string in str and attempts to treat it as an ISO
190 * date/time stamp to put into timestamp. Throws on errors.
191 * @param timestamp
192 * @param str
193 */
194void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
195 const com::Utf8Str &str) const
196{
197 const char *pcsz = str.c_str();
198 // yyyy-mm-ddThh:mm:ss
199 // "2009-07-10T11:54:03Z"
200 // 01234567890123456789
201 // 1
202 if (str.length() > 19)
203 {
204 // timezone must either be unspecified or 'Z' for UTC
205 if ( (pcsz[19])
206 && (pcsz[19] != 'Z')
207 )
208 throw ConfigFileError(this, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
209
210 int32_t yyyy;
211 uint32_t mm, dd, hh, min, secs;
212 if ( (pcsz[4] == '-')
213 && (pcsz[7] == '-')
214 && (pcsz[10] == 'T')
215 && (pcsz[13] == ':')
216 && (pcsz[16] == ':')
217 )
218 {
219 int rc;
220 if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
221 // could theoretically be negative but let's assume that nobody
222 // created virtual machines before the Christian era
223 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
224 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
225 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
226 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
227 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
228 )
229 {
230 RTTIME time = { yyyy,
231 (uint8_t)mm,
232 0,
233 0,
234 (uint8_t)dd,
235 (uint8_t)hh,
236 (uint8_t)min,
237 (uint8_t)secs,
238 0,
239 RTTIME_FLAGS_TYPE_UTC };
240 if (RTTimeNormalize(&time))
241 if (RTTimeImplode(&timestamp, &time))
242 return;
243 }
244
245 throw ConfigFileError(this, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
246 }
247
248 throw ConfigFileError(this, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
249 }
250}
251
252/**
253 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
254 * @param stamp
255 * @return
256 */
257com::Utf8Str ConfigFileBase::makeString(const RTTIMESPEC &stamp)
258{
259 RTTIME time;
260 if (!RTTimeExplode(&time, &stamp))
261 throw ConfigFileError(this, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
262
263 return Utf8StrFmt("%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
264 time.i32Year,
265 (uint16_t)time.u8Month,
266 (uint16_t)time.u8MonthDay,
267 (uint16_t)time.u8Hour,
268 (uint16_t)time.u8Minute,
269 (uint16_t)time.u8Second);
270}
271
272com::Utf8Str ConfigFileBase::makeString(const Guid &guid)
273{
274 Utf8Str str("{");
275 str.append(guid.toString());
276 str.append("}");
277 return str;
278}
279
280/**
281 * Helper method to read in an ExtraData subtree and stores its contents
282 * in the given map of extradata items. Used for both main and machine
283 * extradata (MainConfigFile and MachineConfigFile).
284 * @param elmExtraData
285 * @param map
286 */
287void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
288 ExtraDataItemsMap &map)
289{
290 xml::NodesLoop nlLevel4(elmExtraData);
291 const xml::ElementNode *pelmExtraDataItem;
292 while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
293 {
294 if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
295 {
296 // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
297 Utf8Str strName, strValue;
298 if ( ((pelmExtraDataItem->getAttributeValue("name", strName)))
299 && ((pelmExtraDataItem->getAttributeValue("value", strValue)))
300 )
301 map[strName] = strValue;
302 else
303 throw ConfigFileError(this, N_("ExtraDataItem element must have name and value attributes"));
304 }
305 else
306 throw ConfigFileError(this, N_("Invalid element %s in ExtraData section"), pelmExtraDataItem->getName());
307 }
308}
309
310/**
311 * Reads <USBDeviceFilter> entries from under the given elmDeviceFilters node and
312 * stores them in the given linklist. This is in ConfigFileBase because it's used
313 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
314 * filters).
315 * @param elmDeviceFilters
316 * @param ll
317 */
318void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
319 USBDeviceFiltersList &ll)
320{
321 xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
322 const xml::ElementNode *pelmLevel4Child;
323 while ((pelmLevel4Child = nl1.forAllNodes()))
324 {
325 USBDeviceFilter flt;
326 flt.action = USBDeviceFilterAction_Ignore;
327 Utf8Str strAction;
328 if ( (pelmLevel4Child->getAttributeValue("name", flt.strName))
329 && (pelmLevel4Child->getAttributeValue("active", flt.fActive))
330 )
331 {
332 pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId);
333 pelmLevel4Child->getAttributeValue("productId", flt.strProductId);
334 pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
335 pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
336 pelmLevel4Child->getAttributeValue("product", flt.strProduct);
337 pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber);
338 pelmLevel4Child->getAttributeValue("port", flt.strPort);
339
340 // the next 2 are irrelevant for host USB objects
341 pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
342 pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
343
344 // action is only used with host USB objects
345 if (pelmLevel4Child->getAttributeValue("action", strAction))
346 {
347 if (strAction == "Ignore")
348 flt.action = USBDeviceFilterAction_Ignore;
349 else if (strAction == "Hold")
350 flt.action = USBDeviceFilterAction_Hold;
351 else
352 throw ConfigFileError(this, N_("Invalid value %s in DeviceFilter/action attribute"), strAction.c_str());
353 }
354
355 ll.push_back(flt);
356 }
357 }
358}
359
360/**
361 * Creates a new stub xml::Document in the m->pDoc member with the
362 * root "VirtualBox" element set up. This is used by both
363 * MainConfigFile and MachineConfigFile when writing out their XML.
364 */
365void ConfigFileBase::createStubDocument()
366{
367 Assert(m->pDoc == NULL);
368 m->pDoc = new xml::Document;
369
370 m->pelmRoot = m->pDoc->createRootElement("VirtualBox");
371 m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
372
373 const char *pcszVersion;
374 switch (m->sv)
375 {
376 case SettingsVersion_v1_7:
377 pcszVersion = "1.7";
378 break;
379 case SettingsVersion_v1_8:
380 pcszVersion = "1.8";
381 break;
382 }
383 m->pelmRoot->setAttribute("version", Utf8StrFmt("%s-%s",
384 pcszVersion,
385 VBOX_XML_PLATFORM)); // e.g. "linux"
386}
387
388/**
389 *
390 * @param elmParent
391 * @param me
392 */
393void ConfigFileBase::writeExtraData(xml::ElementNode &elmParent,
394 const ExtraDataItemsMap &me)
395{
396 if (me.size())
397 {
398 xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
399 for (ExtraDataItemsMap::const_iterator it = me.begin();
400 it != me.end();
401 ++it)
402 {
403 const Utf8Str &strName = it->first;
404 const Utf8Str &strValue = it->second;
405 xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
406 pelmThis->setAttribute("name", strName);
407 pelmThis->setAttribute("value", strValue);
408 }
409 }
410}
411
412/**
413 * Creates <DeviceFilter> nodes under the given parent element according to
414 * the contents of the given USBDeviceFiltersList. If fHostMode is true,
415 * this means that we're supposed to write filters for the IHost interface
416 * (respect "action", omit "strRemote" and "ulMaskedInterfaces" in
417 * struct USBDeviceFilter).
418 * @param elmParent
419 * @param ll
420 * @param fHostMode
421 */
422void ConfigFileBase::writeUSBDeviceFilters(xml::ElementNode &elmParent,
423 const USBDeviceFiltersList &ll,
424 bool fHostMode)
425{
426 for (USBDeviceFiltersList::const_iterator it = ll.begin();
427 it != ll.end();
428 ++it)
429 {
430 const USBDeviceFilter &flt = *it;
431 xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
432 pelmFilter->setAttribute("name", flt.strName);
433 pelmFilter->setAttribute("active", flt.fActive);
434 if (flt.strVendorId.length())
435 pelmFilter->setAttribute("vendorId", flt.strVendorId);
436 if (flt.strProductId.length())
437 pelmFilter->setAttribute("productId", flt.strProductId);
438 if (flt.strRevision.length())
439 pelmFilter->setAttribute("revision", flt.strRevision);
440 if (flt.strManufacturer.length())
441 pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
442 if (flt.strProduct.length())
443 pelmFilter->setAttribute("product", flt.strProduct);
444 if (flt.strSerialNumber.length())
445 pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
446 if (flt.strPort.length())
447 pelmFilter->setAttribute("port", flt.strPort);
448
449 if (fHostMode)
450 {
451 const char *pcsz =
452 (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
453 : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
454 pelmFilter->setAttribute("action", pcsz);
455 }
456 else
457 {
458 if (flt.strRemote.length())
459 pelmFilter->setAttribute("remote", flt.strRemote);
460 if (flt.ulMaskedInterfaces)
461 pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
462 }
463 }
464}
465
466/**
467 * Cleans up memory allocated by the internal XML parser. To be called by
468 * descendant classes when they're done analyzing the DOM tree to discard it.
469 */
470void ConfigFileBase::clearDocument()
471{
472 m->cleanup();
473}
474
475/**
476 * Returns true only if the underlying config file exists on disk;
477 * either because the file has been loaded from disk, or it's been written
478 * to disk, or both.
479 * @return
480 */
481bool ConfigFileBase::fileExists()
482{
483 return m->fFileExists;
484}
485
486/**
487 * Reads one <MachineEntry> from the main VirtualBox.xml file.
488 * @param elmMachineRegistry
489 */
490void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
491{
492 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
493 xml::NodesLoop nl1(elmMachineRegistry);
494 const xml::ElementNode *pelmChild1;
495 while ((pelmChild1 = nl1.forAllNodes()))
496 {
497 if (pelmChild1->nameEquals("MachineEntry"))
498 {
499 MachineRegistryEntry mre;
500 Utf8Str strUUID;
501 if ( ((pelmChild1->getAttributeValue("uuid", strUUID)))
502 && ((pelmChild1->getAttributeValue("src", mre.strSettingsFile)))
503 )
504 {
505 parseUUID(mre.uuid, strUUID);
506 llMachines.push_back(mre);
507 }
508 else
509 throw ConfigFileError(this, N_("MachineEntry element must have uuid and src attributes"));
510 }
511 else
512 throw ConfigFileError(this, N_("Invalid element %s in MachineRegistry section"), pelmChild1->getName());
513 }
514}
515
516/**
517 * Reads a media registry entry from the main VirtualBox.xml file.
518 * @param t
519 * @param elmMedium
520 * @param llMedia
521 */
522void MainConfigFile::readMedium(MediaType t,
523 const xml::ElementNode &elmMedium, // MediaRegistry/HardDisks or a single HardDisk node if recursing
524 MediaList &llMedia)
525{
526 // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
527 settings::Medium med;
528 Utf8Str strUUID;
529 if ( (elmMedium.getAttributeValue("uuid", strUUID))
530 && (elmMedium.getAttributeValue("location", med.strLocation))
531 )
532 {
533 parseUUID(med.uuid, strUUID);
534 elmMedium.getAttributeValue("Description", med.strDescription); // optional
535
536 if (t == HardDisk)
537 {
538 if (!(elmMedium.getAttributeValue("format", med.strFormat)))
539 throw ConfigFileError(this, N_("HardDisk element must have format attribute"));
540
541 if (!(elmMedium.getAttributeValue("autoReset", med.fAutoReset)))
542 med.fAutoReset = false;
543
544 Utf8Str strType;
545 if ((elmMedium.getAttributeValue("type", strType)))
546 {
547 if (strType == "Normal")
548 med.hdType = HardDiskType_Normal;
549 else if (strType == "Immutable")
550 med.hdType = HardDiskType_Immutable;
551 else if (strType == "Writethrough")
552 med.hdType = HardDiskType_Writethrough;
553 else
554 throw ConfigFileError(this, N_("HardDisk/type attribute must be one of Normal, Immutable or Writethrough"));
555 }
556 }
557
558 // recurse to handle children
559 xml::NodesLoop nl2(elmMedium);
560 const xml::ElementNode *pelmHDChild;
561 while ((pelmHDChild = nl2.forAllNodes()))
562 {
563 if ( t == HardDisk
564 && (pelmHDChild->nameEquals("HardDisk"))
565 )
566 // recurse with this element and push the child onto our current children list
567 readMedium(t,
568 *pelmHDChild,
569 med.llChildren);
570 else if (pelmHDChild->nameEquals("Property"))
571 {
572 Utf8Str strPropName, strPropValue;
573 if ( (pelmHDChild->getAttributeValue("name", strPropName))
574 && (pelmHDChild->getAttributeValue("value", strPropValue))
575 )
576 med.properties[strPropName] = strPropValue;
577 else
578 throw ConfigFileError(this, N_("HardDisk/Property element must have name and value attributes"));
579 }
580 }
581
582 llMedia.push_back(med);
583 }
584 else
585 throw ConfigFileError(this, N_("%s element must have uuid and location attributes"), elmMedium.getName());
586}
587
588/**
589 * Reads in the entire <MediaRegistry> chunk.
590 * @param elmMediaRegistry
591 */
592void MainConfigFile::readMediaRegistry(const xml::ElementNode &elmMediaRegistry)
593{
594 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
595 xml::NodesLoop nl1(elmMediaRegistry);
596 const xml::ElementNode *pelmChild1;
597 while ((pelmChild1 = nl1.forAllNodes()))
598 {
599 MediaType t = Error;
600 if (pelmChild1->nameEquals("HardDisks"))
601 t = HardDisk;
602 else if (pelmChild1->nameEquals("DVDImages"))
603 t = DVDImage;
604 else if (pelmChild1->nameEquals("FloppyImages"))
605 t = FloppyImage;
606 else
607 throw ConfigFileError(this, N_("Invalid element %s in MediaRegistry section"), pelmChild1->getName());
608
609 xml::NodesLoop nl1(*pelmChild1);
610 const xml::ElementNode *pelmMedium;
611 while ((pelmMedium = nl1.forAllNodes()))
612 {
613 if ( t == HardDisk
614 && (pelmMedium->nameEquals("HardDisk"))
615 )
616 readMedium(t,
617 *pelmMedium,
618 llHardDisks); // list to append hard disk data to: the root list
619 else if ( t == DVDImage
620 && (pelmMedium->nameEquals("Image"))
621 )
622 readMedium(t,
623 *pelmMedium,
624 llDvdImages); // list to append dvd images to: the root list
625 else if ( t == FloppyImage
626 && (pelmMedium->nameEquals("Image"))
627 )
628 readMedium(t,
629 *pelmMedium,
630 llFloppyImages); // list to append floppy images to: the root list
631 }
632 }
633}
634
635/**
636 * Reads in the <DHCPServers> chunk.
637 * @param elmDHCPServers
638 */
639void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
640{
641 xml::NodesLoop nl1(elmDHCPServers);
642 const xml::ElementNode *pelmServer;
643 while ((pelmServer = nl1.forAllNodes()))
644 {
645 if (pelmServer->nameEquals("DHCPServer"))
646 {
647 DHCPServer srv;
648 if ( (pelmServer->getAttributeValue("networkName", srv.strNetworkName))
649 && (pelmServer->getAttributeValue("IPAddress", srv.strIPAddress))
650 && (pelmServer->getAttributeValue("networkMask", srv.strIPNetworkMask))
651 && (pelmServer->getAttributeValue("lowerIP", srv.strIPLower))
652 && (pelmServer->getAttributeValue("upperIP", srv.strIPUpper))
653 && (pelmServer->getAttributeValue("enabled", srv.fEnabled))
654 )
655 llDhcpServers.push_back(srv);
656 else
657 throw ConfigFileError(this, N_("DHCPServer element must have networkName, IPAddress, networkMask, lowerIP, upperIP and enabled attributes"));
658 }
659 else
660 throw ConfigFileError(this, N_("Invalid element %s in DHCPServers section"), pelmServer->getName());
661 }
662}
663
664/**
665 * Constructor.
666 *
667 * If pstrFilename is != NULL, this reads the given settings file into the member
668 * variables and various substructures and lists. Otherwise, the member variables
669 * are initialized with default values.
670 *
671 * Throws variants of xml::Error for I/O, XML and logical content errors, which
672 * the caller should catch; if this constructor does not throw, then the file has
673 * been successfully read.
674 *
675 * @param strFilename
676 */
677MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
678 : ConfigFileBase(pstrFilename)
679{
680 if (pstrFilename)
681 {
682 // the ConfigFileBase constructor has loaded the XML file, so now
683 // we need only analyze what is in there
684 xml::NodesLoop nlRootChildren(*m->pelmRoot);
685 const xml::ElementNode *pelmRootChild;
686 while ((pelmRootChild = nlRootChildren.forAllNodes()))
687 {
688 if (pelmRootChild->nameEquals("Global"))
689 {
690 xml::NodesLoop nlGlobalChildren(*pelmRootChild);
691 const xml::ElementNode *pelmGlobalChild;
692 while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
693 {
694 if (pelmGlobalChild->nameEquals("SystemProperties"))
695 {
696 pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
697 pelmGlobalChild->getAttributeValue("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder);
698 pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
699 pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
700 pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
701 pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.ulLogHistoryCount);
702 }
703 else if (pelmGlobalChild->nameEquals("ExtraData"))
704 readExtraData(*pelmGlobalChild, mapExtraDataItems);
705 else if (pelmGlobalChild->nameEquals("MachineRegistry"))
706 readMachineRegistry(*pelmGlobalChild);
707 else if (pelmGlobalChild->nameEquals("MediaRegistry"))
708 readMediaRegistry(*pelmGlobalChild);
709 else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
710 {
711 xml::NodesLoop nlLevel4(*pelmGlobalChild);
712 const xml::ElementNode *pelmLevel4Child;
713 while ((pelmLevel4Child = nlLevel4.forAllNodes()))
714 {
715 if (pelmLevel4Child->nameEquals("DHCPServers"))
716 readDHCPServers(*pelmLevel4Child);
717 else
718 throw ConfigFileError(this, N_("Invalid element %s in NetserviceRegistry section"), pelmLevel4Child->getName());
719 }
720 }
721 else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
722 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
723 else
724 throw ConfigFileError(this, N_("Invalid element %s in Global section"), pelmGlobalChild->getName());
725 }
726 } // end if (pelmRootChild->nameEquals("Global"))
727 }
728
729 clearDocument();
730 }
731}
732
733/**
734 * Writes out a single <HardDisk> element for the given Medium structure
735 * and recurses to write the child hard disks underneath. Called from
736 * MainConfigFile::write().
737 * @param elmMedium
738 * @param m
739 * @param level
740 */
741void MainConfigFile::writeHardDisk(xml::ElementNode &elmMedium,
742 const Medium &m,
743 uint32_t level) // 0 for "root" call, incremented with each recursion
744{
745 xml::ElementNode *pelmHardDisk = elmMedium.createChild("HardDisk");
746 pelmHardDisk->setAttribute("uuid", makeString(m.uuid));
747 pelmHardDisk->setAttribute("location", m.strLocation);
748 pelmHardDisk->setAttribute("format", m.strFormat);
749 if (m.fAutoReset)
750 pelmHardDisk->setAttribute("autoReset", m.fAutoReset);
751 if (m.strDescription.length())
752 pelmHardDisk->setAttribute("Description", m.strDescription);
753
754 for (PropertiesMap::const_iterator it = m.properties.begin();
755 it != m.properties.end();
756 ++it)
757 {
758 xml::ElementNode *pelmProp = pelmHardDisk->createChild("Property");
759 pelmProp->setAttribute("name", it->first);
760 pelmProp->setAttribute("value", it->second);
761 }
762
763 // only for base hard disks, save the type
764 if (level == 0)
765 {
766 const char *pcszType =
767 m.hdType == HardDiskType_Normal ? "Normal" :
768 m.hdType == HardDiskType_Immutable ? "Immutable" :
769 /*m.hdType == HardDiskType_Writethrough ?*/ "Writethrough";
770 pelmHardDisk->setAttribute("type", pcszType);
771 }
772
773 for (MediaList::const_iterator it = m.llChildren.begin();
774 it != m.llChildren.end();
775 ++it)
776 {
777 // recurse for children
778 writeHardDisk(*pelmHardDisk, // parent
779 *it, // settings::Medium
780 ++level); // recursion level
781 }
782}
783
784/**
785 * Called from the IMachine interface to write out a machine config file. This
786 * builds an XML DOM tree and writes it out to disk.
787 */
788void MainConfigFile::write(const com::Utf8Str strFilename)
789{
790 m->strFilename = strFilename;
791 createStubDocument();
792
793 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
794
795 writeExtraData(*pelmGlobal, mapExtraDataItems);
796
797 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
798 for (MachinesRegistry::const_iterator it = llMachines.begin();
799 it != llMachines.end();
800 ++it)
801 {
802 // <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"/>
803 const MachineRegistryEntry &mre = *it;
804 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
805 pelmMachineEntry->setAttribute("uuid", makeString(mre.uuid));
806 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
807 }
808
809 xml::ElementNode *pelmMediaRegistry = pelmGlobal->createChild("MediaRegistry");
810
811 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
812 for (MediaList::const_iterator it = llHardDisks.begin();
813 it != llHardDisks.end();
814 ++it)
815 {
816 writeHardDisk(*pelmHardDisks, *it, 0);
817 }
818
819 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
820 for (MediaList::const_iterator it = llDvdImages.begin();
821 it != llDvdImages.end();
822 ++it)
823 {
824 const Medium &m = *it;
825 xml::ElementNode *pelmMedium = pelmDVDImages->createChild("Image");
826 pelmMedium->setAttribute("uuid", makeString(m.uuid));
827 pelmMedium->setAttribute("location", m.strLocation);
828 if (m.strDescription.length())
829 pelmMedium->setAttribute("Description", m.strDescription);
830 }
831
832 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
833 for (MediaList::const_iterator it = llFloppyImages.begin();
834 it != llFloppyImages.end();
835 ++it)
836 {
837 const Medium &m = *it;
838 xml::ElementNode *pelmMedium = pelmFloppyImages->createChild("Image");
839 pelmMedium->setAttribute("uuid", makeString(m.uuid));
840 pelmMedium->setAttribute("location", m.strLocation);
841 if (m.strDescription.length())
842 pelmMedium->setAttribute("Description", m.strDescription);
843 }
844
845 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
846 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
847 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
848 it != llDhcpServers.end();
849 ++it)
850 {
851 const DHCPServer &d = *it;
852 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
853 pelmThis->setAttribute("networkName", d.strNetworkName);
854 pelmThis->setAttribute("IPAddress", d.strIPAddress);
855 pelmThis->setAttribute("networkMask", d.strIPNetworkMask);
856 pelmThis->setAttribute("lowerIP", d.strIPLower);
857 pelmThis->setAttribute("upperIP", d.strIPUpper);
858 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
859 }
860
861 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
862 if (systemProperties.strDefaultMachineFolder.length())
863 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
864 if (systemProperties.strDefaultHardDiskFolder.length())
865 pelmSysProps->setAttribute("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder);
866 if (systemProperties.strDefaultHardDiskFormat.length())
867 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
868 if (systemProperties.strRemoteDisplayAuthLibrary.length())
869 pelmSysProps->setAttribute("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
870 if (systemProperties.strWebServiceAuthLibrary.length())
871 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
872 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.ulLogHistoryCount);
873
874 writeUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
875 host.llUSBDeviceFilters,
876 true); // fHostMode
877
878 // now go write the XML
879 xml::XmlFileWriter writer(*m->pDoc);
880 writer.write(m->strFilename.c_str());
881
882 m->fFileExists = true;
883
884 clearDocument();
885}
886
887/**
888 * @param elmNetwork
889 * @param ll
890 */
891void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
892 NetworkAdaptersList &ll)
893{
894 xml::NodesLoop nl1(elmNetwork, "Adapter");
895 const xml::ElementNode *pelmAdapter;
896 while ((pelmAdapter = nl1.forAllNodes()))
897 {
898 NetworkAdapter nic;
899
900 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
901 throw ConfigFileError(this, N_("Required Adapter/slot attribute is missing"));
902
903 Utf8Str strTemp;
904 if (pelmAdapter->getAttributeValue("type", strTemp))
905 {
906 if (strTemp == "Am79C970A")
907 nic.type = NetworkAdapterType_Am79C970A;
908 else if (strTemp == "Am79C973")
909 nic.type = NetworkAdapterType_Am79C973;
910 else if (strTemp == "82540EM")
911 nic.type = NetworkAdapterType_I82540EM;
912 else if (strTemp == "82543GC")
913 nic.type = NetworkAdapterType_I82543GC;
914 else if (strTemp == "82545EM")
915 nic.type = NetworkAdapterType_I82545EM;
916 else
917 throw ConfigFileError(this, N_("Invalid value %s in Adapter/type attribute"), strTemp.c_str());
918 }
919
920 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
921 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
922 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
923 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
924 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
925 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
926
927 const xml::ElementNode *pelmAdapterChild;
928 if ((pelmAdapterChild = pelmAdapter->findChildElement("NAT")))
929 {
930 nic.mode = NetworkAttachmentType_NAT;
931 pelmAdapterChild->getAttributeValue("name", nic.strName); // optional network name
932 }
933 else if ( ((pelmAdapterChild = pelmAdapter->findChildElement("HostInterface")))
934 || ((pelmAdapterChild = pelmAdapter->findChildElement("BridgedInterface")))
935 )
936 {
937 nic.mode = NetworkAttachmentType_Bridged;
938 pelmAdapterChild->getAttributeValue("name", nic.strName); // optional host interface name
939 }
940 else if ((pelmAdapterChild = pelmAdapter->findChildElement("InternalNetwork")))
941 {
942 nic.mode = NetworkAttachmentType_Internal;
943 if (!pelmAdapterChild->getAttributeValue("name", nic.strName)) // required network name
944 throw ConfigFileError(this, N_("Required 'name' element is missing under 'InternalNetwork' element"));
945 }
946 else if ((pelmAdapterChild = pelmAdapter->findChildElement("HostOnlyInterface")))
947 {
948 nic.mode = NetworkAttachmentType_HostOnly;
949 if (!pelmAdapterChild->getAttributeValue("name", nic.strName)) // required network name
950 throw ConfigFileError(this, N_("Required 'name' element is missing under 'HostOnlyInterface' element"));
951 }
952 // else: default is NetworkAttachmentType_Null
953
954 ll.push_back(nic);
955 }
956}
957
958/**
959 * Called from MachineConfigFile::readHardware() to read serial port information.
960 * @param elmUART
961 * @param ll
962 */
963void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
964 SerialPortsList &ll)
965{
966 xml::NodesLoop nl1(elmUART, "Port");
967 const xml::ElementNode *pelmPort;
968 while ((pelmPort = nl1.forAllNodes()))
969 {
970 SerialPort port;
971 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
972 throw ConfigFileError(this, N_("Required UART/Port/slot attribute is missing"));
973
974 // slot must be unique
975 for (SerialPortsList::const_iterator it = ll.begin();
976 it != ll.end();
977 ++it)
978 if ((*it).ulSlot == port.ulSlot)
979 throw ConfigFileError(this, N_("UART/Port/slot attribute value %d is used twice, must be unique"), port.ulSlot);
980
981 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
982 throw ConfigFileError(this, N_("Required UART/Port/enabled attribute is missing"));
983 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
984 throw ConfigFileError(this, N_("Required UART/Port/IOBase attribute is missing"));
985 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
986 throw ConfigFileError(this, N_("Required UART/Port/IRQ attribute is missing"));
987
988 Utf8Str strPortMode;
989 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
990 throw ConfigFileError(this, N_("Required UART/Port/hostMode attribute is missing"));
991 if (strPortMode == "RawFile")
992 port.portMode = PortMode_RawFile;
993 else if (strPortMode == "HostPipe")
994 port.portMode = PortMode_HostPipe;
995 else if (strPortMode == "HostDevice")
996 port.portMode = PortMode_HostDevice;
997 else if (strPortMode == "Disconnected")
998 port.portMode = PortMode_Disconnected;
999 else
1000 throw ConfigFileError(this, N_("Invalid value %s in UART/Port/hostMode attribute"), strPortMode.c_str());
1001
1002 pelmPort->getAttributeValue("path", port.strPath);
1003 pelmPort->getAttributeValue("server", port.fServer);
1004
1005 ll.push_back(port);
1006 }
1007}
1008
1009/**
1010 * Called from MachineConfigFile::readHardware() to read parallel port information.
1011 * @param elmLPT
1012 * @param ll
1013 */
1014void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
1015 ParallelPortsList &ll)
1016{
1017 xml::NodesLoop nl1(elmLPT, "Port");
1018 const xml::ElementNode *pelmPort;
1019 while ((pelmPort = nl1.forAllNodes()))
1020 {
1021 ParallelPort port;
1022 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
1023 throw ConfigFileError(this, N_("Required LPT/Port/slot attribute is missing"));
1024
1025 // slot must be unique
1026 for (ParallelPortsList::const_iterator it = ll.begin();
1027 it != ll.end();
1028 ++it)
1029 if ((*it).ulSlot == port.ulSlot)
1030 throw ConfigFileError(this, N_("LPT/Port/slot attribute value %d is used twice, must be unique"), port.ulSlot);
1031
1032 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
1033 throw ConfigFileError(this, N_("Required LPT/Port/enabled attribute is missing"));
1034 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
1035 throw ConfigFileError(this, N_("Required LPT/Port/IOBase attribute is missing"));
1036 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
1037 throw ConfigFileError(this, N_("Required LPT/Port/IRQ attribute is missing"));
1038
1039 pelmPort->getAttributeValue("path", port.strPath);
1040
1041 ll.push_back(port);
1042 }
1043}
1044
1045/**
1046 * Called from MachineConfigFile::readHardware() to read guest property information.
1047 * @param elmGuestProperties
1048 * @param hw
1049 */
1050void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
1051 Hardware &hw)
1052{
1053 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
1054 const xml::ElementNode *pelmProp;
1055 while ((pelmProp = nl1.forAllNodes()))
1056 {
1057 GuestProperty prop;
1058 pelmProp->getAttributeValue("name", prop.strName);
1059 pelmProp->getAttributeValue("value", prop.strValue);
1060
1061 pelmProp->getAttributeValue("timestamp", prop.timestamp);
1062 pelmProp->getAttributeValue("flags", prop.strFlags);
1063 hw.llGuestProperties.push_back(prop);
1064 }
1065
1066 elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
1067}
1068
1069/**
1070 * Reads in a <Hardware> block and stores it in the given structure. Used
1071 * both directly from readMachine and from readSnapshot, since snapshots
1072 * have their own hardware sections.
1073 * @param elmHardware
1074 * @param hw
1075 */
1076void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
1077 Hardware &hw)
1078{
1079 elmHardware.getAttributeValue("version", hw.strVersion);
1080 // defaults to 2 and is only written if != 2
1081
1082 xml::NodesLoop nl1(elmHardware);
1083 const xml::ElementNode *pelmHwChild;
1084 while ((pelmHwChild = nl1.forAllNodes()))
1085 {
1086 if (pelmHwChild->nameEquals("CPU"))
1087 {
1088 pelmHwChild->getAttributeValue("count", hw.cCPUs);
1089
1090 const xml::ElementNode *pelmCPUChild;
1091 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
1092 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
1093 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
1094 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
1095 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
1096 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
1097 if ((pelmCPUChild = pelmHwChild->findChildElement("PAE")))
1098 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
1099 }
1100 else if (pelmHwChild->nameEquals("Memory"))
1101 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
1102 else if (pelmHwChild->nameEquals("Boot"))
1103 {
1104 xml::NodesLoop nl2(*pelmHwChild, "Order");
1105 const xml::ElementNode *pelmOrder;
1106 while ((pelmOrder = nl2.forAllNodes()))
1107 {
1108 uint32_t ulPos;
1109 Utf8Str strDevice;
1110 if (!pelmOrder->getAttributeValue("position", ulPos))
1111 throw ConfigFileError(this, N_("'Order' element lacks 'position' attribute"));
1112 if (!pelmOrder->getAttributeValue("device", strDevice))
1113 throw ConfigFileError(this, N_("'Order' element lacks 'device' attribute"));
1114 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
1115 throw ConfigFileError(this, N_("Value %d of 'position' attribute in 'Order' element is not unique"), ulPos);
1116
1117 DeviceType_T type;
1118 if (strDevice == "None")
1119 type = DeviceType_Null;
1120 else if (strDevice == "Floppy")
1121 type = DeviceType_Floppy;
1122 else if (strDevice == "DVD")
1123 type = DeviceType_DVD;
1124 else if (strDevice == "HardDisk")
1125 type = DeviceType_HardDisk;
1126 else if (strDevice == "Network")
1127 type = DeviceType_Network;
1128 else
1129 throw ConfigFileError(this, N_("Invalid value %s in Boot/Order/device attribute"), strDevice.c_str());
1130 hw.mapBootOrder[ulPos] = type;
1131 }
1132 }
1133 else if (pelmHwChild->nameEquals("Display"))
1134 {
1135 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
1136 pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors);
1137 pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D);
1138 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
1139 }
1140 else if (pelmHwChild->nameEquals("RemoteDisplay"))
1141 {
1142 pelmHwChild->getAttributeValue("enabled", hw.vrdpSettings.fEnabled);
1143 pelmHwChild->getAttributeValue("port", hw.vrdpSettings.ulPort);
1144 pelmHwChild->getAttributeValue("netAddress", hw.vrdpSettings.strNetAddress);
1145
1146 Utf8Str strAuthType;
1147 if (pelmHwChild->getAttributeValue("authType", strAuthType))
1148 {
1149 if (strAuthType == "Null")
1150 hw.vrdpSettings.authType = VRDPAuthType_Null;
1151 else if (strAuthType == "Guest")
1152 hw.vrdpSettings.authType = VRDPAuthType_Guest;
1153 else if (strAuthType == "External")
1154 hw.vrdpSettings.authType = VRDPAuthType_External;
1155 else
1156 throw ConfigFileError(this, N_("Invalid value %s in RemoteDisplay/authType attribute"), strAuthType.c_str());
1157 }
1158
1159 pelmHwChild->getAttributeValue("authTimeout", hw.vrdpSettings.ulAuthTimeout);
1160 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
1161 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
1162 }
1163 else if (pelmHwChild->nameEquals("BIOS"))
1164 {
1165 const xml::ElementNode *pelmBIOSChild;
1166 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
1167 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
1168 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
1169 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
1170 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
1171 {
1172 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
1173 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
1174 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
1175 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
1176 }
1177 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
1178 {
1179 Utf8Str strBootMenuMode;
1180 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
1181 {
1182 if (strBootMenuMode == "Disabled")
1183 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
1184 else if (strBootMenuMode == "MenuOnly")
1185 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
1186 else if (strBootMenuMode == "MessageAndMenu")
1187 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
1188 else
1189 throw ConfigFileError(this, N_("Invalid value %s in BootMenu/mode attribute"), strBootMenuMode.c_str());
1190 }
1191 }
1192 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
1193 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
1194 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
1195 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
1196 }
1197 else if (pelmHwChild->nameEquals("DVDDrive"))
1198 {
1199 const xml::ElementNode *pDriveChild;
1200 pelmHwChild->getAttributeValue("passthrough", hw.dvdDrive.fPassThrough);
1201 Utf8Str strTmp;
1202 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
1203 && (pDriveChild->getAttributeValue("uuid", strTmp))
1204 )
1205 parseUUID(hw.dvdDrive.uuid, strTmp);
1206 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
1207 pDriveChild->getAttributeValue("src", hw.dvdDrive.strHostDriveSrc);
1208 }
1209 else if (pelmHwChild->nameEquals("FloppyDrive"))
1210 {
1211 const xml::ElementNode *pDriveChild;
1212 pelmHwChild->getAttributeValue("enabled", hw.floppyDrive.fEnabled);
1213 Utf8Str strTmp;
1214 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
1215 && (pDriveChild->getAttributeValue("uuid", strTmp))
1216 )
1217 parseUUID(hw.floppyDrive.uuid, strTmp);
1218 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
1219 pDriveChild->getAttributeValue("src", hw.floppyDrive.strHostDriveSrc);
1220 }
1221 else if (pelmHwChild->nameEquals("USBController"))
1222 {
1223 pelmHwChild->getAttributeValue("enabled", hw.usbController.fEnabled);
1224 pelmHwChild->getAttributeValue("enabledEhci", hw.usbController.fEnabledEHCI);
1225
1226 readUSBDeviceFilters(*pelmHwChild,
1227 hw.usbController.llDeviceFilters);
1228 }
1229 else if (pelmHwChild->nameEquals("Network"))
1230 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
1231 else if (pelmHwChild->nameEquals("UART"))
1232 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
1233 else if (pelmHwChild->nameEquals("LPT"))
1234 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
1235 else if (pelmHwChild->nameEquals("AudioAdapter"))
1236 {
1237 pelmHwChild->getAttributeValue("enabled", hw.audioAdapter.fEnabled);
1238
1239 Utf8Str strTemp;
1240 if (pelmHwChild->getAttributeValue("controller", strTemp))
1241 {
1242 if (strTemp == "SB16")
1243 hw.audioAdapter.controllerType = AudioControllerType_SB16;
1244 else if (strTemp == "AC97")
1245 hw.audioAdapter.controllerType = AudioControllerType_AC97;
1246 else
1247 throw ConfigFileError(this, N_("Invalid value %s in AudioAdapter/controller attribute"), strTemp.c_str());
1248 }
1249 if (pelmHwChild->getAttributeValue("driver", strTemp))
1250 {
1251 if (strTemp == "Null")
1252 hw.audioAdapter.driverType = AudioDriverType_Null;
1253 else if (strTemp == "WinMM")
1254 hw.audioAdapter.driverType = AudioDriverType_WinMM;
1255 else if (strTemp == "DirectSound")
1256 hw.audioAdapter.driverType = AudioDriverType_DirectSound;
1257 else if (strTemp == "SolAudio")
1258 hw.audioAdapter.driverType = AudioDriverType_SolAudio;
1259 else if (strTemp == "ALSA")
1260 hw.audioAdapter.driverType = AudioDriverType_ALSA;
1261 else if (strTemp == "Pulse")
1262 hw.audioAdapter.driverType = AudioDriverType_Pulse;
1263 else if (strTemp == "OSS")
1264 hw.audioAdapter.driverType = AudioDriverType_OSS;
1265 else if (strTemp == "CoreAudio")
1266 hw.audioAdapter.driverType = AudioDriverType_CoreAudio;
1267 else if (strTemp == "MMPM")
1268 hw.audioAdapter.driverType = AudioDriverType_MMPM;
1269 else
1270 throw ConfigFileError(this, N_("Invalid value %s in AudioAdapter/driver attribute"), strTemp.c_str());
1271 }
1272 }
1273 else if (pelmHwChild->nameEquals("SharedFolders"))
1274 {
1275 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
1276 const xml::ElementNode *pelmFolder;
1277 while ((pelmFolder = nl2.forAllNodes()))
1278 {
1279 SharedFolder sf;
1280 pelmFolder->getAttributeValue("name", sf.strName);
1281 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
1282 pelmFolder->getAttributeValue("writable", sf.fWritable);
1283 hw.llSharedFolders.push_back(sf);
1284 }
1285 }
1286 else if (pelmHwChild->nameEquals("Clipboard"))
1287 {
1288 Utf8Str strTemp;
1289 if (pelmHwChild->getAttributeValue("mode", strTemp))
1290 {
1291 if (strTemp == "Disabled")
1292 hw.clipboardMode = ClipboardMode_Disabled;
1293 else if (strTemp == "HostToGuest")
1294 hw.clipboardMode = ClipboardMode_HostToGuest;
1295 else if (strTemp == "GuestToHost")
1296 hw.clipboardMode = ClipboardMode_GuestToHost;
1297 else if (strTemp == "Bidirectional")
1298 hw.clipboardMode = ClipboardMode_Bidirectional;
1299 else
1300 throw ConfigFileError(this, N_("Invalid value %s in Clipbord/mode attribute"), strTemp.c_str());
1301 }
1302 }
1303 else if (pelmHwChild->nameEquals("Guest"))
1304 {
1305 pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize);
1306 pelmHwChild->getAttributeValue("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
1307 }
1308 else if (pelmHwChild->nameEquals("GuestProperties"))
1309 readGuestProperties(*pelmHwChild, hw);
1310 else
1311 throw ConfigFileError(this, N_("Invalid element %s in Hardware section"), pelmHwChild->getName());
1312 }
1313
1314 if (hw.ulMemorySizeMB == (uint32_t)-1)
1315 throw ConfigFileError(this, N_("Required Memory/RAMSize element/attribute is missing"));
1316}
1317
1318/**
1319 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
1320 * Used both directly from readMachine and from readSnapshot, since snapshots
1321 * have their own storage controllers sections.
1322 *
1323 * @param elmStorageControllers
1324 */
1325void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
1326 Storage &strg)
1327{
1328 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
1329 const xml::ElementNode *pelmController;
1330 while ((pelmController = nlStorageControllers.forAllNodes()))
1331 {
1332 StorageController sctl;
1333
1334 if (!pelmController->getAttributeValue("name", sctl.strName))
1335 throw ConfigFileError(this, N_("Required StorageController/name attribute is missing"));
1336 Utf8Str strType;
1337 if (!pelmController->getAttributeValue("type", strType))
1338 throw ConfigFileError(this, N_("Required StorageController/type attribute is missing"));
1339
1340 if (strType == "AHCI")
1341 {
1342 sctl.storageBus = StorageBus_SATA;
1343 sctl.controllerType = StorageControllerType_IntelAhci;
1344 }
1345 else if (strType == "LsiLogic")
1346 {
1347 sctl.storageBus = StorageBus_SCSI;
1348 sctl.controllerType = StorageControllerType_LsiLogic;
1349 }
1350 else if (strType == "BusLogic")
1351 {
1352 sctl.storageBus = StorageBus_SCSI;
1353 sctl.controllerType = StorageControllerType_BusLogic;
1354 }
1355 else if (strType == "PIIX3")
1356 {
1357 sctl.storageBus = StorageBus_IDE;
1358 sctl.controllerType = StorageControllerType_PIIX3;
1359 }
1360 else if (strType == "PIIX4")
1361 {
1362 sctl.storageBus = StorageBus_IDE;
1363 sctl.controllerType = StorageControllerType_PIIX4;
1364 }
1365 else if (strType == "ICH6")
1366 {
1367 sctl.storageBus = StorageBus_IDE;
1368 sctl.controllerType = StorageControllerType_ICH6;
1369 }
1370 else
1371 throw ConfigFileError(this, N_("Invalid value %s for StorageController/type attribute"), strType.c_str());
1372
1373 pelmController->getAttributeValue("PortCount", sctl.ulPortCount);
1374
1375 pelmController->getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
1376 pelmController->getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
1377 pelmController->getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
1378 pelmController->getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
1379
1380 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
1381 const xml::ElementNode *pelmAttached;
1382 while ((pelmAttached = nlAttached.forAllNodes()))
1383 {
1384 AttachedDevice att;
1385
1386 Utf8Str strTemp;
1387 pelmAttached->getAttributeValue("type", strTemp);
1388 if (strTemp != "HardDisk")
1389 throw ConfigFileError(this, N_("AttachedDevice element must have 'HardDisk' type"));
1390
1391 const xml::ElementNode *pelmImage;
1392 if (!(pelmImage = pelmAttached->findChildElement("Image")))
1393 throw ConfigFileError(this, N_("Required AttachedDevice/Image element is missing"));
1394
1395 if (!pelmImage->getAttributeValue("uuid", strTemp))
1396 throw ConfigFileError(this, N_("Required AttachedDevice/Image/uuid attributeis missing"));
1397 parseUUID(att.uuid, strTemp);
1398
1399 if (!pelmAttached->getAttributeValue("port", att.lPort))
1400 throw ConfigFileError(this, N_("Required AttachedDevice/port attribute is missing"));
1401 if (!pelmAttached->getAttributeValue("device", att.lDevice))
1402 throw ConfigFileError(this, N_("Required AttachedDevice/device attribute is missing"));
1403
1404 sctl.llAttachedDevices.push_back(att);
1405 }
1406
1407 strg.llStorageControllers.push_back(sctl);
1408 }
1409}
1410
1411/**
1412 * Called initially for the <Snapshot> element under <Machine>, if present,
1413 * to store the snapshot's data into the given Snapshot structure (which is
1414 * then the one in the Machine struct). This might then recurse if
1415 * a <Snapshots> (plural) element is found in the snapshot, which should
1416 * contain a list of child snapshots; such lists are maintained in the
1417 * Snapshot structure.
1418 *
1419 * @param elmSnapshot
1420 * @param snap
1421 */
1422void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
1423 Snapshot &snap)
1424{
1425 Utf8Str strTemp;
1426
1427 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
1428 throw ConfigFileError(this, N_("Required Snapshot/uuid attribute is missing"));
1429 parseUUID(snap.uuid, strTemp);
1430
1431 if (!elmSnapshot.getAttributeValue("name", snap.strName))
1432 throw ConfigFileError(this, N_("Required Snapshot/name attribute is missing"));
1433
1434 elmSnapshot.getAttributeValue("Description", snap.strDescription);
1435
1436 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
1437 throw ConfigFileError(this, N_("Required Snapshot/timeStamp attribute is missing"));
1438 parseTimestamp(snap.timestamp, strTemp);
1439
1440 elmSnapshot.getAttributeValue("stateFile", snap.strStateFile); // online snapshots only
1441
1442 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
1443 const xml::ElementNode *pelmSnapshotChild;
1444 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
1445 {
1446 if (pelmSnapshotChild->nameEquals("Description"))
1447 snap.strDescription = pelmSnapshotChild->getValue();
1448 else if (pelmSnapshotChild->nameEquals("Hardware"))
1449 readHardware(*pelmSnapshotChild, snap.hardware);
1450 else if (pelmSnapshotChild->nameEquals("StorageControllers"))
1451 readStorageControllers(*pelmSnapshotChild, snap.storage);
1452 else if (pelmSnapshotChild->nameEquals("Snapshots"))
1453 {
1454 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
1455 const xml::ElementNode *pelmChildSnapshot;
1456 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
1457 {
1458 if (pelmChildSnapshot->nameEquals("Snapshot"))
1459 {
1460 Snapshot child;
1461 readSnapshot(*pelmChildSnapshot, child);
1462 snap.llChildSnapshots.push_back(child);
1463 }
1464 else
1465 throw ConfigFileError(this, N_("Invalid element %s under Snapshots element"), pelmChildSnapshot->getName());
1466 }
1467 }
1468 else
1469 throw ConfigFileError(this, N_("Invalid element %s under Snapshot element"), pelmSnapshotChild->getName());
1470 }
1471}
1472
1473/**
1474 * Called from the constructor to actually read in the Machine element
1475 * of a machine config file.
1476 * @param elmMachine
1477 */
1478void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
1479{
1480 Utf8Str strUUID;
1481 if ( (elmMachine.getAttributeValue("uuid", strUUID))
1482 && (elmMachine.getAttributeValue("name", strName))
1483 )
1484 {
1485 parseUUID(uuid, strUUID);
1486
1487 if (!elmMachine.getAttributeValue("nameSync", fNameSync))
1488 fNameSync = true;
1489
1490 Utf8Str str;
1491 elmMachine.getAttributeValue("Description", strDescription);
1492 elmMachine.getAttributeValue("OSType", strOsType);
1493 elmMachine.getAttributeValue("stateFile", strStateFile);
1494 if (elmMachine.getAttributeValue("currentSnapshot", str))
1495 parseUUID(uuidCurrentSnapshot, str);
1496 elmMachine.getAttributeValue("snapshotFolder", strSnapshotFolder);
1497 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
1498 fCurrentStateModified = true;
1499 if (elmMachine.getAttributeValue("lastStateChange", str))
1500 parseTimestamp(timeLastStateChange, str);
1501 // constructor has called RTTimeNow(&timeLastStateChange) before
1502
1503 xml::NodesLoop nlRootChildren(elmMachine);
1504 const xml::ElementNode *pelmMachineChild;
1505 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
1506 {
1507 if (pelmMachineChild->nameEquals("ExtraData"))
1508 readExtraData(*pelmMachineChild,
1509 mapExtraDataItems);
1510 else if (pelmMachineChild->nameEquals("Hardware"))
1511 readHardware(*pelmMachineChild, hardwareMachine);
1512 else if (pelmMachineChild->nameEquals("StorageControllers"))
1513 readStorageControllers(*pelmMachineChild, storageMachine);
1514 else if (pelmMachineChild->nameEquals("Snapshot"))
1515 {
1516 Snapshot snap;
1517 // this will recurse into child snapshots, if necessary
1518 readSnapshot(*pelmMachineChild, snap);
1519 llFirstSnapshot.push_back(snap);
1520 }
1521 else if (pelmMachineChild->nameEquals("Description"))
1522 strDescription = pelmMachineChild->getValue();
1523 else
1524 throw ConfigFileError(this, N_("Invalid element %s under Machine element"), pelmMachineChild->getName());
1525 }
1526 }
1527 else
1528 throw ConfigFileError(this, N_("Machine element must have uuid and name attributes"));
1529}
1530
1531/**
1532 * Constructor.
1533 *
1534 * If pstrFilename is != NULL, this reads the given settings file into the member
1535 * variables and various substructures and lists. Otherwise, the member variables
1536 * are initialized with default values.
1537 *
1538 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1539 * the caller should catch; if this constructor does not throw, then the file has
1540 * been successfully read and parsed.
1541 *
1542 * @param strFilename
1543 */
1544MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
1545 : ConfigFileBase(pstrFilename),
1546 fNameSync(true),
1547 fCurrentStateModified(true),
1548 fAborted(false)
1549{
1550 RTTimeNow(&timeLastStateChange);
1551
1552 if (pstrFilename)
1553 {
1554 // the ConfigFileBase constructor has loaded the XML file, so now
1555 // we need only analyze what is in there
1556
1557 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1558 const xml::ElementNode *pelmRootChild;
1559 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1560 {
1561 if (pelmRootChild->nameEquals("Machine"))
1562 readMachine(*pelmRootChild);
1563 else
1564 throw ConfigFileError(this, N_("Invalid element %s under root element"), pelmRootChild->getName());
1565 }
1566
1567 clearDocument();
1568 }
1569}
1570
1571/**
1572 * Creates a <Hardware> node under elmParent and then writes out the XML
1573 * keys under that. Called for both the <Machine> node and for snapshots.
1574 * @param elmParent
1575 * @param st
1576 */
1577void MachineConfigFile::writeHardware(xml::ElementNode &elmParent,
1578 const Hardware &hw)
1579{
1580 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
1581
1582 if (hw.strVersion != "2")
1583 pelmHardware->setAttribute("version", hw.strVersion);
1584
1585 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
1586 pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
1587 if (hw.fNestedPaging)
1588 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
1589 if (hw.fVPID)
1590 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
1591 if (hw.fPAE)
1592 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
1593 pelmCPU->setAttribute("count", hw.cCPUs);
1594
1595 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
1596 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
1597
1598 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
1599 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
1600 it != hw.mapBootOrder.end();
1601 ++it)
1602 {
1603 uint32_t i = it->first;
1604 DeviceType_T type = it->second;
1605 const char *pcszDevice;
1606
1607 switch (type)
1608 {
1609 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
1610 case DeviceType_DVD: pcszDevice = "DVD"; break;
1611 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
1612 case DeviceType_Network: pcszDevice = "Network"; break;
1613 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
1614 }
1615
1616 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
1617 pelmOrder->setAttribute("position", i);
1618 pelmOrder->setAttribute("device", pcszDevice);
1619 }
1620
1621 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
1622 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
1623 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
1624 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
1625
1626 if (m->sv >= SettingsVersion_v1_8)
1627 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
1628
1629 xml::ElementNode *pelmVRDP = pelmHardware->createChild("RemoteDisplay");
1630 pelmVRDP->setAttribute("enabled", hw.vrdpSettings.fEnabled);
1631 pelmVRDP->setAttribute("port", hw.vrdpSettings.ulPort);
1632 if (hw.vrdpSettings.strNetAddress.length())
1633 pelmVRDP->setAttribute("netAddress", hw.vrdpSettings.strNetAddress);
1634 const char *pcszAuthType;
1635 switch (hw.vrdpSettings.authType)
1636 {
1637 case VRDPAuthType_Guest: pcszAuthType = "Guest"; break;
1638 case VRDPAuthType_External: pcszAuthType = "External"; break;
1639 default: /*case VRDPAuthType_Null:*/ pcszAuthType = "Null"; break;
1640 }
1641 pelmVRDP->setAttribute("authType", pcszAuthType);
1642
1643 if (hw.vrdpSettings.ulAuthTimeout != 0)
1644 pelmVRDP->setAttribute("authTimeout", hw.vrdpSettings.ulAuthTimeout);
1645 if (hw.vrdpSettings.fAllowMultiConnection)
1646 pelmVRDP->setAttribute("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
1647 if (hw.vrdpSettings.fReuseSingleConnection)
1648 pelmVRDP->setAttribute("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
1649
1650 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
1651 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
1652 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
1653
1654 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
1655 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
1656 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
1657 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
1658 if (hw.biosSettings.strLogoImagePath.length())
1659 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
1660
1661 const char *pcszBootMenu;
1662 switch (hw.biosSettings.biosBootMenuMode)
1663 {
1664 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
1665 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
1666 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
1667 }
1668 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
1669 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
1670 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
1671
1672 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
1673 pelmDVD->setAttribute("passthrough", hw.dvdDrive.fPassThrough);
1674 if (!hw.dvdDrive.uuid.isEmpty())
1675 pelmDVD->createChild("Image")->setAttribute("uuid", makeString(hw.dvdDrive.uuid));
1676 else if (hw.dvdDrive.strHostDriveSrc.length())
1677 pelmDVD->createChild("HostDrive")->setAttribute("src", hw.dvdDrive.strHostDriveSrc);
1678
1679 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
1680 pelmFloppy->setAttribute("enabled", hw.floppyDrive.fEnabled);
1681 if (!hw.floppyDrive.uuid.isEmpty())
1682 pelmFloppy->createChild("Image")->setAttribute("uuid", makeString(hw.floppyDrive.uuid));
1683 else if (hw.floppyDrive.strHostDriveSrc.length())
1684 pelmFloppy->createChild("HostDrive")->setAttribute("src", hw.floppyDrive.strHostDriveSrc);
1685
1686 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
1687 pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
1688 pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
1689
1690 writeUSBDeviceFilters(*pelmUSB,
1691 hw.usbController.llDeviceFilters,
1692 false); // fHostMode
1693
1694 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
1695 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
1696 it != hw.llNetworkAdapters.end();
1697 ++it)
1698 {
1699 const NetworkAdapter &nic = *it;
1700
1701 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
1702 pelmAdapter->setAttribute("slot", nic.ulSlot);
1703 pelmAdapter->setAttribute("enabled", nic.fEnabled);
1704 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
1705 pelmAdapter->setAttribute("cable", nic.fCableConnected);
1706 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
1707 if (nic.fTraceEnabled)
1708 {
1709 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
1710 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
1711 }
1712
1713 const char *pcszType;
1714 switch (nic.type)
1715 {
1716 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
1717 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
1718 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
1719 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
1720 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
1721 }
1722 pelmAdapter->setAttribute("type", pcszType);
1723
1724 xml::ElementNode *pelmNAT;
1725 switch (nic.mode)
1726 {
1727 case NetworkAttachmentType_NAT:
1728 pelmNAT = pelmAdapter->createChild("NAT");
1729 if (nic.strName.length())
1730 pelmNAT->setAttribute("network", nic.strName);
1731 break;
1732
1733 case NetworkAttachmentType_Bridged:
1734 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
1735 break;
1736
1737 case NetworkAttachmentType_Internal:
1738 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
1739 break;
1740
1741 case NetworkAttachmentType_HostOnly:
1742 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
1743 break;
1744
1745 default: /*case NetworkAttachmentType_Null:*/
1746 break;
1747 }
1748 }
1749
1750 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
1751 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
1752 it != hw.llSerialPorts.end();
1753 ++it)
1754 {
1755 const SerialPort &port = *it;
1756 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
1757 pelmPort->setAttribute("slot", port.ulSlot);
1758 pelmPort->setAttribute("enabled", port.fEnabled);
1759 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
1760 pelmPort->setAttribute("IRQ", port.ulIRQ);
1761
1762 const char *pcszHostMode;
1763 switch (port.portMode)
1764 {
1765 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
1766 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
1767 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
1768 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
1769 }
1770 pelmPort->setAttribute("hostMode", pcszHostMode);
1771 }
1772
1773 pelmPorts = pelmHardware->createChild("LPT");
1774 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
1775 it != hw.llParallelPorts.end();
1776 ++it)
1777 {
1778 const ParallelPort &port = *it;
1779 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
1780 pelmPort->setAttribute("slot", port.ulSlot);
1781 pelmPort->setAttribute("enabled", port.fEnabled);
1782 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
1783 pelmPort->setAttribute("IRQ", port.ulIRQ);
1784 if (port.strPath.length())
1785 pelmPort->setAttribute("path", port.strPath);
1786 }
1787
1788 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
1789 pelmAudio->setAttribute("controller", (hw.audioAdapter.controllerType == AudioControllerType_SB16) ? "SB16" : "AC97");
1790
1791 const char *pcszDriver;
1792 switch (hw.audioAdapter.driverType)
1793 {
1794 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
1795 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
1796 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
1797 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
1798 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
1799 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
1800 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
1801 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
1802 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
1803 }
1804 pelmAudio->setAttribute("driver", pcszDriver);
1805
1806 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
1807
1808 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
1809 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
1810 it != hw.llSharedFolders.end();
1811 ++it)
1812 {
1813 const SharedFolder &sf = *it;
1814 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
1815 pelmThis->setAttribute("name", sf.strName);
1816 pelmThis->setAttribute("hostPath", sf.strHostPath);
1817 pelmThis->setAttribute("writable", sf.fWritable);
1818 }
1819
1820 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
1821 const char *pcszClip;
1822 switch (hw.clipboardMode)
1823 {
1824 case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
1825 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
1826 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
1827 default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
1828 }
1829 pelmClip->setAttribute("mode", pcszClip);
1830
1831 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
1832 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
1833 pelmGuest->setAttribute("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
1834
1835 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
1836 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
1837 it != hw.llGuestProperties.end();
1838 ++it)
1839 {
1840 const GuestProperty &prop = *it;
1841 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
1842 pelmProp->setAttribute("name", prop.strName);
1843 pelmProp->setAttribute("value", prop.strValue);
1844 pelmProp->setAttribute("timestamp", prop.timestamp);
1845 pelmProp->setAttribute("flags", prop.strFlags);
1846 }
1847
1848 if (hw.strNotificationPatterns.length())
1849 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
1850}
1851
1852/**
1853 * Creates a <StorageControllers> node under elmParent and then writes out the XML
1854 * keys under that. Called for both the <Machine> node and for snapshots.
1855 * @param elmParent
1856 * @param st
1857 */
1858void MachineConfigFile::writeStorageControllers(xml::ElementNode &elmParent,
1859 const Storage &st)
1860{
1861 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
1862
1863 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
1864 it != st.llStorageControllers.end();
1865 ++it)
1866 {
1867 const StorageController &sc = *it;
1868
1869 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
1870 pelmController->setAttribute("name", sc.strName);
1871
1872 const char *pcszType;
1873 switch (sc.controllerType)
1874 {
1875 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
1876 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
1877 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
1878 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
1879 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
1880 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
1881 }
1882 pelmController->setAttribute("type", pcszType);
1883
1884 pelmController->setAttribute("PortCount", sc.ulPortCount);
1885
1886 if (sc.controllerType == StorageControllerType_IntelAhci)
1887 {
1888 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
1889 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
1890 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
1891 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
1892 }
1893
1894 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
1895 it2 != sc.llAttachedDevices.end();
1896 ++it2)
1897 {
1898 const AttachedDevice &dev = *it2;
1899
1900 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
1901 pelmDevice->setAttribute("type", "HardDisk");
1902
1903 pelmDevice->setAttribute("port", dev.lPort);
1904 pelmDevice->setAttribute("device", dev.lDevice);
1905 pelmDevice->createChild("Image")->setAttribute("uuid", makeString(dev.uuid));
1906 }
1907 }
1908}
1909
1910/**
1911 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
1912 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
1913 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
1914 * @param elmParent
1915 * @param snap
1916 */
1917void MachineConfigFile::writeSnapshot(xml::ElementNode &elmParent,
1918 const Snapshot &snap)
1919{
1920 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
1921
1922 pelmSnapshot->setAttribute("uuid", makeString(snap.uuid));
1923 pelmSnapshot->setAttribute("name", snap.strName);
1924 pelmSnapshot->setAttribute("Description", snap.strDescription);
1925
1926 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
1927
1928 if (snap.strStateFile.length())
1929 pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
1930
1931 writeHardware(*pelmSnapshot, snap.hardware);
1932 writeStorageControllers(*pelmSnapshot, snap.storage);
1933
1934 if (snap.llChildSnapshots.size())
1935 {
1936 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
1937 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
1938 it != snap.llChildSnapshots.end();
1939 ++it)
1940 {
1941 const Snapshot &child = *it;
1942 writeSnapshot(*pelmChildren, child);
1943 }
1944 }
1945}
1946
1947/**
1948 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
1949 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
1950 * in particular if the file cannot be written.
1951 */
1952void MachineConfigFile::write(const com::Utf8Str &strFilename)
1953{
1954 m->strFilename = strFilename;
1955 createStubDocument();
1956
1957 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
1958
1959 pelmMachine->setAttribute("uuid", makeString(uuid));
1960 pelmMachine->setAttribute("name", strName);
1961 if (!fNameSync)
1962 pelmMachine->setAttribute("nameSync", fNameSync);
1963 if (strDescription.length())
1964 pelmMachine->createChild("Description")->addContent(strDescription);
1965 pelmMachine->setAttribute("OSType", strOsType);
1966 if (strStateFile.length())
1967 pelmMachine->setAttribute("stateFile", strStateFile);
1968 if (!uuidCurrentSnapshot.isEmpty())
1969 pelmMachine->setAttribute("currentSnapshot", makeString(uuidCurrentSnapshot));
1970 if (strSnapshotFolder.length())
1971 pelmMachine->setAttribute("snapshotFolder", strSnapshotFolder);
1972 if (!fCurrentStateModified)
1973 pelmMachine->setAttribute("currentStateModified", fCurrentStateModified);
1974 pelmMachine->setAttribute("lastStateChange", makeString(timeLastStateChange));
1975 if (fAborted)
1976 pelmMachine->setAttribute("aborted", fAborted);
1977
1978 writeExtraData(*pelmMachine, mapExtraDataItems);
1979
1980 if (llFirstSnapshot.size())
1981 writeSnapshot(*pelmMachine, llFirstSnapshot.front());
1982
1983 writeHardware(*pelmMachine, hardwareMachine);
1984
1985 writeStorageControllers(*pelmMachine, storageMachine);
1986
1987 // now go write the XML
1988 xml::XmlFileWriter writer(*m->pDoc);
1989 writer.write(m->strFilename.c_str());
1990
1991 m->fFileExists = true;
1992
1993 clearDocument();
1994}
1995
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