VirtualBox

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

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

Main: fix boot order XML (position indices were wrong), set correct defaults

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