VirtualBox

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

Last change on this file since 22173 was 22173, checked in by vboxsync, 16 years ago

Main: the big XML settings rework. Move XML reading/writing out of interface implementation code into separate layer so it can handle individual settings versions in the future.

  • 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()
789{
790 createStubDocument();
791
792 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
793
794 writeExtraData(*pelmGlobal, mapExtraDataItems);
795
796 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
797 for (MachinesRegistry::const_iterator it = llMachines.begin();
798 it != llMachines.end();
799 ++it)
800 {
801 // <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"/>
802 const MachineRegistryEntry &mre = *it;
803 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
804 pelmMachineEntry->setAttribute("uuid", makeString(mre.uuid));
805 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
806 }
807
808 xml::ElementNode *pelmMediaRegistry = pelmGlobal->createChild("MediaRegistry");
809
810 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
811 for (MediaList::const_iterator it = llHardDisks.begin();
812 it != llHardDisks.end();
813 ++it)
814 {
815 writeHardDisk(*pelmHardDisks, *it, 0);
816 }
817
818 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
819 for (MediaList::const_iterator it = llDvdImages.begin();
820 it != llDvdImages.end();
821 ++it)
822 {
823 const Medium &m = *it;
824 xml::ElementNode *pelmMedium = pelmDVDImages->createChild("Image");
825 pelmMedium->setAttribute("uuid", makeString(m.uuid));
826 pelmMedium->setAttribute("location", m.strLocation);
827 if (m.strDescription.length())
828 pelmMedium->setAttribute("Description", m.strDescription);
829 }
830
831 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
832 for (MediaList::const_iterator it = llFloppyImages.begin();
833 it != llFloppyImages.end();
834 ++it)
835 {
836 const Medium &m = *it;
837 xml::ElementNode *pelmMedium = pelmFloppyImages->createChild("Image");
838 pelmMedium->setAttribute("uuid", makeString(m.uuid));
839 pelmMedium->setAttribute("location", m.strLocation);
840 if (m.strDescription.length())
841 pelmMedium->setAttribute("Description", m.strDescription);
842 }
843
844 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
845 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
846 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
847 it != llDhcpServers.end();
848 ++it)
849 {
850 const DHCPServer &d = *it;
851 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
852 pelmThis->setAttribute("networkName", d.strNetworkName);
853 pelmThis->setAttribute("IPAddress", d.strIPAddress);
854 pelmThis->setAttribute("networkMask", d.strIPNetworkMask);
855 pelmThis->setAttribute("lowerIP", d.strIPLower);
856 pelmThis->setAttribute("upperIP", d.strIPUpper);
857 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
858 }
859
860 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
861 if (systemProperties.strDefaultMachineFolder.length())
862 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
863 if (systemProperties.strDefaultHardDiskFolder.length())
864 pelmSysProps->setAttribute("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder);
865 if (systemProperties.strDefaultHardDiskFormat.length())
866 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
867 if (systemProperties.strRemoteDisplayAuthLibrary.length())
868 pelmSysProps->setAttribute("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
869 if (systemProperties.strWebServiceAuthLibrary.length())
870 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
871 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.ulLogHistoryCount);
872
873 writeUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
874 host.llUSBDeviceFilters,
875 true); // fHostMode
876
877 // now go write the XML
878 xml::XmlFileWriter writer(*m->pDoc);
879 writer.write(m->strFilename.c_str());
880
881 m->fFileExists = true;
882
883 clearDocument();
884}
885
886/**
887 * @param elmNetwork
888 * @param ll
889 */
890void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
891 NetworkAdaptersList &ll)
892{
893 xml::NodesLoop nl1(elmNetwork, "Adapter");
894 const xml::ElementNode *pelmAdapter;
895 while ((pelmAdapter = nl1.forAllNodes()))
896 {
897 NetworkAdapter nic;
898
899 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
900 throw ConfigFileError(this, N_("Required Adapter/slot attribute is missing"));
901
902 Utf8Str strTemp;
903 if (pelmAdapter->getAttributeValue("type", strTemp))
904 {
905 if (strTemp == "Am79C970A")
906 nic.type = NetworkAdapterType_Am79C970A;
907 else if (strTemp == "Am79C973")
908 nic.type = NetworkAdapterType_Am79C973;
909 else if (strTemp == "82540EM")
910 nic.type = NetworkAdapterType_I82540EM;
911 else if (strTemp == "82543GC")
912 nic.type = NetworkAdapterType_I82543GC;
913 else if (strTemp == "82545EM")
914 nic.type = NetworkAdapterType_I82545EM;
915 else
916 throw ConfigFileError(this, N_("Invalid value %s in Adapter/type attribute"), strTemp.c_str());
917 }
918
919 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
920 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
921 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
922 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
923 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
924 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
925
926 const xml::ElementNode *pelmAdapterChild;
927 if ((pelmAdapterChild = pelmAdapter->findChildElement("NAT")))
928 {
929 nic.mode = NetworkAttachmentType_NAT;
930 pelmAdapterChild->getAttributeValue("name", nic.strName); // optional network name
931 }
932 else if ( ((pelmAdapterChild = pelmAdapter->findChildElement("HostInterface")))
933 || ((pelmAdapterChild = pelmAdapter->findChildElement("BridgedInterface")))
934 )
935 {
936 nic.mode = NetworkAttachmentType_Bridged;
937 pelmAdapterChild->getAttributeValue("name", nic.strName); // optional host interface name
938 }
939 else if ((pelmAdapterChild = pelmAdapter->findChildElement("InternalNetwork")))
940 {
941 nic.mode = NetworkAttachmentType_Internal;
942 if (!pelmAdapterChild->getAttributeValue("name", nic.strName)) // required network name
943 throw ConfigFileError(this, N_("Required 'name' element is missing under 'InternalNetwork' element"));
944 }
945 else if ((pelmAdapterChild = pelmAdapter->findChildElement("HostOnlyInterface")))
946 {
947 nic.mode = NetworkAttachmentType_HostOnly;
948 if (!pelmAdapterChild->getAttributeValue("name", nic.strName)) // required network name
949 throw ConfigFileError(this, N_("Required 'name' element is missing under 'HostOnlyInterface' element"));
950 }
951 // else: default is NetworkAttachmentType_Null
952
953 ll.push_back(nic);
954 }
955}
956
957/**
958 * Called from MachineConfigFile::readHardware() to read serial port information.
959 * @param elmUART
960 * @param ll
961 */
962void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
963 SerialPortsList &ll)
964{
965 xml::NodesLoop nl1(elmUART, "Port");
966 const xml::ElementNode *pelmPort;
967 while ((pelmPort = nl1.forAllNodes()))
968 {
969 SerialPort port;
970 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
971 throw ConfigFileError(this, N_("Required UART/Port/slot attribute is missing"));
972
973 // slot must be unique
974 for (SerialPortsList::const_iterator it = ll.begin();
975 it != ll.end();
976 ++it)
977 if ((*it).ulSlot == port.ulSlot)
978 throw ConfigFileError(this, N_("UART/Port/slot attribute value %d is used twice, must be unique"), port.ulSlot);
979
980 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
981 throw ConfigFileError(this, N_("Required UART/Port/enabled attribute is missing"));
982 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
983 throw ConfigFileError(this, N_("Required UART/Port/IOBase attribute is missing"));
984 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
985 throw ConfigFileError(this, N_("Required UART/Port/IRQ attribute is missing"));
986
987 Utf8Str strPortMode;
988 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
989 throw ConfigFileError(this, N_("Required UART/Port/hostMode attribute is missing"));
990 if (strPortMode == "RawFile")
991 port.portMode = PortMode_RawFile;
992 else if (strPortMode == "HostPipe")
993 port.portMode = PortMode_HostPipe;
994 else if (strPortMode == "HostDevice")
995 port.portMode = PortMode_HostDevice;
996 else if (strPortMode == "Disconnected")
997 port.portMode = PortMode_Disconnected;
998 else
999 throw ConfigFileError(this, N_("Invalid value %s in UART/Port/hostMode attribute"), strPortMode.c_str());
1000
1001 pelmPort->getAttributeValue("path", port.strPath);
1002 pelmPort->getAttributeValue("server", port.fServer);
1003
1004 ll.push_back(port);
1005 }
1006}
1007
1008/**
1009 * Called from MachineConfigFile::readHardware() to read parallel port information.
1010 * @param elmLPT
1011 * @param ll
1012 */
1013void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
1014 ParallelPortsList &ll)
1015{
1016 xml::NodesLoop nl1(elmLPT, "Port");
1017 const xml::ElementNode *pelmPort;
1018 while ((pelmPort = nl1.forAllNodes()))
1019 {
1020 ParallelPort port;
1021 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
1022 throw ConfigFileError(this, N_("Required LPT/Port/slot attribute is missing"));
1023
1024 // slot must be unique
1025 for (ParallelPortsList::const_iterator it = ll.begin();
1026 it != ll.end();
1027 ++it)
1028 if ((*it).ulSlot == port.ulSlot)
1029 throw ConfigFileError(this, N_("LPT/Port/slot attribute value %d is used twice, must be unique"), port.ulSlot);
1030
1031 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
1032 throw ConfigFileError(this, N_("Required LPT/Port/enabled attribute is missing"));
1033 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
1034 throw ConfigFileError(this, N_("Required LPT/Port/IOBase attribute is missing"));
1035 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
1036 throw ConfigFileError(this, N_("Required LPT/Port/IRQ attribute is missing"));
1037
1038 pelmPort->getAttributeValue("path", port.strPath);
1039
1040 ll.push_back(port);
1041 }
1042}
1043
1044/**
1045 * Called from MachineConfigFile::readHardware() to read guest property information.
1046 * @param elmGuestProperties
1047 * @param hw
1048 */
1049void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
1050 Hardware &hw)
1051{
1052 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
1053 const xml::ElementNode *pelmProp;
1054 while ((pelmProp = nl1.forAllNodes()))
1055 {
1056 GuestProperty prop;
1057 pelmProp->getAttributeValue("name", prop.strName);
1058 pelmProp->getAttributeValue("value", prop.strValue);
1059
1060 pelmProp->getAttributeValue("timestamp", prop.timestamp);
1061 pelmProp->getAttributeValue("flags", prop.strFlags);
1062 hw.llGuestProperties.push_back(prop);
1063 }
1064
1065 elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
1066}
1067
1068/**
1069 * Reads in a <Hardware> block and stores it in the given structure. Used
1070 * both directly from readMachine and from readSnapshot, since snapshots
1071 * have their own hardware sections.
1072 * @param elmHardware
1073 * @param hw
1074 */
1075void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
1076 Hardware &hw)
1077{
1078 elmHardware.getAttributeValue("version", hw.strVersion);
1079 // defaults to 2 and is only written if != 2
1080
1081 xml::NodesLoop nl1(elmHardware);
1082 const xml::ElementNode *pelmHwChild;
1083 while ((pelmHwChild = nl1.forAllNodes()))
1084 {
1085 if (pelmHwChild->nameEquals("CPU"))
1086 {
1087 pelmHwChild->getAttributeValue("count", hw.cCPUs);
1088
1089 const xml::ElementNode *pelmCPUChild;
1090 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
1091 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
1092 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
1093 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
1094 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
1095 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
1096 if ((pelmCPUChild = pelmHwChild->findChildElement("PAE")))
1097 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
1098 }
1099 else if (pelmHwChild->nameEquals("Memory"))
1100 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
1101 else if (pelmHwChild->nameEquals("Boot"))
1102 {
1103 xml::NodesLoop nl2(*pelmHwChild, "Order");
1104 const xml::ElementNode *pelmOrder;
1105 while ((pelmOrder = nl2.forAllNodes()))
1106 {
1107 uint32_t ulPos;
1108 Utf8Str strDevice;
1109 if (!pelmOrder->getAttributeValue("position", ulPos))
1110 throw ConfigFileError(this, N_("'Order' element lacks 'position' attribute"));
1111 if (!pelmOrder->getAttributeValue("device", strDevice))
1112 throw ConfigFileError(this, N_("'Order' element lacks 'device' attribute"));
1113 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
1114 throw ConfigFileError(this, N_("Value %d of 'position' attribute in 'Order' element is not unique"), ulPos);
1115
1116 DeviceType_T type;
1117 if (strDevice == "None")
1118 type = DeviceType_Null;
1119 else if (strDevice == "Floppy")
1120 type = DeviceType_Floppy;
1121 else if (strDevice == "DVD")
1122 type = DeviceType_DVD;
1123 else if (strDevice == "HardDisk")
1124 type = DeviceType_HardDisk;
1125 else if (strDevice == "Network")
1126 type = DeviceType_Network;
1127 else
1128 throw ConfigFileError(this, N_("Invalid value %s in Boot/Order/device attribute"), strDevice.c_str());
1129 hw.mapBootOrder[ulPos] = type;
1130 }
1131 }
1132 else if (pelmHwChild->nameEquals("Display"))
1133 {
1134 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
1135 pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors);
1136 pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D);
1137 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
1138 }
1139 else if (pelmHwChild->nameEquals("RemoteDisplay"))
1140 {
1141 pelmHwChild->getAttributeValue("enabled", hw.vrdpSettings.fEnabled);
1142 pelmHwChild->getAttributeValue("port", hw.vrdpSettings.ulPort);
1143 pelmHwChild->getAttributeValue("netAddress", hw.vrdpSettings.strNetAddress);
1144
1145 Utf8Str strAuthType;
1146 if (pelmHwChild->getAttributeValue("authType", strAuthType))
1147 {
1148 if (strAuthType == "Null")
1149 hw.vrdpSettings.authType = VRDPAuthType_Null;
1150 else if (strAuthType == "Guest")
1151 hw.vrdpSettings.authType = VRDPAuthType_Guest;
1152 else if (strAuthType == "External")
1153 hw.vrdpSettings.authType = VRDPAuthType_External;
1154 else
1155 throw ConfigFileError(this, N_("Invalid value %s in RemoteDisplay/authType attribute"), strAuthType.c_str());
1156 }
1157
1158 pelmHwChild->getAttributeValue("authTimeout", hw.vrdpSettings.ulAuthTimeout);
1159 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
1160 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
1161 }
1162 else if (pelmHwChild->nameEquals("BIOS"))
1163 {
1164 const xml::ElementNode *pelmBIOSChild;
1165 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
1166 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
1167 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
1168 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
1169 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
1170 {
1171 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
1172 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
1173 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
1174 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
1175 }
1176 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
1177 {
1178 Utf8Str strBootMenuMode;
1179 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
1180 {
1181 if (strBootMenuMode == "Disabled")
1182 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
1183 else if (strBootMenuMode == "MenuOnly")
1184 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
1185 else if (strBootMenuMode == "MessageAndMenu")
1186 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
1187 else
1188 throw ConfigFileError(this, N_("Invalid value %s in BootMenu/mode attribute"), strBootMenuMode.c_str());
1189 }
1190 }
1191 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
1192 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
1193 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
1194 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
1195 }
1196 else if (pelmHwChild->nameEquals("DVDDrive"))
1197 {
1198 const xml::ElementNode *pDriveChild;
1199 pelmHwChild->getAttributeValue("passthrough", hw.dvdDrive.fPassThrough);
1200 Utf8Str strTmp;
1201 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
1202 && (pDriveChild->getAttributeValue("uuid", strTmp))
1203 )
1204 parseUUID(hw.dvdDrive.uuid, strTmp);
1205 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
1206 pDriveChild->getAttributeValue("src", hw.dvdDrive.strHostDriveSrc);
1207 }
1208 else if (pelmHwChild->nameEquals("FloppyDrive"))
1209 {
1210 const xml::ElementNode *pDriveChild;
1211 pelmHwChild->getAttributeValue("enabled", hw.floppyDrive.fEnabled);
1212 Utf8Str strTmp;
1213 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
1214 && (pDriveChild->getAttributeValue("uuid", strTmp))
1215 )
1216 parseUUID(hw.floppyDrive.uuid, strTmp);
1217 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
1218 pDriveChild->getAttributeValue("src", hw.floppyDrive.strHostDriveSrc);
1219 }
1220 else if (pelmHwChild->nameEquals("USBController"))
1221 {
1222 pelmHwChild->getAttributeValue("enabled", hw.usbController.fEnabled);
1223 pelmHwChild->getAttributeValue("enabledEhci", hw.usbController.fEnabledEHCI);
1224
1225 readUSBDeviceFilters(*pelmHwChild,
1226 hw.usbController.llDeviceFilters);
1227 }
1228 else if (pelmHwChild->nameEquals("Network"))
1229 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
1230 else if (pelmHwChild->nameEquals("UART"))
1231 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
1232 else if (pelmHwChild->nameEquals("LPT"))
1233 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
1234 else if (pelmHwChild->nameEquals("AudioAdapter"))
1235 {
1236 pelmHwChild->getAttributeValue("enabled", hw.audioAdapter.fEnabled);
1237
1238 Utf8Str strTemp;
1239 if (pelmHwChild->getAttributeValue("controller", strTemp))
1240 {
1241 if (strTemp == "SB16")
1242 hw.audioAdapter.controllerType = AudioControllerType_SB16;
1243 else if (strTemp == "AC97")
1244 hw.audioAdapter.controllerType = AudioControllerType_AC97;
1245 else
1246 throw ConfigFileError(this, N_("Invalid value %s in AudioAdapter/controller attribute"), strTemp.c_str());
1247 }
1248 if (pelmHwChild->getAttributeValue("driver", strTemp))
1249 {
1250 if (strTemp == "Null")
1251 hw.audioAdapter.driverType = AudioDriverType_Null;
1252 else if (strTemp == "WinMM")
1253 hw.audioAdapter.driverType = AudioDriverType_WinMM;
1254 else if (strTemp == "DirectSound")
1255 hw.audioAdapter.driverType = AudioDriverType_DirectSound;
1256 else if (strTemp == "SolAudio")
1257 hw.audioAdapter.driverType = AudioDriverType_SolAudio;
1258 else if (strTemp == "ALSA")
1259 hw.audioAdapter.driverType = AudioDriverType_ALSA;
1260 else if (strTemp == "Pulse")
1261 hw.audioAdapter.driverType = AudioDriverType_Pulse;
1262 else if (strTemp == "OSS")
1263 hw.audioAdapter.driverType = AudioDriverType_OSS;
1264 else if (strTemp == "CoreAudio")
1265 hw.audioAdapter.driverType = AudioDriverType_CoreAudio;
1266 else if (strTemp == "MMPM")
1267 hw.audioAdapter.driverType = AudioDriverType_MMPM;
1268 else
1269 throw ConfigFileError(this, N_("Invalid value %s in AudioAdapter/driver attribute"), strTemp.c_str());
1270 }
1271 }
1272 else if (pelmHwChild->nameEquals("SharedFolders"))
1273 {
1274 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
1275 const xml::ElementNode *pelmFolder;
1276 while ((pelmFolder = nl2.forAllNodes()))
1277 {
1278 SharedFolder sf;
1279 pelmFolder->getAttributeValue("name", sf.strName);
1280 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
1281 pelmFolder->getAttributeValue("writable", sf.fWritable);
1282 hw.llSharedFolders.push_back(sf);
1283 }
1284 }
1285 else if (pelmHwChild->nameEquals("Clipboard"))
1286 {
1287 Utf8Str strTemp;
1288 if (pelmHwChild->getAttributeValue("mode", strTemp))
1289 {
1290 if (strTemp == "Disabled")
1291 hw.clipboardMode = ClipboardMode_Disabled;
1292 else if (strTemp == "HostToGuest")
1293 hw.clipboardMode = ClipboardMode_HostToGuest;
1294 else if (strTemp == "GuestToHost")
1295 hw.clipboardMode = ClipboardMode_GuestToHost;
1296 else if (strTemp == "Bidirectional")
1297 hw.clipboardMode = ClipboardMode_Bidirectional;
1298 else
1299 throw ConfigFileError(this, N_("Invalid value %s in Clipbord/mode attribute"), strTemp.c_str());
1300 }
1301 }
1302 else if (pelmHwChild->nameEquals("Guest"))
1303 {
1304 pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize);
1305 pelmHwChild->getAttributeValue("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
1306 }
1307 else if (pelmHwChild->nameEquals("GuestProperties"))
1308 readGuestProperties(*pelmHwChild, hw);
1309 else
1310 throw ConfigFileError(this, N_("Invalid element %s in Hardware section"), pelmHwChild->getName());
1311 }
1312
1313 if (hw.ulMemorySizeMB == (uint32_t)-1)
1314 throw ConfigFileError(this, N_("Required Memory/RAMSize element/attribute is missing"));
1315}
1316
1317/**
1318 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
1319 * Used both directly from readMachine and from readSnapshot, since snapshots
1320 * have their own storage controllers sections.
1321 *
1322 * @param elmStorageControllers
1323 */
1324void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
1325 Storage &strg)
1326{
1327 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
1328 const xml::ElementNode *pelmController;
1329 while ((pelmController = nlStorageControllers.forAllNodes()))
1330 {
1331 StorageController sctl;
1332
1333 if (!pelmController->getAttributeValue("name", sctl.strName))
1334 throw ConfigFileError(this, N_("Required StorageController/name attribute is missing"));
1335 Utf8Str strType;
1336 if (!pelmController->getAttributeValue("type", strType))
1337 throw ConfigFileError(this, N_("Required StorageController/type attribute is missing"));
1338
1339 if (strType == "AHCI")
1340 {
1341 sctl.storageBus = StorageBus_SATA;
1342 sctl.controllerType = StorageControllerType_IntelAhci;
1343 }
1344 else if (strType == "LsiLogic")
1345 {
1346 sctl.storageBus = StorageBus_SCSI;
1347 sctl.controllerType = StorageControllerType_LsiLogic;
1348 }
1349 else if (strType == "BusLogic")
1350 {
1351 sctl.storageBus = StorageBus_SCSI;
1352 sctl.controllerType = StorageControllerType_BusLogic;
1353 }
1354 else if (strType == "PIIX3")
1355 {
1356 sctl.storageBus = StorageBus_IDE;
1357 sctl.controllerType = StorageControllerType_PIIX3;
1358 }
1359 else if (strType == "PIIX4")
1360 {
1361 sctl.storageBus = StorageBus_IDE;
1362 sctl.controllerType = StorageControllerType_PIIX4;
1363 }
1364 else if (strType == "ICH6")
1365 {
1366 sctl.storageBus = StorageBus_IDE;
1367 sctl.controllerType = StorageControllerType_ICH6;
1368 }
1369 else
1370 throw ConfigFileError(this, N_("Invalid value %s for StorageController/type attribute"), strType.c_str());
1371
1372 pelmController->getAttributeValue("PortCount", sctl.ulPortCount);
1373
1374 pelmController->getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
1375 pelmController->getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
1376 pelmController->getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
1377 pelmController->getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
1378
1379 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
1380 const xml::ElementNode *pelmAttached;
1381 while ((pelmAttached = nlAttached.forAllNodes()))
1382 {
1383 AttachedDevice att;
1384
1385 Utf8Str strTemp;
1386 pelmAttached->getAttributeValue("type", strTemp);
1387 if (strTemp != "HardDisk")
1388 throw ConfigFileError(this, N_("AttachedDevice element must have 'HardDisk' type"));
1389
1390 const xml::ElementNode *pelmImage;
1391 if (!(pelmImage = pelmAttached->findChildElement("Image")))
1392 throw ConfigFileError(this, N_("Required AttachedDevice/Image element is missing"));
1393
1394 if (!pelmImage->getAttributeValue("uuid", strTemp))
1395 throw ConfigFileError(this, N_("Required AttachedDevice/Image/uuid attributeis missing"));
1396 parseUUID(att.uuid, strTemp);
1397
1398 if (!pelmAttached->getAttributeValue("port", att.lPort))
1399 throw ConfigFileError(this, N_("Required AttachedDevice/port attribute is missing"));
1400 if (!pelmAttached->getAttributeValue("device", att.lDevice))
1401 throw ConfigFileError(this, N_("Required AttachedDevice/device attribute is missing"));
1402
1403 sctl.llAttachedDevices.push_back(att);
1404 }
1405
1406 strg.llStorageControllers.push_back(sctl);
1407 }
1408}
1409
1410/**
1411 * Called initially for the <Snapshot> element under <Machine>, if present,
1412 * to store the snapshot's data into the given Snapshot structure (which is
1413 * then the one in the Machine struct). This might then recurse if
1414 * a <Snapshots> (plural) element is found in the snapshot, which should
1415 * contain a list of child snapshots; such lists are maintained in the
1416 * Snapshot structure.
1417 *
1418 * @param elmSnapshot
1419 * @param snap
1420 */
1421void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
1422 Snapshot &snap)
1423{
1424 Utf8Str strTemp;
1425
1426 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
1427 throw ConfigFileError(this, N_("Required Snapshot/uuid attribute is missing"));
1428 parseUUID(snap.uuid, strTemp);
1429
1430 if (!elmSnapshot.getAttributeValue("name", snap.strName))
1431 throw ConfigFileError(this, N_("Required Snapshot/name attribute is missing"));
1432
1433 elmSnapshot.getAttributeValue("Description", snap.strDescription);
1434
1435 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
1436 throw ConfigFileError(this, N_("Required Snapshot/timeStamp attribute is missing"));
1437 parseTimestamp(snap.timestamp, strTemp);
1438
1439 elmSnapshot.getAttributeValue("stateFile", snap.strStateFile); // online snapshots only
1440
1441 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
1442 const xml::ElementNode *pelmSnapshotChild;
1443 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
1444 {
1445 if (pelmSnapshotChild->nameEquals("Description"))
1446 snap.strDescription = pelmSnapshotChild->getValue();
1447 else if (pelmSnapshotChild->nameEquals("Hardware"))
1448 readHardware(*pelmSnapshotChild, snap.hardware);
1449 else if (pelmSnapshotChild->nameEquals("StorageControllers"))
1450 readStorageControllers(*pelmSnapshotChild, snap.storage);
1451 else if (pelmSnapshotChild->nameEquals("Snapshots"))
1452 {
1453 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
1454 const xml::ElementNode *pelmChildSnapshot;
1455 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
1456 {
1457 if (pelmChildSnapshot->nameEquals("Snapshot"))
1458 {
1459 Snapshot child;
1460 readSnapshot(*pelmChildSnapshot, child);
1461 snap.llChildSnapshots.push_back(child);
1462 }
1463 else
1464 throw ConfigFileError(this, N_("Invalid element %s under Snapshots element"), pelmChildSnapshot->getName());
1465 }
1466 }
1467 else
1468 throw ConfigFileError(this, N_("Invalid element %s under Snapshot element"), pelmSnapshotChild->getName());
1469 }
1470}
1471
1472/**
1473 * Called from the constructor to actually read in the Machine element
1474 * of a machine config file.
1475 * @param elmMachine
1476 */
1477void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
1478{
1479 Utf8Str strUUID;
1480 if ( (elmMachine.getAttributeValue("uuid", strUUID))
1481 && (elmMachine.getAttributeValue("name", strName))
1482 )
1483 {
1484 parseUUID(uuid, strUUID);
1485
1486 if (!elmMachine.getAttributeValue("nameSync", fNameSync))
1487 fNameSync = true;
1488
1489 Utf8Str str;
1490 elmMachine.getAttributeValue("Description", strDescription);
1491 elmMachine.getAttributeValue("OSType", strOsType);
1492 elmMachine.getAttributeValue("stateFile", strStateFile);
1493 if (elmMachine.getAttributeValue("currentSnapshot", str))
1494 parseUUID(uuidCurrentSnapshot, str);
1495 elmMachine.getAttributeValue("snapshotFolder", strSnapshotFolder);
1496 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
1497 fCurrentStateModified = true;
1498 if (elmMachine.getAttributeValue("lastStateChange", str))
1499 parseTimestamp(timeLastStateChange, str);
1500 // constructor has called RTTimeNow(&timeLastStateChange) before
1501
1502 xml::NodesLoop nlRootChildren(elmMachine);
1503 const xml::ElementNode *pelmMachineChild;
1504 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
1505 {
1506 if (pelmMachineChild->nameEquals("ExtraData"))
1507 readExtraData(*pelmMachineChild,
1508 mapExtraDataItems);
1509 else if (pelmMachineChild->nameEquals("Hardware"))
1510 readHardware(*pelmMachineChild, hardwareMachine);
1511 else if (pelmMachineChild->nameEquals("StorageControllers"))
1512 readStorageControllers(*pelmMachineChild, storageMachine);
1513 else if (pelmMachineChild->nameEquals("Snapshot"))
1514 {
1515 Snapshot snap;
1516 // this will recurse into child snapshots, if necessary
1517 readSnapshot(*pelmMachineChild, snap);
1518 llFirstSnapshot.push_back(snap);
1519 }
1520 else if (pelmMachineChild->nameEquals("Description"))
1521 strDescription = pelmMachineChild->getValue();
1522 else
1523 throw ConfigFileError(this, N_("Invalid element %s under Machine element"), pelmMachineChild->getName());
1524 }
1525 }
1526 else
1527 throw ConfigFileError(this, N_("Machine element must have uuid and name attributes"));
1528}
1529
1530/**
1531 * Constructor.
1532 *
1533 * If pstrFilename is != NULL, this reads the given settings file into the member
1534 * variables and various substructures and lists. Otherwise, the member variables
1535 * are initialized with default values.
1536 *
1537 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1538 * the caller should catch; if this constructor does not throw, then the file has
1539 * been successfully read and parsed.
1540 *
1541 * @param strFilename
1542 */
1543MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
1544 : ConfigFileBase(pstrFilename),
1545 fNameSync(true),
1546 fCurrentStateModified(true),
1547 fAborted(false)
1548{
1549 RTTimeNow(&timeLastStateChange);
1550
1551 if (pstrFilename)
1552 {
1553 // the ConfigFileBase constructor has loaded the XML file, so now
1554 // we need only analyze what is in there
1555
1556 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1557 const xml::ElementNode *pelmRootChild;
1558 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1559 {
1560 if (pelmRootChild->nameEquals("Machine"))
1561 readMachine(*pelmRootChild);
1562 else
1563 throw ConfigFileError(this, N_("Invalid element %s under root element"), pelmRootChild->getName());
1564 }
1565
1566 clearDocument();
1567 }
1568}
1569
1570/**
1571 * Creates a <Hardware> node under elmParent and then writes out the XML
1572 * keys under that. Called for both the <Machine> node and for snapshots.
1573 * @param elmParent
1574 * @param st
1575 */
1576void MachineConfigFile::writeHardware(xml::ElementNode &elmParent,
1577 const Hardware &hw)
1578{
1579 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
1580
1581 if (hw.strVersion != "2")
1582 pelmHardware->setAttribute("version", hw.strVersion);
1583
1584 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
1585 pelmCPU->createChild("HardwareVirtEx")->setAttribute("enabled", hw.fHardwareVirt);
1586 if (hw.fNestedPaging)
1587 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
1588 if (hw.fVPID)
1589 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
1590 if (hw.fPAE)
1591 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
1592 pelmCPU->setAttribute("count", hw.cCPUs);
1593
1594 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
1595 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
1596
1597 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
1598 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
1599 it != hw.mapBootOrder.end();
1600 ++it)
1601 {
1602 uint32_t i = it->first;
1603 DeviceType_T type = it->second;
1604 const char *pcszDevice;
1605
1606 switch (type)
1607 {
1608 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
1609 case DeviceType_DVD: pcszDevice = "DVD"; break;
1610 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
1611 case DeviceType_Network: pcszDevice = "Network"; break;
1612 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
1613 }
1614
1615 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
1616 pelmOrder->setAttribute("position", i);
1617 pelmOrder->setAttribute("device", pcszDevice);
1618 }
1619
1620 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
1621 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
1622 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
1623 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
1624
1625 if (m->sv >= SettingsVersion_v1_8)
1626 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
1627
1628 xml::ElementNode *pelmVRDP = pelmHardware->createChild("RemoteDisplay");
1629 pelmVRDP->setAttribute("enabled", hw.vrdpSettings.fEnabled);
1630 pelmVRDP->setAttribute("port", hw.vrdpSettings.ulPort);
1631 if (hw.vrdpSettings.strNetAddress.length())
1632 pelmVRDP->setAttribute("netAddress", hw.vrdpSettings.strNetAddress);
1633 const char *pcszAuthType;
1634 switch (hw.vrdpSettings.authType)
1635 {
1636 case VRDPAuthType_Guest: pcszAuthType = "Guest"; break;
1637 case VRDPAuthType_External: pcszAuthType = "External"; break;
1638 default: /*case VRDPAuthType_Null:*/ pcszAuthType = "Null"; break;
1639 }
1640 pelmVRDP->setAttribute("authType", pcszAuthType);
1641
1642 if (hw.vrdpSettings.ulAuthTimeout != 0)
1643 pelmVRDP->setAttribute("authTimeout", hw.vrdpSettings.ulAuthTimeout);
1644 if (hw.vrdpSettings.fAllowMultiConnection)
1645 pelmVRDP->setAttribute("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
1646 if (hw.vrdpSettings.fReuseSingleConnection)
1647 pelmVRDP->setAttribute("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
1648
1649 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
1650 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
1651 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
1652
1653 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
1654 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
1655 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
1656 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
1657 if (hw.biosSettings.strLogoImagePath.length())
1658 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
1659
1660 const char *pcszBootMenu;
1661 switch (hw.biosSettings.biosBootMenuMode)
1662 {
1663 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
1664 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
1665 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
1666 }
1667 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
1668 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
1669 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
1670
1671 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
1672 pelmDVD->setAttribute("passthrough", hw.dvdDrive.fPassThrough);
1673 if (!hw.dvdDrive.uuid.isEmpty())
1674 pelmDVD->createChild("Image")->setAttribute("uuid", makeString(hw.dvdDrive.uuid));
1675 else if (hw.dvdDrive.strHostDriveSrc.length())
1676 pelmDVD->createChild("HostDrive")->setAttribute("src", hw.dvdDrive.strHostDriveSrc);
1677
1678 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
1679 pelmFloppy->setAttribute("enabled", hw.floppyDrive.fEnabled);
1680 if (!hw.floppyDrive.uuid.isEmpty())
1681 pelmFloppy->createChild("Image")->setAttribute("uuid", makeString(hw.floppyDrive.uuid));
1682 else if (hw.floppyDrive.strHostDriveSrc.length())
1683 pelmFloppy->createChild("HostDrive")->setAttribute("src", hw.floppyDrive.strHostDriveSrc);
1684
1685 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
1686 pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
1687 pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
1688
1689 writeUSBDeviceFilters(*pelmUSB,
1690 hw.usbController.llDeviceFilters,
1691 false); // fHostMode
1692
1693 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
1694 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
1695 it != hw.llNetworkAdapters.end();
1696 ++it)
1697 {
1698 const NetworkAdapter &nic = *it;
1699
1700 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
1701 pelmAdapter->setAttribute("slot", nic.ulSlot);
1702 pelmAdapter->setAttribute("enabled", nic.fEnabled);
1703 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
1704 pelmAdapter->setAttribute("cable", nic.fCableConnected);
1705 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
1706 if (nic.fTraceEnabled)
1707 {
1708 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
1709 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
1710 }
1711
1712 const char *pcszType;
1713 switch (nic.type)
1714 {
1715 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
1716 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
1717 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
1718 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
1719 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
1720 }
1721 pelmAdapter->setAttribute("type", pcszType);
1722
1723 xml::ElementNode *pelmNAT;
1724 switch (nic.mode)
1725 {
1726 case NetworkAttachmentType_NAT:
1727 pelmNAT = pelmAdapter->createChild("NAT");
1728 if (nic.strName.length())
1729 pelmNAT->setAttribute("network", nic.strName);
1730 break;
1731
1732 case NetworkAttachmentType_Bridged:
1733 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
1734 break;
1735
1736 case NetworkAttachmentType_Internal:
1737 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
1738 break;
1739
1740 case NetworkAttachmentType_HostOnly:
1741 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
1742 break;
1743
1744 default: /*case NetworkAttachmentType_Null:*/
1745 break;
1746 }
1747 }
1748
1749 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
1750 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
1751 it != hw.llSerialPorts.end();
1752 ++it)
1753 {
1754 const SerialPort &port = *it;
1755 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
1756 pelmPort->setAttribute("slot", port.ulSlot);
1757 pelmPort->setAttribute("enabled", port.fEnabled);
1758 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
1759 pelmPort->setAttribute("IRQ", port.ulIRQ);
1760
1761 const char *pcszHostMode;
1762 switch (port.portMode)
1763 {
1764 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
1765 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
1766 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
1767 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
1768 }
1769 pelmPort->setAttribute("hostMode", pcszHostMode);
1770 }
1771
1772 pelmPorts = pelmHardware->createChild("LPT");
1773 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
1774 it != hw.llParallelPorts.end();
1775 ++it)
1776 {
1777 const ParallelPort &port = *it;
1778 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
1779 pelmPort->setAttribute("slot", port.ulSlot);
1780 pelmPort->setAttribute("enabled", port.fEnabled);
1781 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
1782 pelmPort->setAttribute("IRQ", port.ulIRQ);
1783 if (port.strPath.length())
1784 pelmPort->setAttribute("path", port.strPath);
1785 }
1786
1787 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
1788 pelmAudio->setAttribute("controller", (hw.audioAdapter.controllerType == AudioControllerType_SB16) ? "SB16" : "AC97");
1789
1790 const char *pcszDriver;
1791 switch (hw.audioAdapter.driverType)
1792 {
1793 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
1794 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
1795 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
1796 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
1797 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
1798 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
1799 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
1800 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
1801 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
1802 }
1803 pelmAudio->setAttribute("driver", pcszDriver);
1804
1805 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
1806
1807 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
1808 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
1809 it != hw.llSharedFolders.end();
1810 ++it)
1811 {
1812 const SharedFolder &sf = *it;
1813 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
1814 pelmThis->setAttribute("name", sf.strName);
1815 pelmThis->setAttribute("hostPath", sf.strHostPath);
1816 pelmThis->setAttribute("writable", sf.fWritable);
1817 }
1818
1819 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
1820 const char *pcszClip;
1821 switch (hw.clipboardMode)
1822 {
1823 case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
1824 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
1825 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
1826 default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
1827 }
1828 pelmClip->setAttribute("mode", pcszClip);
1829
1830 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
1831 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
1832 pelmGuest->setAttribute("statisticsUpdateInterval", hw.ulStatisticsUpdateInterval);
1833
1834 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
1835 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
1836 it != hw.llGuestProperties.end();
1837 ++it)
1838 {
1839 const GuestProperty &prop = *it;
1840 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
1841 pelmProp->setAttribute("name", prop.strName);
1842 pelmProp->setAttribute("value", prop.strValue);
1843 pelmProp->setAttribute("timestamp", prop.timestamp);
1844 pelmProp->setAttribute("flags", prop.strFlags);
1845 }
1846
1847 if (hw.strNotificationPatterns.length())
1848 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
1849}
1850
1851/**
1852 * Creates a <StorageControllers> node under elmParent and then writes out the XML
1853 * keys under that. Called for both the <Machine> node and for snapshots.
1854 * @param elmParent
1855 * @param st
1856 */
1857void MachineConfigFile::writeStorageControllers(xml::ElementNode &elmParent,
1858 const Storage &st)
1859{
1860 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
1861
1862 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
1863 it != st.llStorageControllers.end();
1864 ++it)
1865 {
1866 const StorageController &sc = *it;
1867
1868 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
1869 pelmController->setAttribute("name", sc.strName);
1870
1871 const char *pcszType;
1872 switch (sc.controllerType)
1873 {
1874 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
1875 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
1876 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
1877 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
1878 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
1879 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
1880 }
1881 pelmController->setAttribute("type", pcszType);
1882
1883 pelmController->setAttribute("PortCount", sc.ulPortCount);
1884
1885 if (sc.controllerType == StorageControllerType_IntelAhci)
1886 {
1887 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
1888 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
1889 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
1890 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
1891 }
1892
1893 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
1894 it2 != sc.llAttachedDevices.end();
1895 ++it2)
1896 {
1897 const AttachedDevice &dev = *it2;
1898
1899 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
1900 pelmDevice->setAttribute("type", "HardDisk");
1901
1902 pelmDevice->setAttribute("port", dev.lPort);
1903 pelmDevice->setAttribute("device", dev.lDevice);
1904 pelmDevice->createChild("Image")->setAttribute("uuid", makeString(dev.uuid));
1905 }
1906 }
1907}
1908
1909/**
1910 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
1911 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
1912 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
1913 * @param elmParent
1914 * @param snap
1915 */
1916void MachineConfigFile::writeSnapshot(xml::ElementNode &elmParent,
1917 const Snapshot &snap)
1918{
1919 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
1920
1921 pelmSnapshot->setAttribute("uuid", makeString(snap.uuid));
1922 pelmSnapshot->setAttribute("name", snap.strName);
1923 pelmSnapshot->setAttribute("Description", snap.strDescription);
1924
1925 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
1926
1927 if (snap.strStateFile.length())
1928 pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
1929
1930 writeHardware(*pelmSnapshot, snap.hardware);
1931 writeStorageControllers(*pelmSnapshot, snap.storage);
1932
1933 if (snap.llChildSnapshots.size())
1934 {
1935 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
1936 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
1937 it != snap.llChildSnapshots.end();
1938 ++it)
1939 {
1940 const Snapshot &child = *it;
1941 writeSnapshot(*pelmChildren, child);
1942 }
1943 }
1944}
1945
1946/**
1947 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
1948 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
1949 * in particular if the file cannot be written.
1950 */
1951void MachineConfigFile::write(const com::Utf8Str &strFilename)
1952{
1953 m->strFilename = strFilename;
1954
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.

© 2025 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette