VirtualBox

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

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

Main: fix missing shared folders XML save

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