VirtualBox

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

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

Main/Medium: Implement medium type Shareable, and along the way added a method to query the medium variant. Lots of terminology cleanup to make sure the error messages talk about "medium" instead of "hard disk", as the latter sometimes also showed up in errors about floppy/DVD images.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Date Revision Author Id
File size: 173.2 KB
Line 
1/* $Id: Settings.cpp 31063 2010-07-23 14:36:53Z vboxsync $ */
2/** @file
3 * Settings File Manipulation API.
4 *
5 * Two classes, MainConfigFile and MachineConfigFile, represent the VirtualBox.xml and
6 * machine XML files. They share a common ancestor class, ConfigFileBase, which shares
7 * functionality such as talking to the XML back-end classes and settings version management.
8 *
9 * The code can read all VirtualBox settings files version 1.3 and higher. That version was
10 * written by VirtualBox 2.0. It can write settings version 1.7 (used by VirtualBox 2.2 and
11 * 3.0) and 1.9 (used by VirtualBox 3.1).
12 *
13 * Rules for introducing new settings: If an element or attribute is introduced that was not
14 * present before VirtualBox 3.1, then settings version checks need to be introduced. The
15 * settings version for VirtualBox 3.1 is 1.9; see the SettingsVersion enumeration in
16 * src/VBox/Main/idl/VirtualBox.xidl for details about which version was used when.
17 *
18 * The settings versions checks are necessary because VirtualBox 3.1 no longer automatically
19 * converts XML settings files but only if necessary, that is, if settings are present that
20 * the old format does not support. If we write an element or attribute to a settings file
21 * of an older version, then an old VirtualBox (before 3.1) will attempt to validate it
22 * with XML schema, and that will certainly fail.
23 *
24 * So, to introduce a new setting:
25 *
26 * 1) Make sure the constructor of corresponding settings structure has a proper default.
27 *
28 * 2) In the settings reader method, try to read the setting; if it's there, great, if not,
29 * the default value will have been set by the constructor.
30 *
31 * 3) In the settings writer method, write the setting _only_ if the current settings
32 * version (stored in m->sv) is high enough. That is, for VirtualBox 3.3, write it
33 * only if (m->sv >= SettingsVersion_v1_11).
34 *
35 * 4) In MachineConfigFile::bumpSettingsVersionIfNeeded(), check if the new setting has
36 * a non-default value (i.e. that differs from the constructor). If so, bump the
37 * settings version to the current version so the settings writer (3) can write out
38 * the non-default value properly.
39 *
40 * So far a corresponding method for MainConfigFile has not been necessary since there
41 * have been no incompatible changes yet.
42 */
43
44/*
45 * Copyright (C) 2007-2010 Oracle Corporation
46 *
47 * This file is part of VirtualBox Open Source Edition (OSE), as
48 * available from http://www.virtualbox.org. This file is free software;
49 * you can redistribute it and/or modify it under the terms of the GNU
50 * General Public License (GPL) as published by the Free Software
51 * Foundation, in version 2 as it comes in the "COPYING" file of the
52 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
53 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
54 */
55
56#include "VBox/com/string.h"
57#include "VBox/settings.h"
58#include <iprt/cpp/xml.h>
59#include <iprt/stream.h>
60#include <iprt/ctype.h>
61#include <iprt/file.h>
62#include <iprt/process.h>
63#include <iprt/ldr.h>
64#include <iprt/cpp/lock.h>
65
66// generated header
67#include "SchemaDefs.h"
68
69#include "Logging.h"
70
71using namespace com;
72using namespace settings;
73
74////////////////////////////////////////////////////////////////////////////////
75//
76// Defines
77//
78////////////////////////////////////////////////////////////////////////////////
79
80/** VirtualBox XML settings namespace */
81#define VBOX_XML_NAMESPACE "http://www.innotek.de/VirtualBox-settings"
82
83/** VirtualBox XML settings version number substring ("x.y") */
84#define VBOX_XML_VERSION "1.11"
85
86/** VirtualBox XML settings version platform substring */
87#if defined (RT_OS_DARWIN)
88# define VBOX_XML_PLATFORM "macosx"
89#elif defined (RT_OS_FREEBSD)
90# define VBOX_XML_PLATFORM "freebsd"
91#elif defined (RT_OS_LINUX)
92# define VBOX_XML_PLATFORM "linux"
93#elif defined (RT_OS_NETBSD)
94# define VBOX_XML_PLATFORM "netbsd"
95#elif defined (RT_OS_OPENBSD)
96# define VBOX_XML_PLATFORM "openbsd"
97#elif defined (RT_OS_OS2)
98# define VBOX_XML_PLATFORM "os2"
99#elif defined (RT_OS_SOLARIS)
100# define VBOX_XML_PLATFORM "solaris"
101#elif defined (RT_OS_WINDOWS)
102# define VBOX_XML_PLATFORM "windows"
103#else
104# error Unsupported platform!
105#endif
106
107/** VirtualBox XML settings full version string ("x.y-platform") */
108#define VBOX_XML_VERSION_FULL VBOX_XML_VERSION "-" VBOX_XML_PLATFORM
109
110////////////////////////////////////////////////////////////////////////////////
111//
112// Internal data
113//
114////////////////////////////////////////////////////////////////////////////////
115
116/**
117 * Opaque data structore for ConfigFileBase (only declared
118 * in header, defined only here).
119 */
120
121struct ConfigFileBase::Data
122{
123 Data()
124 : pDoc(NULL),
125 pelmRoot(NULL),
126 sv(SettingsVersion_Null),
127 svRead(SettingsVersion_Null)
128 {}
129
130 ~Data()
131 {
132 cleanup();
133 }
134
135 iprt::MiniString strFilename;
136 bool fFileExists;
137
138 xml::Document *pDoc;
139 xml::ElementNode *pelmRoot;
140
141 com::Utf8Str strSettingsVersionFull; // e.g. "1.7-linux"
142 SettingsVersion_T sv; // e.g. SettingsVersion_v1_7
143
144 SettingsVersion_T svRead; // settings version that the original file had when it was read,
145 // or SettingsVersion_Null if none
146
147 void copyFrom(const Data &d)
148 {
149 strFilename = d.strFilename;
150 fFileExists = d.fFileExists;
151 strSettingsVersionFull = d.strSettingsVersionFull;
152 sv = d.sv;
153 svRead = d.svRead;
154 }
155
156 void cleanup()
157 {
158 if (pDoc)
159 {
160 delete pDoc;
161 pDoc = NULL;
162 pelmRoot = NULL;
163 }
164 }
165};
166
167/**
168 * Private exception class (not in the header file) that makes
169 * throwing xml::LogicError instances easier. That class is public
170 * and should be caught by client code.
171 */
172class settings::ConfigFileError : public xml::LogicError
173{
174public:
175 ConfigFileError(const ConfigFileBase *file,
176 const xml::Node *pNode,
177 const char *pcszFormat, ...)
178 : xml::LogicError()
179 {
180 va_list args;
181 va_start(args, pcszFormat);
182 Utf8StrFmtVA strWhat(pcszFormat, args);
183 va_end(args);
184
185 Utf8Str strLine;
186 if (pNode)
187 strLine = Utf8StrFmt(" (line %RU32)", pNode->getLineNumber());
188
189 const char *pcsz = strLine.c_str();
190 Utf8StrFmt str(N_("Error in %s%s -- %s"),
191 file->m->strFilename.c_str(),
192 (pcsz) ? pcsz : "",
193 strWhat.c_str());
194
195 setWhat(str.c_str());
196 }
197};
198
199////////////////////////////////////////////////////////////////////////////////
200//
201// ConfigFileBase
202//
203////////////////////////////////////////////////////////////////////////////////
204
205/**
206 * Constructor. Allocates the XML internals.
207 * @param strFilename
208 */
209ConfigFileBase::ConfigFileBase(const com::Utf8Str *pstrFilename)
210 : m(new Data)
211{
212 Utf8Str strMajor;
213 Utf8Str strMinor;
214
215 m->fFileExists = false;
216
217 if (pstrFilename)
218 {
219 // reading existing settings file:
220 m->strFilename = *pstrFilename;
221
222 xml::XmlFileParser parser;
223 m->pDoc = new xml::Document;
224 parser.read(*pstrFilename,
225 *m->pDoc);
226
227 m->fFileExists = true;
228
229 m->pelmRoot = m->pDoc->getRootElement();
230 if (!m->pelmRoot || !m->pelmRoot->nameEquals("VirtualBox"))
231 throw ConfigFileError(this, NULL, N_("Root element in VirtualBox settings files must be \"VirtualBox\"."));
232
233 if (!(m->pelmRoot->getAttributeValue("version", m->strSettingsVersionFull)))
234 throw ConfigFileError(this, m->pelmRoot, N_("Required VirtualBox/@version attribute is missing"));
235
236 LogRel(("Loading settings file \"%s\" with version \"%s\"\n", m->strFilename.c_str(), m->strSettingsVersionFull.c_str()));
237
238 // parse settings version; allow future versions but fail if file is older than 1.6
239 m->sv = SettingsVersion_Null;
240 if (m->strSettingsVersionFull.length() > 3)
241 {
242 const char *pcsz = m->strSettingsVersionFull.c_str();
243 char c;
244
245 while ( (c = *pcsz)
246 && RT_C_IS_DIGIT(c)
247 )
248 {
249 strMajor.append(c);
250 ++pcsz;
251 }
252
253 if (*pcsz++ == '.')
254 {
255 while ( (c = *pcsz)
256 && RT_C_IS_DIGIT(c)
257 )
258 {
259 strMinor.append(c);
260 ++pcsz;
261 }
262 }
263
264 uint32_t ulMajor = RTStrToUInt32(strMajor.c_str());
265 uint32_t ulMinor = RTStrToUInt32(strMinor.c_str());
266
267 if (ulMajor == 1)
268 {
269 if (ulMinor == 3)
270 m->sv = SettingsVersion_v1_3;
271 else if (ulMinor == 4)
272 m->sv = SettingsVersion_v1_4;
273 else if (ulMinor == 5)
274 m->sv = SettingsVersion_v1_5;
275 else if (ulMinor == 6)
276 m->sv = SettingsVersion_v1_6;
277 else if (ulMinor == 7)
278 m->sv = SettingsVersion_v1_7;
279 else if (ulMinor == 8)
280 m->sv = SettingsVersion_v1_8;
281 else if (ulMinor == 9)
282 m->sv = SettingsVersion_v1_9;
283 else if (ulMinor == 10)
284 m->sv = SettingsVersion_v1_10;
285 else if (ulMinor == 11)
286 m->sv = SettingsVersion_v1_11;
287 else if (ulMinor > 11)
288 m->sv = SettingsVersion_Future;
289 }
290 else if (ulMajor > 1)
291 m->sv = SettingsVersion_Future;
292
293 LogRel(("Parsed settings version %d.%d to enum value %d\n", ulMajor, ulMinor, m->sv));
294 }
295
296 if (m->sv == SettingsVersion_Null)
297 throw ConfigFileError(this, m->pelmRoot, N_("Cannot handle settings version '%s'"), m->strSettingsVersionFull.c_str());
298
299 // remember the settings version we read in case it gets upgraded later,
300 // so we know when to make backups
301 m->svRead = m->sv;
302 }
303 else
304 {
305 // creating new settings file:
306 m->strSettingsVersionFull = VBOX_XML_VERSION_FULL;
307 m->sv = SettingsVersion_v1_11;
308 }
309}
310
311/**
312 * Clean up.
313 */
314ConfigFileBase::~ConfigFileBase()
315{
316 if (m)
317 {
318 delete m;
319 m = NULL;
320 }
321}
322
323/**
324 * Helper function that parses a UUID in string form into
325 * a com::Guid item. Since that uses an IPRT function which
326 * does not accept "{}" characters around the UUID string,
327 * we handle that here. Throws on errors.
328 * @param guid
329 * @param strUUID
330 */
331void ConfigFileBase::parseUUID(Guid &guid,
332 const Utf8Str &strUUID) const
333{
334 // {5f102a55-a51b-48e3-b45a-b28d33469488}
335 // 01234567890123456789012345678901234567
336 // 1 2 3
337 if ( (strUUID[0] == '{')
338 && (strUUID[37] == '}')
339 )
340 guid = strUUID.substr(1, 36).c_str();
341 else
342 guid = strUUID.c_str();
343
344 if (guid.isEmpty())
345 throw ConfigFileError(this, NULL, N_("UUID \"%s\" has invalid format"), strUUID.c_str());
346}
347
348/**
349 * Parses the given string in str and attempts to treat it as an ISO
350 * date/time stamp to put into timestamp. Throws on errors.
351 * @param timestamp
352 * @param str
353 */
354void ConfigFileBase::parseTimestamp(RTTIMESPEC &timestamp,
355 const com::Utf8Str &str) const
356{
357 const char *pcsz = str.c_str();
358 // yyyy-mm-ddThh:mm:ss
359 // "2009-07-10T11:54:03Z"
360 // 01234567890123456789
361 // 1
362 if (str.length() > 19)
363 {
364 // timezone must either be unspecified or 'Z' for UTC
365 if ( (pcsz[19])
366 && (pcsz[19] != 'Z')
367 )
368 throw ConfigFileError(this, NULL, N_("Cannot handle ISO timestamp '%s': is not UTC date"), str.c_str());
369
370 int32_t yyyy;
371 uint32_t mm, dd, hh, min, secs;
372 if ( (pcsz[4] == '-')
373 && (pcsz[7] == '-')
374 && (pcsz[10] == 'T')
375 && (pcsz[13] == ':')
376 && (pcsz[16] == ':')
377 )
378 {
379 int rc;
380 if ( (RT_SUCCESS(rc = RTStrToInt32Ex(pcsz, NULL, 0, &yyyy)))
381 // could theoretically be negative but let's assume that nobody
382 // created virtual machines before the Christian era
383 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 5, NULL, 0, &mm)))
384 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 8, NULL, 0, &dd)))
385 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 11, NULL, 0, &hh)))
386 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 14, NULL, 0, &min)))
387 && (RT_SUCCESS(rc = RTStrToUInt32Ex(pcsz + 17, NULL, 0, &secs)))
388 )
389 {
390 RTTIME time =
391 {
392 yyyy,
393 (uint8_t)mm,
394 0,
395 0,
396 (uint8_t)dd,
397 (uint8_t)hh,
398 (uint8_t)min,
399 (uint8_t)secs,
400 0,
401 RTTIME_FLAGS_TYPE_UTC,
402 0
403 };
404 if (RTTimeNormalize(&time))
405 if (RTTimeImplode(&timestamp, &time))
406 return;
407 }
408
409 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': runtime error, %Rra"), str.c_str(), rc);
410 }
411
412 throw ConfigFileError(this, NULL, N_("Cannot parse ISO timestamp '%s': invalid format"), str.c_str());
413 }
414}
415
416/**
417 * Helper to create a string for a RTTIMESPEC for writing out ISO timestamps.
418 * @param stamp
419 * @return
420 */
421com::Utf8Str ConfigFileBase::makeString(const RTTIMESPEC &stamp)
422{
423 RTTIME time;
424 if (!RTTimeExplode(&time, &stamp))
425 throw ConfigFileError(this, NULL, N_("Timespec %lld ms is invalid"), RTTimeSpecGetMilli(&stamp));
426
427 return Utf8StrFmt("%04ld-%02hd-%02hdT%02hd:%02hd:%02hdZ",
428 time.i32Year,
429 (uint16_t)time.u8Month,
430 (uint16_t)time.u8MonthDay,
431 (uint16_t)time.u8Hour,
432 (uint16_t)time.u8Minute,
433 (uint16_t)time.u8Second);
434}
435
436/**
437 * Helper method to read in an ExtraData subtree and stores its contents
438 * in the given map of extradata items. Used for both main and machine
439 * extradata (MainConfigFile and MachineConfigFile).
440 * @param elmExtraData
441 * @param map
442 */
443void ConfigFileBase::readExtraData(const xml::ElementNode &elmExtraData,
444 ExtraDataItemsMap &map)
445{
446 xml::NodesLoop nlLevel4(elmExtraData);
447 const xml::ElementNode *pelmExtraDataItem;
448 while ((pelmExtraDataItem = nlLevel4.forAllNodes()))
449 {
450 if (pelmExtraDataItem->nameEquals("ExtraDataItem"))
451 {
452 // <ExtraDataItem name="GUI/LastWindowPostion" value="97,88,981,858"/>
453 Utf8Str strName, strValue;
454 if ( ((pelmExtraDataItem->getAttributeValue("name", strName)))
455 && ((pelmExtraDataItem->getAttributeValue("value", strValue)))
456 )
457 map[strName] = strValue;
458 else
459 throw ConfigFileError(this, pelmExtraDataItem, N_("Required ExtraDataItem/@name or @value attribute is missing"));
460 }
461 }
462}
463
464/**
465 * Reads <USBDeviceFilter> entries from under the given elmDeviceFilters node and
466 * stores them in the given linklist. This is in ConfigFileBase because it's used
467 * from both MainConfigFile (for host filters) and MachineConfigFile (for machine
468 * filters).
469 * @param elmDeviceFilters
470 * @param ll
471 */
472void ConfigFileBase::readUSBDeviceFilters(const xml::ElementNode &elmDeviceFilters,
473 USBDeviceFiltersList &ll)
474{
475 xml::NodesLoop nl1(elmDeviceFilters, "DeviceFilter");
476 const xml::ElementNode *pelmLevel4Child;
477 while ((pelmLevel4Child = nl1.forAllNodes()))
478 {
479 USBDeviceFilter flt;
480 flt.action = USBDeviceFilterAction_Ignore;
481 Utf8Str strAction;
482 if ( (pelmLevel4Child->getAttributeValue("name", flt.strName))
483 && (pelmLevel4Child->getAttributeValue("active", flt.fActive))
484 )
485 {
486 if (!pelmLevel4Child->getAttributeValue("vendorId", flt.strVendorId))
487 pelmLevel4Child->getAttributeValue("vendorid", flt.strVendorId); // used before 1.3
488 if (!pelmLevel4Child->getAttributeValue("productId", flt.strProductId))
489 pelmLevel4Child->getAttributeValue("productid", flt.strProductId); // used before 1.3
490 pelmLevel4Child->getAttributeValue("revision", flt.strRevision);
491 pelmLevel4Child->getAttributeValue("manufacturer", flt.strManufacturer);
492 pelmLevel4Child->getAttributeValue("product", flt.strProduct);
493 if (!pelmLevel4Child->getAttributeValue("serialNumber", flt.strSerialNumber))
494 pelmLevel4Child->getAttributeValue("serialnumber", flt.strSerialNumber); // used before 1.3
495 pelmLevel4Child->getAttributeValue("port", flt.strPort);
496
497 // the next 2 are irrelevant for host USB objects
498 pelmLevel4Child->getAttributeValue("remote", flt.strRemote);
499 pelmLevel4Child->getAttributeValue("maskedInterfaces", flt.ulMaskedInterfaces);
500
501 // action is only used with host USB objects
502 if (pelmLevel4Child->getAttributeValue("action", strAction))
503 {
504 if (strAction == "Ignore")
505 flt.action = USBDeviceFilterAction_Ignore;
506 else if (strAction == "Hold")
507 flt.action = USBDeviceFilterAction_Hold;
508 else
509 throw ConfigFileError(this, pelmLevel4Child, N_("Invalid value '%s' in DeviceFilter/@action attribute"), strAction.c_str());
510 }
511
512 ll.push_back(flt);
513 }
514 }
515}
516
517/**
518 * Adds a "version" attribute to the given XML element with the
519 * VirtualBox settings version (e.g. "1.10-linux"). Used by
520 * the XML format for the root element and by the OVF export
521 * for the vbox:Machine element.
522 * @param elm
523 */
524void ConfigFileBase::setVersionAttribute(xml::ElementNode &elm)
525{
526 const char *pcszVersion = NULL;
527 switch (m->sv)
528 {
529 case SettingsVersion_v1_8:
530 pcszVersion = "1.8";
531 break;
532
533 case SettingsVersion_v1_9:
534 pcszVersion = "1.9";
535 break;
536
537 case SettingsVersion_v1_10:
538 pcszVersion = "1.10";
539 break;
540
541 case SettingsVersion_v1_11:
542 pcszVersion = "1.11";
543 break;
544
545 case SettingsVersion_Future:
546 // can be set if this code runs on XML files that were created by a future version of VBox;
547 // in that case, downgrade to current version when writing since we can't write future versions...
548 pcszVersion = "1.11";
549 m->sv = SettingsVersion_v1_10;
550 break;
551
552 default:
553 // silently upgrade if this is less than 1.7 because that's the oldest we can write
554 pcszVersion = "1.7";
555 m->sv = SettingsVersion_v1_7;
556 break;
557 }
558
559 elm.setAttribute("version", Utf8StrFmt("%s-%s",
560 pcszVersion,
561 VBOX_XML_PLATFORM)); // e.g. "linux"
562}
563
564/**
565 * Creates a new stub xml::Document in the m->pDoc member with the
566 * root "VirtualBox" element set up. This is used by both
567 * MainConfigFile and MachineConfigFile at the beginning of writing
568 * out their XML.
569 *
570 * Before calling this, it is the responsibility of the caller to
571 * set the "sv" member to the required settings version that is to
572 * be written. For newly created files, the settings version will be
573 * the latest (1.9); for files read in from disk earlier, it will be
574 * the settings version indicated in the file. However, this method
575 * will silently make sure that the settings version is always
576 * at least 1.7 and change it if necessary, since there is no write
577 * support for earlier settings versions.
578 */
579void ConfigFileBase::createStubDocument()
580{
581 Assert(m->pDoc == NULL);
582 m->pDoc = new xml::Document;
583
584 m->pelmRoot = m->pDoc->createRootElement("VirtualBox");
585 m->pelmRoot->setAttribute("xmlns", VBOX_XML_NAMESPACE);
586
587 // add settings version attribute to root element
588 setVersionAttribute(*m->pelmRoot);
589
590 // since this gets called before the XML document is actually written out,
591 // this is where we must check whether we're upgrading the settings version
592 // and need to make a backup, so the user can go back to an earlier
593 // VirtualBox version and recover his old settings files.
594 if ( (m->svRead != SettingsVersion_Null) // old file exists?
595 && (m->svRead < m->sv) // we're upgrading?
596 )
597 {
598 // compose new filename: strip off trailing ".xml"
599 Utf8Str strFilenameNew = m->strFilename.substr(0, m->strFilename.length() - 4);
600 // and append something likd "-1.3-linux.xml"
601 strFilenameNew.append("-");
602 strFilenameNew.append(m->strSettingsVersionFull); // e.g. "1.3-linux"
603 strFilenameNew.append(".xml");
604
605 RTFileMove(m->strFilename.c_str(),
606 strFilenameNew.c_str(),
607 0); // no RTFILEMOVE_FLAGS_REPLACE
608
609 // do this only once
610 m->svRead = SettingsVersion_Null;
611 }
612}
613
614/**
615 * Creates an <ExtraData> node under the given parent element with
616 * <ExtraDataItem> childern according to the contents of the given
617 * map.
618 * This is in ConfigFileBase because it's used in both MainConfigFile
619 * MachineConfigFile, which both can have extradata.
620 *
621 * @param elmParent
622 * @param me
623 */
624void ConfigFileBase::writeExtraData(xml::ElementNode &elmParent,
625 const ExtraDataItemsMap &me)
626{
627 if (me.size())
628 {
629 xml::ElementNode *pelmExtraData = elmParent.createChild("ExtraData");
630 for (ExtraDataItemsMap::const_iterator it = me.begin();
631 it != me.end();
632 ++it)
633 {
634 const Utf8Str &strName = it->first;
635 const Utf8Str &strValue = it->second;
636 xml::ElementNode *pelmThis = pelmExtraData->createChild("ExtraDataItem");
637 pelmThis->setAttribute("name", strName);
638 pelmThis->setAttribute("value", strValue);
639 }
640 }
641}
642
643/**
644 * Creates <DeviceFilter> nodes under the given parent element according to
645 * the contents of the given USBDeviceFiltersList. This is in ConfigFileBase
646 * because it's used in both MainConfigFile (for host filters) and
647 * MachineConfigFile (for machine filters).
648 *
649 * If fHostMode is true, this means that we're supposed to write filters
650 * for the IHost interface (respect "action", omit "strRemote" and
651 * "ulMaskedInterfaces" in struct USBDeviceFilter).
652 *
653 * @param elmParent
654 * @param ll
655 * @param fHostMode
656 */
657void ConfigFileBase::writeUSBDeviceFilters(xml::ElementNode &elmParent,
658 const USBDeviceFiltersList &ll,
659 bool fHostMode)
660{
661 for (USBDeviceFiltersList::const_iterator it = ll.begin();
662 it != ll.end();
663 ++it)
664 {
665 const USBDeviceFilter &flt = *it;
666 xml::ElementNode *pelmFilter = elmParent.createChild("DeviceFilter");
667 pelmFilter->setAttribute("name", flt.strName);
668 pelmFilter->setAttribute("active", flt.fActive);
669 if (flt.strVendorId.length())
670 pelmFilter->setAttribute("vendorId", flt.strVendorId);
671 if (flt.strProductId.length())
672 pelmFilter->setAttribute("productId", flt.strProductId);
673 if (flt.strRevision.length())
674 pelmFilter->setAttribute("revision", flt.strRevision);
675 if (flt.strManufacturer.length())
676 pelmFilter->setAttribute("manufacturer", flt.strManufacturer);
677 if (flt.strProduct.length())
678 pelmFilter->setAttribute("product", flt.strProduct);
679 if (flt.strSerialNumber.length())
680 pelmFilter->setAttribute("serialNumber", flt.strSerialNumber);
681 if (flt.strPort.length())
682 pelmFilter->setAttribute("port", flt.strPort);
683
684 if (fHostMode)
685 {
686 const char *pcsz =
687 (flt.action == USBDeviceFilterAction_Ignore) ? "Ignore"
688 : /*(flt.action == USBDeviceFilterAction_Hold) ?*/ "Hold";
689 pelmFilter->setAttribute("action", pcsz);
690 }
691 else
692 {
693 if (flt.strRemote.length())
694 pelmFilter->setAttribute("remote", flt.strRemote);
695 if (flt.ulMaskedInterfaces)
696 pelmFilter->setAttribute("maskedInterfaces", flt.ulMaskedInterfaces);
697 }
698 }
699}
700
701/**
702 * Cleans up memory allocated by the internal XML parser. To be called by
703 * descendant classes when they're done analyzing the DOM tree to discard it.
704 */
705void ConfigFileBase::clearDocument()
706{
707 m->cleanup();
708}
709
710/**
711 * Returns true only if the underlying config file exists on disk;
712 * either because the file has been loaded from disk, or it's been written
713 * to disk, or both.
714 * @return
715 */
716bool ConfigFileBase::fileExists()
717{
718 return m->fFileExists;
719}
720
721/**
722 * Copies the base variables from another instance. Used by Machine::saveSettings
723 * so that the settings version does not get lost when a copy of the Machine settings
724 * file is made to see if settings have actually changed.
725 * @param b
726 */
727void ConfigFileBase::copyBaseFrom(const ConfigFileBase &b)
728{
729 m->copyFrom(*b.m);
730}
731
732////////////////////////////////////////////////////////////////////////////////
733//
734// Structures shared between Machine XML and VirtualBox.xml
735//
736////////////////////////////////////////////////////////////////////////////////
737
738/**
739 * Comparison operator. This gets called from MachineConfigFile::operator==,
740 * which in turn gets called from Machine::saveSettings to figure out whether
741 * machine settings have really changed and thus need to be written out to disk.
742 */
743bool USBDeviceFilter::operator==(const USBDeviceFilter &u) const
744{
745 return ( (this == &u)
746 || ( (strName == u.strName)
747 && (fActive == u.fActive)
748 && (strVendorId == u.strVendorId)
749 && (strProductId == u.strProductId)
750 && (strRevision == u.strRevision)
751 && (strManufacturer == u.strManufacturer)
752 && (strProduct == u.strProduct)
753 && (strSerialNumber == u.strSerialNumber)
754 && (strPort == u.strPort)
755 && (action == u.action)
756 && (strRemote == u.strRemote)
757 && (ulMaskedInterfaces == u.ulMaskedInterfaces)
758 )
759 );
760}
761
762////////////////////////////////////////////////////////////////////////////////
763//
764// MainConfigFile
765//
766////////////////////////////////////////////////////////////////////////////////
767
768/**
769 * Reads one <MachineEntry> from the main VirtualBox.xml file.
770 * @param elmMachineRegistry
771 */
772void MainConfigFile::readMachineRegistry(const xml::ElementNode &elmMachineRegistry)
773{
774 // <MachineEntry uuid="{ xxx }" src=" xxx "/>
775 xml::NodesLoop nl1(elmMachineRegistry);
776 const xml::ElementNode *pelmChild1;
777 while ((pelmChild1 = nl1.forAllNodes()))
778 {
779 if (pelmChild1->nameEquals("MachineEntry"))
780 {
781 MachineRegistryEntry mre;
782 Utf8Str strUUID;
783 if ( ((pelmChild1->getAttributeValue("uuid", strUUID)))
784 && ((pelmChild1->getAttributeValue("src", mre.strSettingsFile)))
785 )
786 {
787 parseUUID(mre.uuid, strUUID);
788 llMachines.push_back(mre);
789 }
790 else
791 throw ConfigFileError(this, pelmChild1, N_("Required MachineEntry/@uuid or @src attribute is missing"));
792 }
793 }
794}
795
796/**
797 * Reads a media registry entry from the main VirtualBox.xml file.
798 *
799 * Whereas the current media registry code is fairly straightforward, it was quite a mess
800 * with settings format before 1.4 (VirtualBox 2.0 used settings format 1.3). The elements
801 * in the media registry were much more inconsistent, and different elements were used
802 * depending on the type of device and image.
803 *
804 * @param t
805 * @param elmMedium
806 * @param llMedia
807 */
808void MainConfigFile::readMedium(MediaType t,
809 const xml::ElementNode &elmMedium, // HardDisk node if root; if recursing,
810 // child HardDisk node or DiffHardDisk node for pre-1.4
811 MediaList &llMedia) // list to append medium to (root disk or child list)
812{
813 // <HardDisk uuid="{5471ecdb-1ddb-4012-a801-6d98e226868b}" location="/mnt/innotek-unix/vdis/Windows XP.vdi" format="VDI" type="Normal">
814 settings::Medium med;
815 Utf8Str strUUID;
816 if (!(elmMedium.getAttributeValue("uuid", strUUID)))
817 throw ConfigFileError(this, &elmMedium, N_("Required %s/@uuid attribute is missing"), elmMedium.getName());
818
819 parseUUID(med.uuid, strUUID);
820
821 bool fNeedsLocation = true;
822
823 if (t == HardDisk)
824 {
825 if (m->sv < SettingsVersion_v1_4)
826 {
827 // here the system is:
828 // <HardDisk uuid="{....}" type="normal">
829 // <VirtualDiskImage filePath="/path/to/xxx.vdi"/>
830 // </HardDisk>
831
832 fNeedsLocation = false;
833 bool fNeedsFilePath = true;
834 const xml::ElementNode *pelmImage;
835 if ((pelmImage = elmMedium.findChildElement("VirtualDiskImage")))
836 med.strFormat = "VDI";
837 else if ((pelmImage = elmMedium.findChildElement("VMDKImage")))
838 med.strFormat = "VMDK";
839 else if ((pelmImage = elmMedium.findChildElement("VHDImage")))
840 med.strFormat = "VHD";
841 else if ((pelmImage = elmMedium.findChildElement("ISCSIHardDisk")))
842 {
843 med.strFormat = "iSCSI";
844
845 fNeedsFilePath = false;
846 // location is special here: current settings specify an "iscsi://user@server:port/target/lun"
847 // string for the location and also have several disk properties for these, whereas this used
848 // to be hidden in several sub-elements before 1.4, so compose a location string and set up
849 // the properties:
850 med.strLocation = "iscsi://";
851 Utf8Str strUser, strServer, strPort, strTarget, strLun;
852 if (pelmImage->getAttributeValue("userName", strUser))
853 {
854 med.strLocation.append(strUser);
855 med.strLocation.append("@");
856 }
857 Utf8Str strServerAndPort;
858 if (pelmImage->getAttributeValue("server", strServer))
859 {
860 strServerAndPort = strServer;
861 }
862 if (pelmImage->getAttributeValue("port", strPort))
863 {
864 if (strServerAndPort.length())
865 strServerAndPort.append(":");
866 strServerAndPort.append(strPort);
867 }
868 med.strLocation.append(strServerAndPort);
869 if (pelmImage->getAttributeValue("target", strTarget))
870 {
871 med.strLocation.append("/");
872 med.strLocation.append(strTarget);
873 }
874 if (pelmImage->getAttributeValue("lun", strLun))
875 {
876 med.strLocation.append("/");
877 med.strLocation.append(strLun);
878 }
879
880 if (strServer.length() && strPort.length())
881 med.properties["TargetAddress"] = strServerAndPort;
882 if (strTarget.length())
883 med.properties["TargetName"] = strTarget;
884 if (strUser.length())
885 med.properties["InitiatorUsername"] = strUser;
886 Utf8Str strPassword;
887 if (pelmImage->getAttributeValue("password", strPassword))
888 med.properties["InitiatorSecret"] = strPassword;
889 if (strLun.length())
890 med.properties["LUN"] = strLun;
891 }
892 else if ((pelmImage = elmMedium.findChildElement("CustomHardDisk")))
893 {
894 fNeedsFilePath = false;
895 fNeedsLocation = true;
896 // also requires @format attribute, which will be queried below
897 }
898 else
899 throw ConfigFileError(this, &elmMedium, N_("Required %s/VirtualDiskImage element is missing"), elmMedium.getName());
900
901 if (fNeedsFilePath)
902 if (!(pelmImage->getAttributeValue("filePath", med.strLocation)))
903 throw ConfigFileError(this, &elmMedium, N_("Required %s/@filePath attribute is missing"), elmMedium.getName());
904 }
905
906 if (med.strFormat.isEmpty()) // not set with 1.4 format above, or 1.4 Custom format?
907 if (!(elmMedium.getAttributeValue("format", med.strFormat)))
908 throw ConfigFileError(this, &elmMedium, N_("Required %s/@format attribute is missing"), elmMedium.getName());
909
910 if (!(elmMedium.getAttributeValue("autoReset", med.fAutoReset)))
911 med.fAutoReset = false;
912
913 Utf8Str strType;
914 if ((elmMedium.getAttributeValue("type", strType)))
915 {
916 // pre-1.4 used lower case, so make this case-insensitive
917 strType.toUpper();
918 if (strType == "NORMAL")
919 med.hdType = MediumType_Normal;
920 else if (strType == "IMMUTABLE")
921 med.hdType = MediumType_Immutable;
922 else if (strType == "WRITETHROUGH")
923 med.hdType = MediumType_Writethrough;
924 else if (strType == "SHAREABLE")
925 med.hdType = MediumType_Shareable;
926 else
927 throw ConfigFileError(this, &elmMedium, N_("HardDisk/@type attribute must be one of Normal, Immutable or Writethrough"));
928 }
929 }
930 else if (m->sv < SettingsVersion_v1_4)
931 {
932 // DVD and floppy images before 1.4 had "src" attribute instead of "location"
933 if (!(elmMedium.getAttributeValue("src", med.strLocation)))
934 throw ConfigFileError(this, &elmMedium, N_("Required %s/@src attribute is missing"), elmMedium.getName());
935
936 fNeedsLocation = false;
937 }
938
939 if (fNeedsLocation)
940 // current files and 1.4 CustomHardDisk elements must have a location attribute
941 if (!(elmMedium.getAttributeValue("location", med.strLocation)))
942 throw ConfigFileError(this, &elmMedium, N_("Required %s/@location attribute is missing"), elmMedium.getName());
943
944 elmMedium.getAttributeValue("Description", med.strDescription); // optional
945
946 // recurse to handle children
947 xml::NodesLoop nl2(elmMedium);
948 const xml::ElementNode *pelmHDChild;
949 while ((pelmHDChild = nl2.forAllNodes()))
950 {
951 if ( t == HardDisk
952 && ( pelmHDChild->nameEquals("HardDisk")
953 || ( (m->sv < SettingsVersion_v1_4)
954 && (pelmHDChild->nameEquals("DiffHardDisk"))
955 )
956 )
957 )
958 // recurse with this element and push the child onto our current children list
959 readMedium(t,
960 *pelmHDChild,
961 med.llChildren);
962 else if (pelmHDChild->nameEquals("Property"))
963 {
964 Utf8Str strPropName, strPropValue;
965 if ( (pelmHDChild->getAttributeValue("name", strPropName))
966 && (pelmHDChild->getAttributeValue("value", strPropValue))
967 )
968 med.properties[strPropName] = strPropValue;
969 else
970 throw ConfigFileError(this, pelmHDChild, N_("Required HardDisk/Property/@name or @value attribute is missing"));
971 }
972 }
973
974 llMedia.push_back(med);
975}
976
977/**
978 * Reads in the entire <MediaRegistry> chunk. For pre-1.4 files, this gets called
979 * with the <DiskRegistry> chunk instead.
980 * @param elmMediaRegistry
981 */
982void MainConfigFile::readMediaRegistry(const xml::ElementNode &elmMediaRegistry)
983{
984 xml::NodesLoop nl1(elmMediaRegistry);
985 const xml::ElementNode *pelmChild1;
986 while ((pelmChild1 = nl1.forAllNodes()))
987 {
988 MediaType t = Error;
989 if (pelmChild1->nameEquals("HardDisks"))
990 t = HardDisk;
991 else if (pelmChild1->nameEquals("DVDImages"))
992 t = DVDImage;
993 else if (pelmChild1->nameEquals("FloppyImages"))
994 t = FloppyImage;
995 else
996 continue;
997
998 xml::NodesLoop nl2(*pelmChild1);
999 const xml::ElementNode *pelmMedium;
1000 while ((pelmMedium = nl2.forAllNodes()))
1001 {
1002 if ( t == HardDisk
1003 && (pelmMedium->nameEquals("HardDisk"))
1004 )
1005 readMedium(t,
1006 *pelmMedium,
1007 llHardDisks); // list to append hard disk data to: the root list
1008 else if ( t == DVDImage
1009 && (pelmMedium->nameEquals("Image"))
1010 )
1011 readMedium(t,
1012 *pelmMedium,
1013 llDvdImages); // list to append dvd images to: the root list
1014 else if ( t == FloppyImage
1015 && (pelmMedium->nameEquals("Image"))
1016 )
1017 readMedium(t,
1018 *pelmMedium,
1019 llFloppyImages); // list to append floppy images to: the root list
1020 }
1021 }
1022}
1023
1024/**
1025 * Reads in the <DHCPServers> chunk.
1026 * @param elmDHCPServers
1027 */
1028void MainConfigFile::readDHCPServers(const xml::ElementNode &elmDHCPServers)
1029{
1030 xml::NodesLoop nl1(elmDHCPServers);
1031 const xml::ElementNode *pelmServer;
1032 while ((pelmServer = nl1.forAllNodes()))
1033 {
1034 if (pelmServer->nameEquals("DHCPServer"))
1035 {
1036 DHCPServer srv;
1037 if ( (pelmServer->getAttributeValue("networkName", srv.strNetworkName))
1038 && (pelmServer->getAttributeValue("IPAddress", srv.strIPAddress))
1039 && (pelmServer->getAttributeValue("networkMask", srv.strIPNetworkMask))
1040 && (pelmServer->getAttributeValue("lowerIP", srv.strIPLower))
1041 && (pelmServer->getAttributeValue("upperIP", srv.strIPUpper))
1042 && (pelmServer->getAttributeValue("enabled", srv.fEnabled))
1043 )
1044 llDhcpServers.push_back(srv);
1045 else
1046 throw ConfigFileError(this, pelmServer, N_("Required DHCPServer/@networkName, @IPAddress, @networkMask, @lowerIP, @upperIP or @enabled attribute is missing"));
1047 }
1048 }
1049}
1050
1051/**
1052 * Constructor.
1053 *
1054 * If pstrFilename is != NULL, this reads the given settings file into the member
1055 * variables and various substructures and lists. Otherwise, the member variables
1056 * are initialized with default values.
1057 *
1058 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1059 * the caller should catch; if this constructor does not throw, then the member
1060 * variables contain meaningful values (either from the file or defaults).
1061 *
1062 * @param strFilename
1063 */
1064MainConfigFile::MainConfigFile(const Utf8Str *pstrFilename)
1065 : ConfigFileBase(pstrFilename)
1066{
1067 if (pstrFilename)
1068 {
1069 // the ConfigFileBase constructor has loaded the XML file, so now
1070 // we need only analyze what is in there
1071 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1072 const xml::ElementNode *pelmRootChild;
1073 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1074 {
1075 if (pelmRootChild->nameEquals("Global"))
1076 {
1077 xml::NodesLoop nlGlobalChildren(*pelmRootChild);
1078 const xml::ElementNode *pelmGlobalChild;
1079 while ((pelmGlobalChild = nlGlobalChildren.forAllNodes()))
1080 {
1081 if (pelmGlobalChild->nameEquals("SystemProperties"))
1082 {
1083 pelmGlobalChild->getAttributeValue("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1084 if (!pelmGlobalChild->getAttributeValue("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder))
1085 // pre-1.4 used @defaultVDIFolder instead
1086 pelmGlobalChild->getAttributeValue("defaultVDIFolder", systemProperties.strDefaultHardDiskFolder);
1087 pelmGlobalChild->getAttributeValue("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1088 pelmGlobalChild->getAttributeValue("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
1089 pelmGlobalChild->getAttributeValue("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1090 pelmGlobalChild->getAttributeValue("LogHistoryCount", systemProperties.ulLogHistoryCount);
1091 }
1092 else if (pelmGlobalChild->nameEquals("ExtraData"))
1093 readExtraData(*pelmGlobalChild, mapExtraDataItems);
1094 else if (pelmGlobalChild->nameEquals("MachineRegistry"))
1095 readMachineRegistry(*pelmGlobalChild);
1096 else if ( (pelmGlobalChild->nameEquals("MediaRegistry"))
1097 || ( (m->sv < SettingsVersion_v1_4)
1098 && (pelmGlobalChild->nameEquals("DiskRegistry"))
1099 )
1100 )
1101 readMediaRegistry(*pelmGlobalChild);
1102 else if (pelmGlobalChild->nameEquals("NetserviceRegistry"))
1103 {
1104 xml::NodesLoop nlLevel4(*pelmGlobalChild);
1105 const xml::ElementNode *pelmLevel4Child;
1106 while ((pelmLevel4Child = nlLevel4.forAllNodes()))
1107 {
1108 if (pelmLevel4Child->nameEquals("DHCPServers"))
1109 readDHCPServers(*pelmLevel4Child);
1110 }
1111 }
1112 else if (pelmGlobalChild->nameEquals("USBDeviceFilters"))
1113 readUSBDeviceFilters(*pelmGlobalChild, host.llUSBDeviceFilters);
1114 }
1115 } // end if (pelmRootChild->nameEquals("Global"))
1116 }
1117
1118 clearDocument();
1119 }
1120
1121 // DHCP servers were introduced with settings version 1.7; if we're loading
1122 // from an older version OR this is a fresh install, then add one DHCP server
1123 // with default settings
1124 if ( (!llDhcpServers.size())
1125 && ( (!pstrFilename) // empty VirtualBox.xml file
1126 || (m->sv < SettingsVersion_v1_7) // upgrading from before 1.7
1127 )
1128 )
1129 {
1130 DHCPServer srv;
1131 srv.strNetworkName =
1132#ifdef RT_OS_WINDOWS
1133 "HostInterfaceNetworking-VirtualBox Host-Only Ethernet Adapter";
1134#else
1135 "HostInterfaceNetworking-vboxnet0";
1136#endif
1137 srv.strIPAddress = "192.168.56.100";
1138 srv.strIPNetworkMask = "255.255.255.0";
1139 srv.strIPLower = "192.168.56.101";
1140 srv.strIPUpper = "192.168.56.254";
1141 srv.fEnabled = true;
1142 llDhcpServers.push_back(srv);
1143 }
1144}
1145
1146/**
1147 * Creates a single <HardDisk> element for the given Medium structure
1148 * and recurses to write the child hard disks underneath. Called from
1149 * MainConfigFile::write().
1150 *
1151 * @param elmMedium
1152 * @param m
1153 * @param level
1154 */
1155void MainConfigFile::writeHardDisk(xml::ElementNode &elmMedium,
1156 const Medium &mdm,
1157 uint32_t level) // 0 for "root" call, incremented with each recursion
1158{
1159 xml::ElementNode *pelmHardDisk = elmMedium.createChild("HardDisk");
1160 pelmHardDisk->setAttribute("uuid", mdm.uuid.toStringCurly());
1161 pelmHardDisk->setAttribute("location", mdm.strLocation);
1162 pelmHardDisk->setAttribute("format", mdm.strFormat);
1163 if (mdm.fAutoReset)
1164 pelmHardDisk->setAttribute("autoReset", mdm.fAutoReset);
1165 if (mdm.strDescription.length())
1166 pelmHardDisk->setAttribute("Description", mdm.strDescription);
1167
1168 for (PropertiesMap::const_iterator it = mdm.properties.begin();
1169 it != mdm.properties.end();
1170 ++it)
1171 {
1172 xml::ElementNode *pelmProp = pelmHardDisk->createChild("Property");
1173 pelmProp->setAttribute("name", it->first);
1174 pelmProp->setAttribute("value", it->second);
1175 }
1176
1177 // only for base hard disks, save the type
1178 if (level == 0)
1179 {
1180 const char *pcszType =
1181 mdm.hdType == MediumType_Normal ? "Normal" :
1182 mdm.hdType == MediumType_Immutable ? "Immutable" :
1183 mdm.hdType == MediumType_Writethrough ? "Writethrough" :
1184 mdm.hdType == MediumType_Shareable ? "Shareable" : "INVALID";
1185 pelmHardDisk->setAttribute("type", pcszType);
1186 }
1187
1188 for (MediaList::const_iterator it = mdm.llChildren.begin();
1189 it != mdm.llChildren.end();
1190 ++it)
1191 {
1192 // recurse for children
1193 writeHardDisk(*pelmHardDisk, // parent
1194 *it, // settings::Medium
1195 ++level); // recursion level
1196 }
1197}
1198
1199/**
1200 * Called from the IVirtualBox interface to write out VirtualBox.xml. This
1201 * builds an XML DOM tree and writes it out to disk.
1202 */
1203void MainConfigFile::write(const com::Utf8Str strFilename)
1204{
1205 m->strFilename = strFilename;
1206 createStubDocument();
1207
1208 xml::ElementNode *pelmGlobal = m->pelmRoot->createChild("Global");
1209
1210 writeExtraData(*pelmGlobal, mapExtraDataItems);
1211
1212 xml::ElementNode *pelmMachineRegistry = pelmGlobal->createChild("MachineRegistry");
1213 for (MachinesRegistry::const_iterator it = llMachines.begin();
1214 it != llMachines.end();
1215 ++it)
1216 {
1217 // <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"/>
1218 const MachineRegistryEntry &mre = *it;
1219 xml::ElementNode *pelmMachineEntry = pelmMachineRegistry->createChild("MachineEntry");
1220 pelmMachineEntry->setAttribute("uuid", mre.uuid.toStringCurly());
1221 pelmMachineEntry->setAttribute("src", mre.strSettingsFile);
1222 }
1223
1224 xml::ElementNode *pelmMediaRegistry = pelmGlobal->createChild("MediaRegistry");
1225
1226 xml::ElementNode *pelmHardDisks = pelmMediaRegistry->createChild("HardDisks");
1227 for (MediaList::const_iterator it = llHardDisks.begin();
1228 it != llHardDisks.end();
1229 ++it)
1230 {
1231 writeHardDisk(*pelmHardDisks, *it, 0);
1232 }
1233
1234 xml::ElementNode *pelmDVDImages = pelmMediaRegistry->createChild("DVDImages");
1235 for (MediaList::const_iterator it = llDvdImages.begin();
1236 it != llDvdImages.end();
1237 ++it)
1238 {
1239 const Medium &mdm = *it;
1240 xml::ElementNode *pelmMedium = pelmDVDImages->createChild("Image");
1241 pelmMedium->setAttribute("uuid", mdm.uuid.toStringCurly());
1242 pelmMedium->setAttribute("location", mdm.strLocation);
1243 if (mdm.strDescription.length())
1244 pelmMedium->setAttribute("Description", mdm.strDescription);
1245 }
1246
1247 xml::ElementNode *pelmFloppyImages = pelmMediaRegistry->createChild("FloppyImages");
1248 for (MediaList::const_iterator it = llFloppyImages.begin();
1249 it != llFloppyImages.end();
1250 ++it)
1251 {
1252 const Medium &mdm = *it;
1253 xml::ElementNode *pelmMedium = pelmFloppyImages->createChild("Image");
1254 pelmMedium->setAttribute("uuid", mdm.uuid.toStringCurly());
1255 pelmMedium->setAttribute("location", mdm.strLocation);
1256 if (mdm.strDescription.length())
1257 pelmMedium->setAttribute("Description", mdm.strDescription);
1258 }
1259
1260 xml::ElementNode *pelmNetserviceRegistry = pelmGlobal->createChild("NetserviceRegistry");
1261 xml::ElementNode *pelmDHCPServers = pelmNetserviceRegistry->createChild("DHCPServers");
1262 for (DHCPServersList::const_iterator it = llDhcpServers.begin();
1263 it != llDhcpServers.end();
1264 ++it)
1265 {
1266 const DHCPServer &d = *it;
1267 xml::ElementNode *pelmThis = pelmDHCPServers->createChild("DHCPServer");
1268 pelmThis->setAttribute("networkName", d.strNetworkName);
1269 pelmThis->setAttribute("IPAddress", d.strIPAddress);
1270 pelmThis->setAttribute("networkMask", d.strIPNetworkMask);
1271 pelmThis->setAttribute("lowerIP", d.strIPLower);
1272 pelmThis->setAttribute("upperIP", d.strIPUpper);
1273 pelmThis->setAttribute("enabled", (d.fEnabled) ? 1 : 0); // too bad we chose 1 vs. 0 here
1274 }
1275
1276 xml::ElementNode *pelmSysProps = pelmGlobal->createChild("SystemProperties");
1277 if (systemProperties.strDefaultMachineFolder.length())
1278 pelmSysProps->setAttribute("defaultMachineFolder", systemProperties.strDefaultMachineFolder);
1279 if (systemProperties.strDefaultHardDiskFolder.length())
1280 pelmSysProps->setAttribute("defaultHardDiskFolder", systemProperties.strDefaultHardDiskFolder);
1281 if (systemProperties.strDefaultHardDiskFormat.length())
1282 pelmSysProps->setAttribute("defaultHardDiskFormat", systemProperties.strDefaultHardDiskFormat);
1283 if (systemProperties.strRemoteDisplayAuthLibrary.length())
1284 pelmSysProps->setAttribute("remoteDisplayAuthLibrary", systemProperties.strRemoteDisplayAuthLibrary);
1285 if (systemProperties.strWebServiceAuthLibrary.length())
1286 pelmSysProps->setAttribute("webServiceAuthLibrary", systemProperties.strWebServiceAuthLibrary);
1287 pelmSysProps->setAttribute("LogHistoryCount", systemProperties.ulLogHistoryCount);
1288
1289 writeUSBDeviceFilters(*pelmGlobal->createChild("USBDeviceFilters"),
1290 host.llUSBDeviceFilters,
1291 true); // fHostMode
1292
1293 // now go write the XML
1294 xml::XmlFileWriter writer(*m->pDoc);
1295 writer.write(m->strFilename.c_str(), true /*fSafe*/);
1296
1297 m->fFileExists = true;
1298
1299 clearDocument();
1300}
1301
1302////////////////////////////////////////////////////////////////////////////////
1303//
1304// Machine XML structures
1305//
1306////////////////////////////////////////////////////////////////////////////////
1307
1308/**
1309 * Comparison operator. This gets called from MachineConfigFile::operator==,
1310 * which in turn gets called from Machine::saveSettings to figure out whether
1311 * machine settings have really changed and thus need to be written out to disk.
1312 */
1313bool VRDPSettings::operator==(const VRDPSettings& v) const
1314{
1315 return ( (this == &v)
1316 || ( (fEnabled == v.fEnabled)
1317 && (strPort == v.strPort)
1318 && (strNetAddress == v.strNetAddress)
1319 && (authType == v.authType)
1320 && (ulAuthTimeout == v.ulAuthTimeout)
1321 && (fAllowMultiConnection == v.fAllowMultiConnection)
1322 && (fReuseSingleConnection == v.fReuseSingleConnection)
1323 && (fVideoChannel == v.fVideoChannel)
1324 && (ulVideoChannelQuality == v.ulVideoChannelQuality)
1325 )
1326 );
1327}
1328
1329/**
1330 * Comparison operator. This gets called from MachineConfigFile::operator==,
1331 * which in turn gets called from Machine::saveSettings to figure out whether
1332 * machine settings have really changed and thus need to be written out to disk.
1333 */
1334bool BIOSSettings::operator==(const BIOSSettings &d) const
1335{
1336 return ( (this == &d)
1337 || ( fACPIEnabled == d.fACPIEnabled
1338 && fIOAPICEnabled == d.fIOAPICEnabled
1339 && fLogoFadeIn == d.fLogoFadeIn
1340 && fLogoFadeOut == d.fLogoFadeOut
1341 && ulLogoDisplayTime == d.ulLogoDisplayTime
1342 && strLogoImagePath == d.strLogoImagePath
1343 && biosBootMenuMode == d.biosBootMenuMode
1344 && fPXEDebugEnabled == d.fPXEDebugEnabled
1345 && llTimeOffset == d.llTimeOffset)
1346 );
1347}
1348
1349/**
1350 * Comparison operator. This gets called from MachineConfigFile::operator==,
1351 * which in turn gets called from Machine::saveSettings to figure out whether
1352 * machine settings have really changed and thus need to be written out to disk.
1353 */
1354bool USBController::operator==(const USBController &u) const
1355{
1356 return ( (this == &u)
1357 || ( (fEnabled == u.fEnabled)
1358 && (fEnabledEHCI == u.fEnabledEHCI)
1359 && (llDeviceFilters == u.llDeviceFilters)
1360 )
1361 );
1362}
1363
1364/**
1365 * Comparison operator. This gets called from MachineConfigFile::operator==,
1366 * which in turn gets called from Machine::saveSettings to figure out whether
1367 * machine settings have really changed and thus need to be written out to disk.
1368 */
1369bool NetworkAdapter::operator==(const NetworkAdapter &n) const
1370{
1371 return ( (this == &n)
1372 || ( (ulSlot == n.ulSlot)
1373 && (type == n.type)
1374 && (fEnabled == n.fEnabled)
1375 && (strMACAddress == n.strMACAddress)
1376 && (fCableConnected == n.fCableConnected)
1377 && (ulLineSpeed == n.ulLineSpeed)
1378 && (fTraceEnabled == n.fTraceEnabled)
1379 && (strTraceFile == n.strTraceFile)
1380 && (mode == n.mode)
1381 && (nat == n.nat)
1382 && (strName == n.strName)
1383 && (ulBootPriority == n.ulBootPriority)
1384 && (fHasDisabledNAT == n.fHasDisabledNAT)
1385 )
1386 );
1387}
1388
1389/**
1390 * Comparison operator. This gets called from MachineConfigFile::operator==,
1391 * which in turn gets called from Machine::saveSettings to figure out whether
1392 * machine settings have really changed and thus need to be written out to disk.
1393 */
1394bool SerialPort::operator==(const SerialPort &s) const
1395{
1396 return ( (this == &s)
1397 || ( (ulSlot == s.ulSlot)
1398 && (fEnabled == s.fEnabled)
1399 && (ulIOBase == s.ulIOBase)
1400 && (ulIRQ == s.ulIRQ)
1401 && (portMode == s.portMode)
1402 && (strPath == s.strPath)
1403 && (fServer == s.fServer)
1404 )
1405 );
1406}
1407
1408/**
1409 * Comparison operator. This gets called from MachineConfigFile::operator==,
1410 * which in turn gets called from Machine::saveSettings to figure out whether
1411 * machine settings have really changed and thus need to be written out to disk.
1412 */
1413bool ParallelPort::operator==(const ParallelPort &s) const
1414{
1415 return ( (this == &s)
1416 || ( (ulSlot == s.ulSlot)
1417 && (fEnabled == s.fEnabled)
1418 && (ulIOBase == s.ulIOBase)
1419 && (ulIRQ == s.ulIRQ)
1420 && (strPath == s.strPath)
1421 )
1422 );
1423}
1424
1425/**
1426 * Comparison operator. This gets called from MachineConfigFile::operator==,
1427 * which in turn gets called from Machine::saveSettings to figure out whether
1428 * machine settings have really changed and thus need to be written out to disk.
1429 */
1430bool SharedFolder::operator==(const SharedFolder &g) const
1431{
1432 return ( (this == &g)
1433 || ( (strName == g.strName)
1434 && (strHostPath == g.strHostPath)
1435 && (fWritable == g.fWritable)
1436 && (fAutoMount == g.fAutoMount)
1437 )
1438 );
1439}
1440
1441/**
1442 * Comparison operator. This gets called from MachineConfigFile::operator==,
1443 * which in turn gets called from Machine::saveSettings to figure out whether
1444 * machine settings have really changed and thus need to be written out to disk.
1445 */
1446bool GuestProperty::operator==(const GuestProperty &g) const
1447{
1448 return ( (this == &g)
1449 || ( (strName == g.strName)
1450 && (strValue == g.strValue)
1451 && (timestamp == g.timestamp)
1452 && (strFlags == g.strFlags)
1453 )
1454 );
1455}
1456
1457// use a define for the platform-dependent default value of
1458// hwvirt exclusivity, since we'll need to check that value
1459// in bumpSettingsVersionIfNeeded()
1460#if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS)
1461 #define HWVIRTEXCLUSIVEDEFAULT false
1462#else
1463 #define HWVIRTEXCLUSIVEDEFAULT true
1464#endif
1465
1466/**
1467 * Hardware struct constructor.
1468 */
1469Hardware::Hardware()
1470 : strVersion("1"),
1471 fHardwareVirt(true),
1472 fHardwareVirtExclusive(HWVIRTEXCLUSIVEDEFAULT),
1473 fNestedPaging(true),
1474 fLargePages(false),
1475 fVPID(true),
1476 fSyntheticCpu(false),
1477 fPAE(false),
1478 cCPUs(1),
1479 fCpuHotPlug(false),
1480 fHpetEnabled(false),
1481 ulMemorySizeMB((uint32_t)-1),
1482 ulVRAMSizeMB(8),
1483 cMonitors(1),
1484 fAccelerate3D(false),
1485 fAccelerate2DVideo(false),
1486 firmwareType(FirmwareType_BIOS),
1487 pointingHidType(PointingHidType_PS2Mouse),
1488 keyboardHidType(KeyboardHidType_PS2Keyboard),
1489 clipboardMode(ClipboardMode_Bidirectional),
1490 ulMemoryBalloonSize(0),
1491 fPageFusionEnabled(false)
1492{
1493 mapBootOrder[0] = DeviceType_Floppy;
1494 mapBootOrder[1] = DeviceType_DVD;
1495 mapBootOrder[2] = DeviceType_HardDisk;
1496
1497 /* The default value for PAE depends on the host:
1498 * - 64 bits host -> always true
1499 * - 32 bits host -> true for Windows & Darwin (masked off if the host cpu doesn't support it anyway)
1500 */
1501#if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN)
1502 fPAE = true;
1503#endif
1504}
1505
1506/**
1507 * Comparison operator. This gets called from MachineConfigFile::operator==,
1508 * which in turn gets called from Machine::saveSettings to figure out whether
1509 * machine settings have really changed and thus need to be written out to disk.
1510 */
1511bool Hardware::operator==(const Hardware& h) const
1512{
1513 return ( (this == &h)
1514 || ( (strVersion == h.strVersion)
1515 && (uuid == h.uuid)
1516 && (fHardwareVirt == h.fHardwareVirt)
1517 && (fHardwareVirtExclusive == h.fHardwareVirtExclusive)
1518 && (fNestedPaging == h.fNestedPaging)
1519 && (fLargePages == h.fLargePages)
1520 && (fVPID == h.fVPID)
1521 && (fSyntheticCpu == h.fSyntheticCpu)
1522 && (fPAE == h.fPAE)
1523 && (cCPUs == h.cCPUs)
1524 && (fCpuHotPlug == h.fCpuHotPlug)
1525 && (fHpetEnabled == h.fHpetEnabled)
1526 && (llCpus == h.llCpus)
1527 && (llCpuIdLeafs == h.llCpuIdLeafs)
1528 && (ulMemorySizeMB == h.ulMemorySizeMB)
1529 && (mapBootOrder == h.mapBootOrder)
1530 && (ulVRAMSizeMB == h.ulVRAMSizeMB)
1531 && (cMonitors == h.cMonitors)
1532 && (fAccelerate3D == h.fAccelerate3D)
1533 && (fAccelerate2DVideo == h.fAccelerate2DVideo)
1534 && (firmwareType == h.firmwareType)
1535 && (pointingHidType == h.pointingHidType)
1536 && (keyboardHidType == h.keyboardHidType)
1537 && (vrdpSettings == h.vrdpSettings)
1538 && (biosSettings == h.biosSettings)
1539 && (usbController == h.usbController)
1540 && (llNetworkAdapters == h.llNetworkAdapters)
1541 && (llSerialPorts == h.llSerialPorts)
1542 && (llParallelPorts == h.llParallelPorts)
1543 && (audioAdapter == h.audioAdapter)
1544 && (llSharedFolders == h.llSharedFolders)
1545 && (clipboardMode == h.clipboardMode)
1546 && (ulMemoryBalloonSize == h.ulMemoryBalloonSize)
1547 && (fPageFusionEnabled == h.fPageFusionEnabled)
1548 && (llGuestProperties == h.llGuestProperties)
1549 && (strNotificationPatterns == h.strNotificationPatterns)
1550 )
1551 );
1552}
1553
1554/**
1555 * Comparison operator. This gets called from MachineConfigFile::operator==,
1556 * which in turn gets called from Machine::saveSettings to figure out whether
1557 * machine settings have really changed and thus need to be written out to disk.
1558 */
1559bool AttachedDevice::operator==(const AttachedDevice &a) const
1560{
1561 return ( (this == &a)
1562 || ( (deviceType == a.deviceType)
1563 && (fPassThrough == a.fPassThrough)
1564 && (lPort == a.lPort)
1565 && (lDevice == a.lDevice)
1566 && (uuid == a.uuid)
1567 && (strHostDriveSrc == a.strHostDriveSrc)
1568 )
1569 );
1570}
1571
1572/**
1573 * Comparison operator. This gets called from MachineConfigFile::operator==,
1574 * which in turn gets called from Machine::saveSettings to figure out whether
1575 * machine settings have really changed and thus need to be written out to disk.
1576 */
1577bool StorageController::operator==(const StorageController &s) const
1578{
1579 return ( (this == &s)
1580 || ( (strName == s.strName)
1581 && (storageBus == s.storageBus)
1582 && (controllerType == s.controllerType)
1583 && (ulPortCount == s.ulPortCount)
1584 && (ulInstance == s.ulInstance)
1585 && (fUseHostIOCache == s.fUseHostIOCache)
1586 && (lIDE0MasterEmulationPort == s.lIDE0MasterEmulationPort)
1587 && (lIDE0SlaveEmulationPort == s.lIDE0SlaveEmulationPort)
1588 && (lIDE1MasterEmulationPort == s.lIDE1MasterEmulationPort)
1589 && (lIDE1SlaveEmulationPort == s.lIDE1SlaveEmulationPort)
1590 && (llAttachedDevices == s.llAttachedDevices)
1591 )
1592 );
1593}
1594
1595/**
1596 * Comparison operator. This gets called from MachineConfigFile::operator==,
1597 * which in turn gets called from Machine::saveSettings to figure out whether
1598 * machine settings have really changed and thus need to be written out to disk.
1599 */
1600bool Storage::operator==(const Storage &s) const
1601{
1602 return ( (this == &s)
1603 || (llStorageControllers == s.llStorageControllers) // deep compare
1604 );
1605}
1606
1607/**
1608 * Comparison operator. This gets called from MachineConfigFile::operator==,
1609 * which in turn gets called from Machine::saveSettings to figure out whether
1610 * machine settings have really changed and thus need to be written out to disk.
1611 */
1612bool Snapshot::operator==(const Snapshot &s) const
1613{
1614 return ( (this == &s)
1615 || ( (uuid == s.uuid)
1616 && (strName == s.strName)
1617 && (strDescription == s.strDescription)
1618 && (RTTimeSpecIsEqual(&timestamp, &s.timestamp))
1619 && (strStateFile == s.strStateFile)
1620 && (hardware == s.hardware) // deep compare
1621 && (storage == s.storage) // deep compare
1622 && (llChildSnapshots == s.llChildSnapshots) // deep compare
1623 )
1624 );
1625}
1626
1627/**
1628 * IoSettings constructor.
1629 */
1630IoSettings::IoSettings()
1631{
1632 fIoCacheEnabled = true;
1633 ulIoCacheSize = 5;
1634 ulIoBandwidthMax = 0;
1635}
1636
1637////////////////////////////////////////////////////////////////////////////////
1638//
1639// MachineConfigFile
1640//
1641////////////////////////////////////////////////////////////////////////////////
1642
1643/**
1644 * Constructor.
1645 *
1646 * If pstrFilename is != NULL, this reads the given settings file into the member
1647 * variables and various substructures and lists. Otherwise, the member variables
1648 * are initialized with default values.
1649 *
1650 * Throws variants of xml::Error for I/O, XML and logical content errors, which
1651 * the caller should catch; if this constructor does not throw, then the member
1652 * variables contain meaningful values (either from the file or defaults).
1653 *
1654 * @param strFilename
1655 */
1656MachineConfigFile::MachineConfigFile(const Utf8Str *pstrFilename)
1657 : ConfigFileBase(pstrFilename),
1658 fNameSync(true),
1659 fTeleporterEnabled(false),
1660 uTeleporterPort(0),
1661 fRTCUseUTC(false),
1662 fCurrentStateModified(true),
1663 fAborted(false)
1664{
1665 RTTimeNow(&timeLastStateChange);
1666
1667 if (pstrFilename)
1668 {
1669 // the ConfigFileBase constructor has loaded the XML file, so now
1670 // we need only analyze what is in there
1671
1672 xml::NodesLoop nlRootChildren(*m->pelmRoot);
1673 const xml::ElementNode *pelmRootChild;
1674 while ((pelmRootChild = nlRootChildren.forAllNodes()))
1675 {
1676 if (pelmRootChild->nameEquals("Machine"))
1677 readMachine(*pelmRootChild);
1678 }
1679
1680 // clean up memory allocated by XML engine
1681 clearDocument();
1682 }
1683}
1684
1685/**
1686 * Public routine which allows for importing machine XML from an external DOM tree.
1687 * Use this after having called the constructor with a NULL argument.
1688 *
1689 * This is used by the OVF code if a <vbox:Machine> element has been encountered
1690 * in an OVF VirtualSystem element.
1691 *
1692 * @param elmMachine
1693 */
1694void MachineConfigFile::importMachineXML(const xml::ElementNode &elmMachine)
1695{
1696 readMachine(elmMachine);
1697}
1698
1699/**
1700 * Comparison operator. This gets called from Machine::saveSettings to figure out
1701 * whether machine settings have really changed and thus need to be written out to disk.
1702 *
1703 * Even though this is called operator==, this does NOT compare all fields; the "equals"
1704 * should be understood as "has the same machine config as". The following fields are
1705 * NOT compared:
1706 * -- settings versions and file names inherited from ConfigFileBase;
1707 * -- fCurrentStateModified because that is considered separately in Machine::saveSettings!!
1708 *
1709 * The "deep" comparisons marked below will invoke the operator== functions of the
1710 * structs defined in this file, which may in turn go into comparing lists of
1711 * other structures. As a result, invoking this can be expensive, but it's
1712 * less expensive than writing out XML to disk.
1713 */
1714bool MachineConfigFile::operator==(const MachineConfigFile &c) const
1715{
1716 return ( (this == &c)
1717 || ( (uuid == c.uuid)
1718 && (strName == c.strName)
1719 && (fNameSync == c.fNameSync)
1720 && (strDescription == c.strDescription)
1721 && (strOsType == c.strOsType)
1722 && (strStateFile == c.strStateFile)
1723 && (uuidCurrentSnapshot == c.uuidCurrentSnapshot)
1724 && (strSnapshotFolder == c.strSnapshotFolder)
1725 && (fTeleporterEnabled == c.fTeleporterEnabled)
1726 && (uTeleporterPort == c.uTeleporterPort)
1727 && (strTeleporterAddress == c.strTeleporterAddress)
1728 && (strTeleporterPassword == c.strTeleporterPassword)
1729 && (fRTCUseUTC == c.fRTCUseUTC)
1730 // skip fCurrentStateModified!
1731 && (RTTimeSpecIsEqual(&timeLastStateChange, &c.timeLastStateChange))
1732 && (fAborted == c.fAborted)
1733 && (hardwareMachine == c.hardwareMachine) // this one's deep
1734 && (storageMachine == c.storageMachine) // this one's deep
1735 && (mapExtraDataItems == c.mapExtraDataItems) // this one's deep
1736 && (llFirstSnapshot == c.llFirstSnapshot) // this one's deep
1737 )
1738 );
1739}
1740
1741/**
1742 * Called from MachineConfigFile::readHardware() to read cpu information.
1743 * @param elmCpuid
1744 * @param ll
1745 */
1746void MachineConfigFile::readCpuTree(const xml::ElementNode &elmCpu,
1747 CpuList &ll)
1748{
1749 xml::NodesLoop nl1(elmCpu, "Cpu");
1750 const xml::ElementNode *pelmCpu;
1751 while ((pelmCpu = nl1.forAllNodes()))
1752 {
1753 Cpu cpu;
1754
1755 if (!pelmCpu->getAttributeValue("id", cpu.ulId))
1756 throw ConfigFileError(this, pelmCpu, N_("Required Cpu/@id attribute is missing"));
1757
1758 ll.push_back(cpu);
1759 }
1760}
1761
1762/**
1763 * Called from MachineConfigFile::readHardware() to cpuid information.
1764 * @param elmCpuid
1765 * @param ll
1766 */
1767void MachineConfigFile::readCpuIdTree(const xml::ElementNode &elmCpuid,
1768 CpuIdLeafsList &ll)
1769{
1770 xml::NodesLoop nl1(elmCpuid, "CpuIdLeaf");
1771 const xml::ElementNode *pelmCpuIdLeaf;
1772 while ((pelmCpuIdLeaf = nl1.forAllNodes()))
1773 {
1774 CpuIdLeaf leaf;
1775
1776 if (!pelmCpuIdLeaf->getAttributeValue("id", leaf.ulId))
1777 throw ConfigFileError(this, pelmCpuIdLeaf, N_("Required CpuId/@id attribute is missing"));
1778
1779 pelmCpuIdLeaf->getAttributeValue("eax", leaf.ulEax);
1780 pelmCpuIdLeaf->getAttributeValue("ebx", leaf.ulEbx);
1781 pelmCpuIdLeaf->getAttributeValue("ecx", leaf.ulEcx);
1782 pelmCpuIdLeaf->getAttributeValue("edx", leaf.ulEdx);
1783
1784 ll.push_back(leaf);
1785 }
1786}
1787
1788/**
1789 * Called from MachineConfigFile::readHardware() to network information.
1790 * @param elmNetwork
1791 * @param ll
1792 */
1793void MachineConfigFile::readNetworkAdapters(const xml::ElementNode &elmNetwork,
1794 NetworkAdaptersList &ll)
1795{
1796 xml::NodesLoop nl1(elmNetwork, "Adapter");
1797 const xml::ElementNode *pelmAdapter;
1798 while ((pelmAdapter = nl1.forAllNodes()))
1799 {
1800 NetworkAdapter nic;
1801
1802 if (!pelmAdapter->getAttributeValue("slot", nic.ulSlot))
1803 throw ConfigFileError(this, pelmAdapter, N_("Required Adapter/@slot attribute is missing"));
1804
1805 Utf8Str strTemp;
1806 if (pelmAdapter->getAttributeValue("type", strTemp))
1807 {
1808 if (strTemp == "Am79C970A")
1809 nic.type = NetworkAdapterType_Am79C970A;
1810 else if (strTemp == "Am79C973")
1811 nic.type = NetworkAdapterType_Am79C973;
1812 else if (strTemp == "82540EM")
1813 nic.type = NetworkAdapterType_I82540EM;
1814 else if (strTemp == "82543GC")
1815 nic.type = NetworkAdapterType_I82543GC;
1816 else if (strTemp == "82545EM")
1817 nic.type = NetworkAdapterType_I82545EM;
1818 else if (strTemp == "virtio")
1819 nic.type = NetworkAdapterType_Virtio;
1820 else
1821 throw ConfigFileError(this, pelmAdapter, N_("Invalid value '%s' in Adapter/@type attribute"), strTemp.c_str());
1822 }
1823
1824 pelmAdapter->getAttributeValue("enabled", nic.fEnabled);
1825 pelmAdapter->getAttributeValue("MACAddress", nic.strMACAddress);
1826 pelmAdapter->getAttributeValue("cable", nic.fCableConnected);
1827 pelmAdapter->getAttributeValue("speed", nic.ulLineSpeed);
1828 pelmAdapter->getAttributeValue("trace", nic.fTraceEnabled);
1829 pelmAdapter->getAttributeValue("tracefile", nic.strTraceFile);
1830 pelmAdapter->getAttributeValue("bootPriority", nic.ulBootPriority);
1831
1832 xml::ElementNodesList llNetworkModes;
1833 pelmAdapter->getChildElements(llNetworkModes);
1834 xml::ElementNodesList::iterator it;
1835 /* We should have only active mode descriptor and disabled modes set */
1836 if (llNetworkModes.size() > 2)
1837 {
1838 throw ConfigFileError(this, pelmAdapter, N_("Invalid number of modes ('%d') attached to Adapter attribute"), llNetworkModes.size());
1839 }
1840 for (it = llNetworkModes.begin(); it != llNetworkModes.end(); ++it)
1841 {
1842 const xml::ElementNode *pelmNode = *it;
1843 if (pelmNode->nameEquals("DisabledModes"))
1844 {
1845 xml::ElementNodesList llDisabledNetworkModes;
1846 xml::ElementNodesList::iterator itDisabled;
1847 pelmNode->getChildElements(llDisabledNetworkModes);
1848 /* run over disabled list and load settings */
1849 for (itDisabled = llDisabledNetworkModes.begin();
1850 itDisabled != llDisabledNetworkModes.end(); ++itDisabled)
1851 {
1852 const xml::ElementNode *pelmDisabledNode = *itDisabled;
1853 readAttachedNetworkMode(*pelmDisabledNode, false, nic);
1854 }
1855 }
1856 else
1857 readAttachedNetworkMode(*pelmNode, true, nic);
1858 }
1859 // else: default is NetworkAttachmentType_Null
1860
1861 ll.push_back(nic);
1862 }
1863}
1864
1865void MachineConfigFile::readAttachedNetworkMode(const xml::ElementNode &elmMode, bool fEnabled, NetworkAdapter &nic)
1866{
1867 if (elmMode.nameEquals("NAT"))
1868 {
1869 if (fEnabled)
1870 nic.mode = NetworkAttachmentType_NAT;
1871
1872 nic.fHasDisabledNAT = (nic.mode != NetworkAttachmentType_NAT && !fEnabled);
1873 elmMode.getAttributeValue("network", nic.nat.strNetwork); // optional network name
1874 elmMode.getAttributeValue("hostip", nic.nat.strBindIP);
1875 elmMode.getAttributeValue("mtu", nic.nat.u32Mtu);
1876 elmMode.getAttributeValue("sockrcv", nic.nat.u32SockRcv);
1877 elmMode.getAttributeValue("socksnd", nic.nat.u32SockSnd);
1878 elmMode.getAttributeValue("tcprcv", nic.nat.u32TcpRcv);
1879 elmMode.getAttributeValue("tcpsnd", nic.nat.u32TcpSnd);
1880 const xml::ElementNode *pelmDNS;
1881 if ((pelmDNS = elmMode.findChildElement("DNS")))
1882 {
1883 pelmDNS->getAttributeValue("pass-domain", nic.nat.fDnsPassDomain);
1884 pelmDNS->getAttributeValue("use-proxy", nic.nat.fDnsProxy);
1885 pelmDNS->getAttributeValue("use-host-resolver", nic.nat.fDnsUseHostResolver);
1886 }
1887 const xml::ElementNode *pelmAlias;
1888 if ((pelmAlias = elmMode.findChildElement("Alias")))
1889 {
1890 pelmAlias->getAttributeValue("logging", nic.nat.fAliasLog);
1891 pelmAlias->getAttributeValue("proxy-only", nic.nat.fAliasProxyOnly);
1892 pelmAlias->getAttributeValue("use-same-ports", nic.nat.fAliasUseSamePorts);
1893 }
1894 const xml::ElementNode *pelmTFTP;
1895 if ((pelmTFTP = elmMode.findChildElement("TFTP")))
1896 {
1897 pelmTFTP->getAttributeValue("prefix", nic.nat.strTftpPrefix);
1898 pelmTFTP->getAttributeValue("boot-file", nic.nat.strTftpBootFile);
1899 pelmTFTP->getAttributeValue("next-server", nic.nat.strTftpNextServer);
1900 }
1901 xml::ElementNodesList plstNatPF;
1902 elmMode.getChildElements(plstNatPF, "Forwarding");
1903 for (xml::ElementNodesList::iterator pf = plstNatPF.begin(); pf != plstNatPF.end(); ++pf)
1904 {
1905 NATRule rule;
1906 uint32_t port = 0;
1907 (*pf)->getAttributeValue("name", rule.strName);
1908 (*pf)->getAttributeValue("proto", rule.u32Proto);
1909 (*pf)->getAttributeValue("hostip", rule.strHostIP);
1910 (*pf)->getAttributeValue("hostport", port);
1911 rule.u16HostPort = port;
1912 (*pf)->getAttributeValue("guestip", rule.strGuestIP);
1913 (*pf)->getAttributeValue("guestport", port);
1914 rule.u16GuestPort = port;
1915 nic.nat.llRules.push_back(rule);
1916 }
1917 }
1918 else if ( fEnabled
1919 && ( (elmMode.nameEquals("HostInterface"))
1920 || (elmMode.nameEquals("BridgedInterface")))
1921 )
1922 {
1923 nic.mode = NetworkAttachmentType_Bridged;
1924 elmMode.getAttributeValue("name", nic.strName); // optional host interface name
1925 }
1926 else if ( fEnabled
1927 && elmMode.nameEquals("InternalNetwork"))
1928 {
1929 nic.mode = NetworkAttachmentType_Internal;
1930 if (!elmMode.getAttributeValue("name", nic.strName)) // required network name
1931 throw ConfigFileError(this, &elmMode, N_("Required InternalNetwork/@name element is missing"));
1932 }
1933 else if ( fEnabled
1934 && elmMode.nameEquals("HostOnlyInterface"))
1935 {
1936 nic.mode = NetworkAttachmentType_HostOnly;
1937 if (!elmMode.getAttributeValue("name", nic.strName)) // required network name
1938 throw ConfigFileError(this, &elmMode, N_("Required HostOnlyInterface/@name element is missing"));
1939 }
1940#if defined(VBOX_WITH_VDE)
1941 else if ( fEnabled
1942 && elmMode.nameEquals("VDE"))
1943 {
1944 nic.mode = NetworkAttachmentType_VDE;
1945 elmMode.getAttributeValue("network", nic.strName); // optional network name
1946 }
1947#endif
1948}
1949
1950/**
1951 * Called from MachineConfigFile::readHardware() to read serial port information.
1952 * @param elmUART
1953 * @param ll
1954 */
1955void MachineConfigFile::readSerialPorts(const xml::ElementNode &elmUART,
1956 SerialPortsList &ll)
1957{
1958 xml::NodesLoop nl1(elmUART, "Port");
1959 const xml::ElementNode *pelmPort;
1960 while ((pelmPort = nl1.forAllNodes()))
1961 {
1962 SerialPort port;
1963 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
1964 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@slot attribute is missing"));
1965
1966 // slot must be unique
1967 for (SerialPortsList::const_iterator it = ll.begin();
1968 it != ll.end();
1969 ++it)
1970 if ((*it).ulSlot == port.ulSlot)
1971 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in UART/Port/@slot attribute: value is not unique"), port.ulSlot);
1972
1973 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
1974 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@enabled attribute is missing"));
1975 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
1976 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IOBase attribute is missing"));
1977 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
1978 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@IRQ attribute is missing"));
1979
1980 Utf8Str strPortMode;
1981 if (!pelmPort->getAttributeValue("hostMode", strPortMode))
1982 throw ConfigFileError(this, pelmPort, N_("Required UART/Port/@hostMode attribute is missing"));
1983 if (strPortMode == "RawFile")
1984 port.portMode = PortMode_RawFile;
1985 else if (strPortMode == "HostPipe")
1986 port.portMode = PortMode_HostPipe;
1987 else if (strPortMode == "HostDevice")
1988 port.portMode = PortMode_HostDevice;
1989 else if (strPortMode == "Disconnected")
1990 port.portMode = PortMode_Disconnected;
1991 else
1992 throw ConfigFileError(this, pelmPort, N_("Invalid value '%s' in UART/Port/@hostMode attribute"), strPortMode.c_str());
1993
1994 pelmPort->getAttributeValue("path", port.strPath);
1995 pelmPort->getAttributeValue("server", port.fServer);
1996
1997 ll.push_back(port);
1998 }
1999}
2000
2001/**
2002 * Called from MachineConfigFile::readHardware() to read parallel port information.
2003 * @param elmLPT
2004 * @param ll
2005 */
2006void MachineConfigFile::readParallelPorts(const xml::ElementNode &elmLPT,
2007 ParallelPortsList &ll)
2008{
2009 xml::NodesLoop nl1(elmLPT, "Port");
2010 const xml::ElementNode *pelmPort;
2011 while ((pelmPort = nl1.forAllNodes()))
2012 {
2013 ParallelPort port;
2014 if (!pelmPort->getAttributeValue("slot", port.ulSlot))
2015 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@slot attribute is missing"));
2016
2017 // slot must be unique
2018 for (ParallelPortsList::const_iterator it = ll.begin();
2019 it != ll.end();
2020 ++it)
2021 if ((*it).ulSlot == port.ulSlot)
2022 throw ConfigFileError(this, pelmPort, N_("Invalid value %RU32 in LPT/Port/@slot attribute: value is not unique"), port.ulSlot);
2023
2024 if (!pelmPort->getAttributeValue("enabled", port.fEnabled))
2025 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@enabled attribute is missing"));
2026 if (!pelmPort->getAttributeValue("IOBase", port.ulIOBase))
2027 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IOBase attribute is missing"));
2028 if (!pelmPort->getAttributeValue("IRQ", port.ulIRQ))
2029 throw ConfigFileError(this, pelmPort, N_("Required LPT/Port/@IRQ attribute is missing"));
2030
2031 pelmPort->getAttributeValue("path", port.strPath);
2032
2033 ll.push_back(port);
2034 }
2035}
2036
2037/**
2038 * Called from MachineConfigFile::readHardware() to read audio adapter information
2039 * and maybe fix driver information depending on the current host hardware.
2040 *
2041 * @param elmAudioAdapter "AudioAdapter" XML element.
2042 * @param hw
2043 */
2044void MachineConfigFile::readAudioAdapter(const xml::ElementNode &elmAudioAdapter,
2045 AudioAdapter &aa)
2046{
2047 elmAudioAdapter.getAttributeValue("enabled", aa.fEnabled);
2048
2049 Utf8Str strTemp;
2050 if (elmAudioAdapter.getAttributeValue("controller", strTemp))
2051 {
2052 if (strTemp == "SB16")
2053 aa.controllerType = AudioControllerType_SB16;
2054 else if (strTemp == "AC97")
2055 aa.controllerType = AudioControllerType_AC97;
2056 else if (strTemp == "HDA")
2057 aa.controllerType = AudioControllerType_HDA;
2058 else
2059 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@controller attribute"), strTemp.c_str());
2060 }
2061
2062 if (elmAudioAdapter.getAttributeValue("driver", strTemp))
2063 {
2064 // settings before 1.3 used lower case so make sure this is case-insensitive
2065 strTemp.toUpper();
2066 if (strTemp == "NULL")
2067 aa.driverType = AudioDriverType_Null;
2068 else if (strTemp == "WINMM")
2069 aa.driverType = AudioDriverType_WinMM;
2070 else if ( (strTemp == "DIRECTSOUND") || (strTemp == "DSOUND") )
2071 aa.driverType = AudioDriverType_DirectSound;
2072 else if (strTemp == "SOLAUDIO")
2073 aa.driverType = AudioDriverType_SolAudio;
2074 else if (strTemp == "ALSA")
2075 aa.driverType = AudioDriverType_ALSA;
2076 else if (strTemp == "PULSE")
2077 aa.driverType = AudioDriverType_Pulse;
2078 else if (strTemp == "OSS")
2079 aa.driverType = AudioDriverType_OSS;
2080 else if (strTemp == "COREAUDIO")
2081 aa.driverType = AudioDriverType_CoreAudio;
2082 else if (strTemp == "MMPM")
2083 aa.driverType = AudioDriverType_MMPM;
2084 else
2085 throw ConfigFileError(this, &elmAudioAdapter, N_("Invalid value '%s' in AudioAdapter/@driver attribute"), strTemp.c_str());
2086
2087 // now check if this is actually supported on the current host platform;
2088 // people might be opening a file created on a Windows host, and that
2089 // VM should still start on a Linux host
2090 if (!isAudioDriverAllowedOnThisHost(aa.driverType))
2091 aa.driverType = getHostDefaultAudioDriver();
2092 }
2093}
2094
2095/**
2096 * Called from MachineConfigFile::readHardware() to read guest property information.
2097 * @param elmGuestProperties
2098 * @param hw
2099 */
2100void MachineConfigFile::readGuestProperties(const xml::ElementNode &elmGuestProperties,
2101 Hardware &hw)
2102{
2103 xml::NodesLoop nl1(elmGuestProperties, "GuestProperty");
2104 const xml::ElementNode *pelmProp;
2105 while ((pelmProp = nl1.forAllNodes()))
2106 {
2107 GuestProperty prop;
2108 pelmProp->getAttributeValue("name", prop.strName);
2109 pelmProp->getAttributeValue("value", prop.strValue);
2110
2111 pelmProp->getAttributeValue("timestamp", prop.timestamp);
2112 pelmProp->getAttributeValue("flags", prop.strFlags);
2113 hw.llGuestProperties.push_back(prop);
2114 }
2115
2116 elmGuestProperties.getAttributeValue("notificationPatterns", hw.strNotificationPatterns);
2117}
2118
2119/**
2120 * Helper function to read attributes that are common to <SATAController> (pre-1.7)
2121 * and <StorageController>.
2122 * @param elmStorageController
2123 * @param strg
2124 */
2125void MachineConfigFile::readStorageControllerAttributes(const xml::ElementNode &elmStorageController,
2126 StorageController &sctl)
2127{
2128 elmStorageController.getAttributeValue("PortCount", sctl.ulPortCount);
2129 elmStorageController.getAttributeValue("IDE0MasterEmulationPort", sctl.lIDE0MasterEmulationPort);
2130 elmStorageController.getAttributeValue("IDE0SlaveEmulationPort", sctl.lIDE0SlaveEmulationPort);
2131 elmStorageController.getAttributeValue("IDE1MasterEmulationPort", sctl.lIDE1MasterEmulationPort);
2132 elmStorageController.getAttributeValue("IDE1SlaveEmulationPort", sctl.lIDE1SlaveEmulationPort);
2133
2134 elmStorageController.getAttributeValue("useHostIOCache", sctl.fUseHostIOCache);
2135}
2136
2137/**
2138 * Reads in a <Hardware> block and stores it in the given structure. Used
2139 * both directly from readMachine and from readSnapshot, since snapshots
2140 * have their own hardware sections.
2141 *
2142 * For legacy pre-1.7 settings we also need a storage structure because
2143 * the IDE and SATA controllers used to be defined under <Hardware>.
2144 *
2145 * @param elmHardware
2146 * @param hw
2147 */
2148void MachineConfigFile::readHardware(const xml::ElementNode &elmHardware,
2149 Hardware &hw,
2150 Storage &strg)
2151{
2152 if (!elmHardware.getAttributeValue("version", hw.strVersion))
2153 {
2154 /* KLUDGE ALERT! For a while during the 3.1 development this was not
2155 written because it was thought to have a default value of "2". For
2156 sv <= 1.3 it defaults to "1" because the attribute didn't exist,
2157 while for 1.4+ it is sort of mandatory. Now, the buggy XML writer
2158 code only wrote 1.7 and later. So, if it's a 1.7+ XML file and it's
2159 missing the hardware version, then it probably should be "2" instead
2160 of "1". */
2161 if (m->sv < SettingsVersion_v1_7)
2162 hw.strVersion = "1";
2163 else
2164 hw.strVersion = "2";
2165 }
2166 Utf8Str strUUID;
2167 if (elmHardware.getAttributeValue("uuid", strUUID))
2168 parseUUID(hw.uuid, strUUID);
2169
2170 xml::NodesLoop nl1(elmHardware);
2171 const xml::ElementNode *pelmHwChild;
2172 while ((pelmHwChild = nl1.forAllNodes()))
2173 {
2174 if (pelmHwChild->nameEquals("CPU"))
2175 {
2176 if (!pelmHwChild->getAttributeValue("count", hw.cCPUs))
2177 {
2178 // pre-1.5 variant; not sure if this actually exists in the wild anywhere
2179 const xml::ElementNode *pelmCPUChild;
2180 if ((pelmCPUChild = pelmHwChild->findChildElement("CPUCount")))
2181 pelmCPUChild->getAttributeValue("count", hw.cCPUs);
2182 }
2183
2184 pelmHwChild->getAttributeValue("hotplug", hw.fCpuHotPlug);
2185
2186 const xml::ElementNode *pelmCPUChild;
2187 if (hw.fCpuHotPlug)
2188 {
2189 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuTree")))
2190 readCpuTree(*pelmCPUChild, hw.llCpus);
2191 }
2192
2193 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtEx")))
2194 {
2195 pelmCPUChild->getAttributeValue("enabled", hw.fHardwareVirt);
2196 pelmCPUChild->getAttributeValue("exclusive", hw.fHardwareVirtExclusive); // settings version 1.9
2197 }
2198 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExNestedPaging")))
2199 pelmCPUChild->getAttributeValue("enabled", hw.fNestedPaging);
2200 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExLargePages")))
2201 pelmCPUChild->getAttributeValue("enabled", hw.fLargePages);
2202 if ((pelmCPUChild = pelmHwChild->findChildElement("HardwareVirtExVPID")))
2203 pelmCPUChild->getAttributeValue("enabled", hw.fVPID);
2204
2205 if (!(pelmCPUChild = pelmHwChild->findChildElement("PAE")))
2206 {
2207 /* The default for pre 3.1 was false, so we must respect that. */
2208 if (m->sv < SettingsVersion_v1_9)
2209 hw.fPAE = false;
2210 }
2211 else
2212 pelmCPUChild->getAttributeValue("enabled", hw.fPAE);
2213
2214 if ((pelmCPUChild = pelmHwChild->findChildElement("SyntheticCpu")))
2215 pelmCPUChild->getAttributeValue("enabled", hw.fSyntheticCpu);
2216 if ((pelmCPUChild = pelmHwChild->findChildElement("CpuIdTree")))
2217 readCpuIdTree(*pelmCPUChild, hw.llCpuIdLeafs);
2218 }
2219 else if (pelmHwChild->nameEquals("Memory"))
2220 {
2221 pelmHwChild->getAttributeValue("RAMSize", hw.ulMemorySizeMB);
2222 pelmHwChild->getAttributeValue("PageFusion", hw.fPageFusionEnabled);
2223 }
2224 else if (pelmHwChild->nameEquals("Firmware"))
2225 {
2226 Utf8Str strFirmwareType;
2227 if (pelmHwChild->getAttributeValue("type", strFirmwareType))
2228 {
2229 if ( (strFirmwareType == "BIOS")
2230 || (strFirmwareType == "1") // some trunk builds used the number here
2231 )
2232 hw.firmwareType = FirmwareType_BIOS;
2233 else if ( (strFirmwareType == "EFI")
2234 || (strFirmwareType == "2") // some trunk builds used the number here
2235 )
2236 hw.firmwareType = FirmwareType_EFI;
2237 else if ( strFirmwareType == "EFI32")
2238 hw.firmwareType = FirmwareType_EFI32;
2239 else if ( strFirmwareType == "EFI64")
2240 hw.firmwareType = FirmwareType_EFI64;
2241 else if ( strFirmwareType == "EFIDUAL")
2242 hw.firmwareType = FirmwareType_EFIDUAL;
2243 else
2244 throw ConfigFileError(this,
2245 pelmHwChild,
2246 N_("Invalid value '%s' in Firmware/@type"),
2247 strFirmwareType.c_str());
2248 }
2249 }
2250 else if (pelmHwChild->nameEquals("HID"))
2251 {
2252 Utf8Str strHidType;
2253 if (pelmHwChild->getAttributeValue("Keyboard", strHidType))
2254 {
2255 if (strHidType == "None")
2256 hw.keyboardHidType = KeyboardHidType_None;
2257 else if (strHidType == "USBKeyboard")
2258 hw.keyboardHidType = KeyboardHidType_USBKeyboard;
2259 else if (strHidType == "PS2Keyboard")
2260 hw.keyboardHidType = KeyboardHidType_PS2Keyboard;
2261 else if (strHidType == "ComboKeyboard")
2262 hw.keyboardHidType = KeyboardHidType_ComboKeyboard;
2263 else
2264 throw ConfigFileError(this,
2265 pelmHwChild,
2266 N_("Invalid value '%s' in HID/Keyboard/@type"),
2267 strHidType.c_str());
2268 }
2269 if (pelmHwChild->getAttributeValue("Pointing", strHidType))
2270 {
2271 if (strHidType == "None")
2272 hw.pointingHidType = PointingHidType_None;
2273 else if (strHidType == "USBMouse")
2274 hw.pointingHidType = PointingHidType_USBMouse;
2275 else if (strHidType == "USBTablet")
2276 hw.pointingHidType = PointingHidType_USBTablet;
2277 else if (strHidType == "PS2Mouse")
2278 hw.pointingHidType = PointingHidType_PS2Mouse;
2279 else if (strHidType == "ComboMouse")
2280 hw.pointingHidType = PointingHidType_ComboMouse;
2281 else
2282 throw ConfigFileError(this,
2283 pelmHwChild,
2284 N_("Invalid value '%s' in HID/Pointing/@type"),
2285 strHidType.c_str());
2286 }
2287 }
2288 else if (pelmHwChild->nameEquals("HPET"))
2289 {
2290 pelmHwChild->getAttributeValue("enabled", hw.fHpetEnabled);
2291 }
2292 else if (pelmHwChild->nameEquals("Boot"))
2293 {
2294 hw.mapBootOrder.clear();
2295
2296 xml::NodesLoop nl2(*pelmHwChild, "Order");
2297 const xml::ElementNode *pelmOrder;
2298 while ((pelmOrder = nl2.forAllNodes()))
2299 {
2300 uint32_t ulPos;
2301 Utf8Str strDevice;
2302 if (!pelmOrder->getAttributeValue("position", ulPos))
2303 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@position attribute is missing"));
2304
2305 if ( ulPos < 1
2306 || ulPos > SchemaDefs::MaxBootPosition
2307 )
2308 throw ConfigFileError(this,
2309 pelmOrder,
2310 N_("Invalid value '%RU32' in Boot/Order/@position: must be greater than 0 and less than %RU32"),
2311 ulPos,
2312 SchemaDefs::MaxBootPosition + 1);
2313 // XML is 1-based but internal data is 0-based
2314 --ulPos;
2315
2316 if (hw.mapBootOrder.find(ulPos) != hw.mapBootOrder.end())
2317 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%RU32' in Boot/Order/@position: value is not unique"), ulPos);
2318
2319 if (!pelmOrder->getAttributeValue("device", strDevice))
2320 throw ConfigFileError(this, pelmOrder, N_("Required Boot/Order/@device attribute is missing"));
2321
2322 DeviceType_T type;
2323 if (strDevice == "None")
2324 type = DeviceType_Null;
2325 else if (strDevice == "Floppy")
2326 type = DeviceType_Floppy;
2327 else if (strDevice == "DVD")
2328 type = DeviceType_DVD;
2329 else if (strDevice == "HardDisk")
2330 type = DeviceType_HardDisk;
2331 else if (strDevice == "Network")
2332 type = DeviceType_Network;
2333 else
2334 throw ConfigFileError(this, pelmOrder, N_("Invalid value '%s' in Boot/Order/@device attribute"), strDevice.c_str());
2335 hw.mapBootOrder[ulPos] = type;
2336 }
2337 }
2338 else if (pelmHwChild->nameEquals("Display"))
2339 {
2340 pelmHwChild->getAttributeValue("VRAMSize", hw.ulVRAMSizeMB);
2341 if (!pelmHwChild->getAttributeValue("monitorCount", hw.cMonitors))
2342 pelmHwChild->getAttributeValue("MonitorCount", hw.cMonitors); // pre-v1.5 variant
2343 if (!pelmHwChild->getAttributeValue("accelerate3D", hw.fAccelerate3D))
2344 pelmHwChild->getAttributeValue("Accelerate3D", hw.fAccelerate3D); // pre-v1.5 variant
2345 pelmHwChild->getAttributeValue("accelerate2DVideo", hw.fAccelerate2DVideo);
2346 }
2347 else if (pelmHwChild->nameEquals("RemoteDisplay"))
2348 {
2349 pelmHwChild->getAttributeValue("enabled", hw.vrdpSettings.fEnabled);
2350 pelmHwChild->getAttributeValue("port", hw.vrdpSettings.strPort);
2351 pelmHwChild->getAttributeValue("netAddress", hw.vrdpSettings.strNetAddress);
2352
2353 Utf8Str strAuthType;
2354 if (pelmHwChild->getAttributeValue("authType", strAuthType))
2355 {
2356 // settings before 1.3 used lower case so make sure this is case-insensitive
2357 strAuthType.toUpper();
2358 if (strAuthType == "NULL")
2359 hw.vrdpSettings.authType = VRDPAuthType_Null;
2360 else if (strAuthType == "GUEST")
2361 hw.vrdpSettings.authType = VRDPAuthType_Guest;
2362 else if (strAuthType == "EXTERNAL")
2363 hw.vrdpSettings.authType = VRDPAuthType_External;
2364 else
2365 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in RemoteDisplay/@authType attribute"), strAuthType.c_str());
2366 }
2367
2368 pelmHwChild->getAttributeValue("authTimeout", hw.vrdpSettings.ulAuthTimeout);
2369 pelmHwChild->getAttributeValue("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
2370 pelmHwChild->getAttributeValue("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
2371
2372 const xml::ElementNode *pelmVideoChannel;
2373 if ((pelmVideoChannel = pelmHwChild->findChildElement("VideoChannel")))
2374 {
2375 pelmVideoChannel->getAttributeValue("enabled", hw.vrdpSettings.fVideoChannel);
2376 pelmVideoChannel->getAttributeValue("quality", hw.vrdpSettings.ulVideoChannelQuality);
2377 hw.vrdpSettings.ulVideoChannelQuality = RT_CLAMP(hw.vrdpSettings.ulVideoChannelQuality, 10, 100);
2378 }
2379 }
2380 else if (pelmHwChild->nameEquals("BIOS"))
2381 {
2382 const xml::ElementNode *pelmBIOSChild;
2383 if ((pelmBIOSChild = pelmHwChild->findChildElement("ACPI")))
2384 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fACPIEnabled);
2385 if ((pelmBIOSChild = pelmHwChild->findChildElement("IOAPIC")))
2386 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fIOAPICEnabled);
2387 if ((pelmBIOSChild = pelmHwChild->findChildElement("Logo")))
2388 {
2389 pelmBIOSChild->getAttributeValue("fadeIn", hw.biosSettings.fLogoFadeIn);
2390 pelmBIOSChild->getAttributeValue("fadeOut", hw.biosSettings.fLogoFadeOut);
2391 pelmBIOSChild->getAttributeValue("displayTime", hw.biosSettings.ulLogoDisplayTime);
2392 pelmBIOSChild->getAttributeValue("imagePath", hw.biosSettings.strLogoImagePath);
2393 }
2394 if ((pelmBIOSChild = pelmHwChild->findChildElement("BootMenu")))
2395 {
2396 Utf8Str strBootMenuMode;
2397 if (pelmBIOSChild->getAttributeValue("mode", strBootMenuMode))
2398 {
2399 // settings before 1.3 used lower case so make sure this is case-insensitive
2400 strBootMenuMode.toUpper();
2401 if (strBootMenuMode == "DISABLED")
2402 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_Disabled;
2403 else if (strBootMenuMode == "MENUONLY")
2404 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MenuOnly;
2405 else if (strBootMenuMode == "MESSAGEANDMENU")
2406 hw.biosSettings.biosBootMenuMode = BIOSBootMenuMode_MessageAndMenu;
2407 else
2408 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' in BootMenu/@mode attribute"), strBootMenuMode.c_str());
2409 }
2410 }
2411 if ((pelmBIOSChild = pelmHwChild->findChildElement("PXEDebug")))
2412 pelmBIOSChild->getAttributeValue("enabled", hw.biosSettings.fPXEDebugEnabled);
2413 if ((pelmBIOSChild = pelmHwChild->findChildElement("TimeOffset")))
2414 pelmBIOSChild->getAttributeValue("value", hw.biosSettings.llTimeOffset);
2415
2416 // legacy BIOS/IDEController (pre 1.7)
2417 if ( (m->sv < SettingsVersion_v1_7)
2418 && ((pelmBIOSChild = pelmHwChild->findChildElement("IDEController")))
2419 )
2420 {
2421 StorageController sctl;
2422 sctl.strName = "IDE Controller";
2423 sctl.storageBus = StorageBus_IDE;
2424
2425 Utf8Str strType;
2426 if (pelmBIOSChild->getAttributeValue("type", strType))
2427 {
2428 if (strType == "PIIX3")
2429 sctl.controllerType = StorageControllerType_PIIX3;
2430 else if (strType == "PIIX4")
2431 sctl.controllerType = StorageControllerType_PIIX4;
2432 else if (strType == "ICH6")
2433 sctl.controllerType = StorageControllerType_ICH6;
2434 else
2435 throw ConfigFileError(this, pelmBIOSChild, N_("Invalid value '%s' for IDEController/@type attribute"), strType.c_str());
2436 }
2437 sctl.ulPortCount = 2;
2438 strg.llStorageControllers.push_back(sctl);
2439 }
2440 }
2441 else if (pelmHwChild->nameEquals("USBController"))
2442 {
2443 pelmHwChild->getAttributeValue("enabled", hw.usbController.fEnabled);
2444 pelmHwChild->getAttributeValue("enabledEhci", hw.usbController.fEnabledEHCI);
2445
2446 readUSBDeviceFilters(*pelmHwChild,
2447 hw.usbController.llDeviceFilters);
2448 }
2449 else if ( (m->sv < SettingsVersion_v1_7)
2450 && (pelmHwChild->nameEquals("SATAController"))
2451 )
2452 {
2453 bool f;
2454 if ( (pelmHwChild->getAttributeValue("enabled", f))
2455 && (f)
2456 )
2457 {
2458 StorageController sctl;
2459 sctl.strName = "SATA Controller";
2460 sctl.storageBus = StorageBus_SATA;
2461 sctl.controllerType = StorageControllerType_IntelAhci;
2462
2463 readStorageControllerAttributes(*pelmHwChild, sctl);
2464
2465 strg.llStorageControllers.push_back(sctl);
2466 }
2467 }
2468 else if (pelmHwChild->nameEquals("Network"))
2469 readNetworkAdapters(*pelmHwChild, hw.llNetworkAdapters);
2470 else if (pelmHwChild->nameEquals("RTC"))
2471 {
2472 Utf8Str strLocalOrUTC;
2473 fRTCUseUTC = pelmHwChild->getAttributeValue("localOrUTC", strLocalOrUTC)
2474 && strLocalOrUTC == "UTC";
2475 }
2476 else if ( (pelmHwChild->nameEquals("UART"))
2477 || (pelmHwChild->nameEquals("Uart")) // used before 1.3
2478 )
2479 readSerialPorts(*pelmHwChild, hw.llSerialPorts);
2480 else if ( (pelmHwChild->nameEquals("LPT"))
2481 || (pelmHwChild->nameEquals("Lpt")) // used before 1.3
2482 )
2483 readParallelPorts(*pelmHwChild, hw.llParallelPorts);
2484 else if (pelmHwChild->nameEquals("AudioAdapter"))
2485 readAudioAdapter(*pelmHwChild, hw.audioAdapter);
2486 else if (pelmHwChild->nameEquals("SharedFolders"))
2487 {
2488 xml::NodesLoop nl2(*pelmHwChild, "SharedFolder");
2489 const xml::ElementNode *pelmFolder;
2490 while ((pelmFolder = nl2.forAllNodes()))
2491 {
2492 SharedFolder sf;
2493 pelmFolder->getAttributeValue("name", sf.strName);
2494 pelmFolder->getAttributeValue("hostPath", sf.strHostPath);
2495 pelmFolder->getAttributeValue("writable", sf.fWritable);
2496 pelmFolder->getAttributeValue("autoMount", sf.fAutoMount);
2497 hw.llSharedFolders.push_back(sf);
2498 }
2499 }
2500 else if (pelmHwChild->nameEquals("Clipboard"))
2501 {
2502 Utf8Str strTemp;
2503 if (pelmHwChild->getAttributeValue("mode", strTemp))
2504 {
2505 if (strTemp == "Disabled")
2506 hw.clipboardMode = ClipboardMode_Disabled;
2507 else if (strTemp == "HostToGuest")
2508 hw.clipboardMode = ClipboardMode_HostToGuest;
2509 else if (strTemp == "GuestToHost")
2510 hw.clipboardMode = ClipboardMode_GuestToHost;
2511 else if (strTemp == "Bidirectional")
2512 hw.clipboardMode = ClipboardMode_Bidirectional;
2513 else
2514 throw ConfigFileError(this, pelmHwChild, N_("Invalid value '%s' in Clipboard/@mode attribute"), strTemp.c_str());
2515 }
2516 }
2517 else if (pelmHwChild->nameEquals("Guest"))
2518 {
2519 if (!pelmHwChild->getAttributeValue("memoryBalloonSize", hw.ulMemoryBalloonSize))
2520 pelmHwChild->getAttributeValue("MemoryBalloonSize", hw.ulMemoryBalloonSize); // used before 1.3
2521 }
2522 else if (pelmHwChild->nameEquals("GuestProperties"))
2523 readGuestProperties(*pelmHwChild, hw);
2524 else if (pelmHwChild->nameEquals("IO"))
2525 {
2526 const xml::ElementNode *pelmIoChild;
2527
2528 if ((pelmIoChild = pelmHwChild->findChildElement("IoCache")))
2529 {
2530 pelmIoChild->getAttributeValue("enabled", hw.ioSettings.fIoCacheEnabled);
2531 pelmIoChild->getAttributeValue("size", hw.ioSettings.ulIoCacheSize);
2532 }
2533 if ((pelmIoChild = pelmHwChild->findChildElement("IoBandwidth")))
2534 {
2535 pelmIoChild->getAttributeValue("max", hw.ioSettings.ulIoBandwidthMax);
2536 }
2537 }
2538 }
2539
2540 if (hw.ulMemorySizeMB == (uint32_t)-1)
2541 throw ConfigFileError(this, &elmHardware, N_("Required Memory/@RAMSize element/attribute is missing"));
2542}
2543
2544/**
2545 * This gets called instead of readStorageControllers() for legacy pre-1.7 settings
2546 * files which have a <HardDiskAttachments> node and storage controller settings
2547 * hidden in the <Hardware> settings. We set the StorageControllers fields just the
2548 * same, just from different sources.
2549 * @param elmHardware <Hardware> XML node.
2550 * @param elmHardDiskAttachments <HardDiskAttachments> XML node.
2551 * @param strg
2552 */
2553void MachineConfigFile::readHardDiskAttachments_pre1_7(const xml::ElementNode &elmHardDiskAttachments,
2554 Storage &strg)
2555{
2556 StorageController *pIDEController = NULL;
2557 StorageController *pSATAController = NULL;
2558
2559 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
2560 it != strg.llStorageControllers.end();
2561 ++it)
2562 {
2563 StorageController &s = *it;
2564 if (s.storageBus == StorageBus_IDE)
2565 pIDEController = &s;
2566 else if (s.storageBus == StorageBus_SATA)
2567 pSATAController = &s;
2568 }
2569
2570 xml::NodesLoop nl1(elmHardDiskAttachments, "HardDiskAttachment");
2571 const xml::ElementNode *pelmAttachment;
2572 while ((pelmAttachment = nl1.forAllNodes()))
2573 {
2574 AttachedDevice att;
2575 Utf8Str strUUID, strBus;
2576
2577 if (!pelmAttachment->getAttributeValue("hardDisk", strUUID))
2578 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@hardDisk attribute is missing"));
2579 parseUUID(att.uuid, strUUID);
2580
2581 if (!pelmAttachment->getAttributeValue("bus", strBus))
2582 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@bus attribute is missing"));
2583 // pre-1.7 'channel' is now port
2584 if (!pelmAttachment->getAttributeValue("channel", att.lPort))
2585 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@channel attribute is missing"));
2586 // pre-1.7 'device' is still device
2587 if (!pelmAttachment->getAttributeValue("device", att.lDevice))
2588 throw ConfigFileError(this, pelmAttachment, N_("Required HardDiskAttachment/@device attribute is missing"));
2589
2590 att.deviceType = DeviceType_HardDisk;
2591
2592 if (strBus == "IDE")
2593 {
2594 if (!pIDEController)
2595 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'IDE' but cannot find IDE controller"));
2596 pIDEController->llAttachedDevices.push_back(att);
2597 }
2598 else if (strBus == "SATA")
2599 {
2600 if (!pSATAController)
2601 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus is 'SATA' but cannot find SATA controller"));
2602 pSATAController->llAttachedDevices.push_back(att);
2603 }
2604 else
2605 throw ConfigFileError(this, pelmAttachment, N_("HardDiskAttachment/@bus attribute has illegal value '%s'"), strBus.c_str());
2606 }
2607}
2608
2609/**
2610 * Reads in a <StorageControllers> block and stores it in the given Storage structure.
2611 * Used both directly from readMachine and from readSnapshot, since snapshots
2612 * have their own storage controllers sections.
2613 *
2614 * This is only called for settings version 1.7 and above; see readHardDiskAttachments_pre1_7()
2615 * for earlier versions.
2616 *
2617 * @param elmStorageControllers
2618 */
2619void MachineConfigFile::readStorageControllers(const xml::ElementNode &elmStorageControllers,
2620 Storage &strg)
2621{
2622 xml::NodesLoop nlStorageControllers(elmStorageControllers, "StorageController");
2623 const xml::ElementNode *pelmController;
2624 while ((pelmController = nlStorageControllers.forAllNodes()))
2625 {
2626 StorageController sctl;
2627
2628 if (!pelmController->getAttributeValue("name", sctl.strName))
2629 throw ConfigFileError(this, pelmController, N_("Required StorageController/@name attribute is missing"));
2630 // canonicalize storage controller names for configs in the switchover
2631 // period.
2632 if (m->sv < SettingsVersion_v1_9)
2633 {
2634 if (sctl.strName == "IDE")
2635 sctl.strName = "IDE Controller";
2636 else if (sctl.strName == "SATA")
2637 sctl.strName = "SATA Controller";
2638 else if (sctl.strName == "SCSI")
2639 sctl.strName = "SCSI Controller";
2640 }
2641
2642 pelmController->getAttributeValue("Instance", sctl.ulInstance);
2643 // default from constructor is 0
2644
2645 Utf8Str strType;
2646 if (!pelmController->getAttributeValue("type", strType))
2647 throw ConfigFileError(this, pelmController, N_("Required StorageController/@type attribute is missing"));
2648
2649 if (strType == "AHCI")
2650 {
2651 sctl.storageBus = StorageBus_SATA;
2652 sctl.controllerType = StorageControllerType_IntelAhci;
2653 }
2654 else if (strType == "LsiLogic")
2655 {
2656 sctl.storageBus = StorageBus_SCSI;
2657 sctl.controllerType = StorageControllerType_LsiLogic;
2658 }
2659 else if (strType == "BusLogic")
2660 {
2661 sctl.storageBus = StorageBus_SCSI;
2662 sctl.controllerType = StorageControllerType_BusLogic;
2663 }
2664 else if (strType == "PIIX3")
2665 {
2666 sctl.storageBus = StorageBus_IDE;
2667 sctl.controllerType = StorageControllerType_PIIX3;
2668 }
2669 else if (strType == "PIIX4")
2670 {
2671 sctl.storageBus = StorageBus_IDE;
2672 sctl.controllerType = StorageControllerType_PIIX4;
2673 }
2674 else if (strType == "ICH6")
2675 {
2676 sctl.storageBus = StorageBus_IDE;
2677 sctl.controllerType = StorageControllerType_ICH6;
2678 }
2679 else if ( (m->sv >= SettingsVersion_v1_9)
2680 && (strType == "I82078")
2681 )
2682 {
2683 sctl.storageBus = StorageBus_Floppy;
2684 sctl.controllerType = StorageControllerType_I82078;
2685 }
2686 else if (strType == "LsiLogicSas")
2687 {
2688 sctl.storageBus = StorageBus_SAS;
2689 sctl.controllerType = StorageControllerType_LsiLogicSas;
2690 }
2691 else
2692 throw ConfigFileError(this, pelmController, N_("Invalid value '%s' for StorageController/@type attribute"), strType.c_str());
2693
2694 readStorageControllerAttributes(*pelmController, sctl);
2695
2696 xml::NodesLoop nlAttached(*pelmController, "AttachedDevice");
2697 const xml::ElementNode *pelmAttached;
2698 while ((pelmAttached = nlAttached.forAllNodes()))
2699 {
2700 AttachedDevice att;
2701 Utf8Str strTemp;
2702 pelmAttached->getAttributeValue("type", strTemp);
2703
2704 if (strTemp == "HardDisk")
2705 att.deviceType = DeviceType_HardDisk;
2706 else if (m->sv >= SettingsVersion_v1_9)
2707 {
2708 // starting with 1.9 we list DVD and floppy drive info + attachments under <StorageControllers>
2709 if (strTemp == "DVD")
2710 {
2711 att.deviceType = DeviceType_DVD;
2712 pelmAttached->getAttributeValue("passthrough", att.fPassThrough);
2713 }
2714 else if (strTemp == "Floppy")
2715 att.deviceType = DeviceType_Floppy;
2716 }
2717
2718 if (att.deviceType != DeviceType_Null)
2719 {
2720 const xml::ElementNode *pelmImage;
2721 // all types can have images attached, but for HardDisk it's required
2722 if (!(pelmImage = pelmAttached->findChildElement("Image")))
2723 {
2724 if (att.deviceType == DeviceType_HardDisk)
2725 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image element is missing"));
2726 else
2727 {
2728 // DVDs and floppies can also have <HostDrive> instead of <Image>
2729 const xml::ElementNode *pelmHostDrive;
2730 if ((pelmHostDrive = pelmAttached->findChildElement("HostDrive")))
2731 if (!pelmHostDrive->getAttributeValue("src", att.strHostDriveSrc))
2732 throw ConfigFileError(this, pelmHostDrive, N_("Required AttachedDevice/HostDrive/@src attribute is missing"));
2733 }
2734 }
2735 else
2736 {
2737 if (!pelmImage->getAttributeValue("uuid", strTemp))
2738 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/Image/@uuid attribute is missing"));
2739 parseUUID(att.uuid, strTemp);
2740 }
2741
2742 if (!pelmAttached->getAttributeValue("port", att.lPort))
2743 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@port attribute is missing"));
2744 if (!pelmAttached->getAttributeValue("device", att.lDevice))
2745 throw ConfigFileError(this, pelmImage, N_("Required AttachedDevice/@device attribute is missing"));
2746
2747 sctl.llAttachedDevices.push_back(att);
2748 }
2749 }
2750
2751 strg.llStorageControllers.push_back(sctl);
2752 }
2753}
2754
2755/**
2756 * This gets called for legacy pre-1.9 settings files after having parsed the
2757 * <Hardware> and <StorageControllers> sections to parse <Hardware> once more
2758 * for the <DVDDrive> and <FloppyDrive> sections.
2759 *
2760 * Before settings version 1.9, DVD and floppy drives were specified separately
2761 * under <Hardware>; we then need this extra loop to make sure the storage
2762 * controller structs are already set up so we can add stuff to them.
2763 *
2764 * @param elmHardware
2765 * @param strg
2766 */
2767void MachineConfigFile::readDVDAndFloppies_pre1_9(const xml::ElementNode &elmHardware,
2768 Storage &strg)
2769{
2770 xml::NodesLoop nl1(elmHardware);
2771 const xml::ElementNode *pelmHwChild;
2772 while ((pelmHwChild = nl1.forAllNodes()))
2773 {
2774 if (pelmHwChild->nameEquals("DVDDrive"))
2775 {
2776 // create a DVD "attached device" and attach it to the existing IDE controller
2777 AttachedDevice att;
2778 att.deviceType = DeviceType_DVD;
2779 // legacy DVD drive is always secondary master (port 1, device 0)
2780 att.lPort = 1;
2781 att.lDevice = 0;
2782 pelmHwChild->getAttributeValue("passthrough", att.fPassThrough);
2783
2784 const xml::ElementNode *pDriveChild;
2785 Utf8Str strTmp;
2786 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2787 && (pDriveChild->getAttributeValue("uuid", strTmp))
2788 )
2789 parseUUID(att.uuid, strTmp);
2790 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2791 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2792
2793 // find the IDE controller and attach the DVD drive
2794 bool fFound = false;
2795 for (StorageControllersList::iterator it = strg.llStorageControllers.begin();
2796 it != strg.llStorageControllers.end();
2797 ++it)
2798 {
2799 StorageController &sctl = *it;
2800 if (sctl.storageBus == StorageBus_IDE)
2801 {
2802 sctl.llAttachedDevices.push_back(att);
2803 fFound = true;
2804 break;
2805 }
2806 }
2807
2808 if (!fFound)
2809 throw ConfigFileError(this, pelmHwChild, N_("Internal error: found DVD drive but IDE controller does not exist"));
2810 // shouldn't happen because pre-1.9 settings files always had at least one IDE controller in the settings
2811 // which should have gotten parsed in <StorageControllers> before this got called
2812 }
2813 else if (pelmHwChild->nameEquals("FloppyDrive"))
2814 {
2815 bool fEnabled;
2816 if ( (pelmHwChild->getAttributeValue("enabled", fEnabled))
2817 && (fEnabled)
2818 )
2819 {
2820 // create a new floppy controller and attach a floppy "attached device"
2821 StorageController sctl;
2822 sctl.strName = "Floppy Controller";
2823 sctl.storageBus = StorageBus_Floppy;
2824 sctl.controllerType = StorageControllerType_I82078;
2825 sctl.ulPortCount = 1;
2826
2827 AttachedDevice att;
2828 att.deviceType = DeviceType_Floppy;
2829 att.lPort = 0;
2830 att.lDevice = 0;
2831
2832 const xml::ElementNode *pDriveChild;
2833 Utf8Str strTmp;
2834 if ( ((pDriveChild = pelmHwChild->findChildElement("Image")))
2835 && (pDriveChild->getAttributeValue("uuid", strTmp))
2836 )
2837 parseUUID(att.uuid, strTmp);
2838 else if ((pDriveChild = pelmHwChild->findChildElement("HostDrive")))
2839 pDriveChild->getAttributeValue("src", att.strHostDriveSrc);
2840
2841 // store attachment with controller
2842 sctl.llAttachedDevices.push_back(att);
2843 // store controller with storage
2844 strg.llStorageControllers.push_back(sctl);
2845 }
2846 }
2847 }
2848}
2849
2850/**
2851 * Called initially for the <Snapshot> element under <Machine>, if present,
2852 * to store the snapshot's data into the given Snapshot structure (which is
2853 * then the one in the Machine struct). This might then recurse if
2854 * a <Snapshots> (plural) element is found in the snapshot, which should
2855 * contain a list of child snapshots; such lists are maintained in the
2856 * Snapshot structure.
2857 *
2858 * @param elmSnapshot
2859 * @param snap
2860 */
2861void MachineConfigFile::readSnapshot(const xml::ElementNode &elmSnapshot,
2862 Snapshot &snap)
2863{
2864 Utf8Str strTemp;
2865
2866 if (!elmSnapshot.getAttributeValue("uuid", strTemp))
2867 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@uuid attribute is missing"));
2868 parseUUID(snap.uuid, strTemp);
2869
2870 if (!elmSnapshot.getAttributeValue("name", snap.strName))
2871 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@name attribute is missing"));
2872
2873 // earlier 3.1 trunk builds had a bug and added Description as an attribute, read it silently and write it back as an element
2874 elmSnapshot.getAttributeValue("Description", snap.strDescription);
2875
2876 if (!elmSnapshot.getAttributeValue("timeStamp", strTemp))
2877 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@timeStamp attribute is missing"));
2878 parseTimestamp(snap.timestamp, strTemp);
2879
2880 elmSnapshot.getAttributeValue("stateFile", snap.strStateFile); // online snapshots only
2881
2882 // parse Hardware before the other elements because other things depend on it
2883 const xml::ElementNode *pelmHardware;
2884 if (!(pelmHardware = elmSnapshot.findChildElement("Hardware")))
2885 throw ConfigFileError(this, &elmSnapshot, N_("Required Snapshot/@Hardware element is missing"));
2886 readHardware(*pelmHardware, snap.hardware, snap.storage);
2887
2888 xml::NodesLoop nlSnapshotChildren(elmSnapshot);
2889 const xml::ElementNode *pelmSnapshotChild;
2890 while ((pelmSnapshotChild = nlSnapshotChildren.forAllNodes()))
2891 {
2892 if (pelmSnapshotChild->nameEquals("Description"))
2893 snap.strDescription = pelmSnapshotChild->getValue();
2894 else if ( (m->sv < SettingsVersion_v1_7)
2895 && (pelmSnapshotChild->nameEquals("HardDiskAttachments"))
2896 )
2897 readHardDiskAttachments_pre1_7(*pelmSnapshotChild, snap.storage);
2898 else if ( (m->sv >= SettingsVersion_v1_7)
2899 && (pelmSnapshotChild->nameEquals("StorageControllers"))
2900 )
2901 readStorageControllers(*pelmSnapshotChild, snap.storage);
2902 else if (pelmSnapshotChild->nameEquals("Snapshots"))
2903 {
2904 xml::NodesLoop nlChildSnapshots(*pelmSnapshotChild);
2905 const xml::ElementNode *pelmChildSnapshot;
2906 while ((pelmChildSnapshot = nlChildSnapshots.forAllNodes()))
2907 {
2908 if (pelmChildSnapshot->nameEquals("Snapshot"))
2909 {
2910 Snapshot child;
2911 readSnapshot(*pelmChildSnapshot, child);
2912 snap.llChildSnapshots.push_back(child);
2913 }
2914 }
2915 }
2916 }
2917
2918 if (m->sv < SettingsVersion_v1_9)
2919 // go through Hardware once more to repair the settings controller structures
2920 // with data from old DVDDrive and FloppyDrive elements
2921 readDVDAndFloppies_pre1_9(*pelmHardware, snap.storage);
2922}
2923
2924const struct {
2925 const char *pcszOld;
2926 const char *pcszNew;
2927} aConvertOSTypes[] =
2928{
2929 { "unknown", "Other" },
2930 { "dos", "DOS" },
2931 { "win31", "Windows31" },
2932 { "win95", "Windows95" },
2933 { "win98", "Windows98" },
2934 { "winme", "WindowsMe" },
2935 { "winnt4", "WindowsNT4" },
2936 { "win2k", "Windows2000" },
2937 { "winxp", "WindowsXP" },
2938 { "win2k3", "Windows2003" },
2939 { "winvista", "WindowsVista" },
2940 { "win2k8", "Windows2008" },
2941 { "os2warp3", "OS2Warp3" },
2942 { "os2warp4", "OS2Warp4" },
2943 { "os2warp45", "OS2Warp45" },
2944 { "ecs", "OS2eCS" },
2945 { "linux22", "Linux22" },
2946 { "linux24", "Linux24" },
2947 { "linux26", "Linux26" },
2948 { "archlinux", "ArchLinux" },
2949 { "debian", "Debian" },
2950 { "opensuse", "OpenSUSE" },
2951 { "fedoracore", "Fedora" },
2952 { "gentoo", "Gentoo" },
2953 { "mandriva", "Mandriva" },
2954 { "redhat", "RedHat" },
2955 { "ubuntu", "Ubuntu" },
2956 { "xandros", "Xandros" },
2957 { "freebsd", "FreeBSD" },
2958 { "openbsd", "OpenBSD" },
2959 { "netbsd", "NetBSD" },
2960 { "netware", "Netware" },
2961 { "solaris", "Solaris" },
2962 { "opensolaris", "OpenSolaris" },
2963 { "l4", "L4" }
2964};
2965
2966void MachineConfigFile::convertOldOSType_pre1_5(Utf8Str &str)
2967{
2968 for (unsigned u = 0;
2969 u < RT_ELEMENTS(aConvertOSTypes);
2970 ++u)
2971 {
2972 if (str == aConvertOSTypes[u].pcszOld)
2973 {
2974 str = aConvertOSTypes[u].pcszNew;
2975 break;
2976 }
2977 }
2978}
2979
2980/**
2981 * Called from the constructor to actually read in the <Machine> element
2982 * of a machine config file.
2983 * @param elmMachine
2984 */
2985void MachineConfigFile::readMachine(const xml::ElementNode &elmMachine)
2986{
2987 Utf8Str strUUID;
2988 if ( (elmMachine.getAttributeValue("uuid", strUUID))
2989 && (elmMachine.getAttributeValue("name", strName))
2990 )
2991 {
2992 parseUUID(uuid, strUUID);
2993
2994 if (!elmMachine.getAttributeValue("nameSync", fNameSync))
2995 fNameSync = true;
2996
2997 Utf8Str str;
2998 elmMachine.getAttributeValue("Description", strDescription);
2999
3000 elmMachine.getAttributeValue("OSType", strOsType);
3001 if (m->sv < SettingsVersion_v1_5)
3002 convertOldOSType_pre1_5(strOsType);
3003
3004 elmMachine.getAttributeValue("stateFile", strStateFile);
3005 if (elmMachine.getAttributeValue("currentSnapshot", str))
3006 parseUUID(uuidCurrentSnapshot, str);
3007 elmMachine.getAttributeValue("snapshotFolder", strSnapshotFolder);
3008 if (!elmMachine.getAttributeValue("currentStateModified", fCurrentStateModified))
3009 fCurrentStateModified = true;
3010 if (elmMachine.getAttributeValue("lastStateChange", str))
3011 parseTimestamp(timeLastStateChange, str);
3012 // constructor has called RTTimeNow(&timeLastStateChange) before
3013
3014 // parse Hardware before the other elements because other things depend on it
3015 const xml::ElementNode *pelmHardware;
3016 if (!(pelmHardware = elmMachine.findChildElement("Hardware")))
3017 throw ConfigFileError(this, &elmMachine, N_("Required Machine/Hardware element is missing"));
3018 readHardware(*pelmHardware, hardwareMachine, storageMachine);
3019
3020 xml::NodesLoop nlRootChildren(elmMachine);
3021 const xml::ElementNode *pelmMachineChild;
3022 while ((pelmMachineChild = nlRootChildren.forAllNodes()))
3023 {
3024 if (pelmMachineChild->nameEquals("ExtraData"))
3025 readExtraData(*pelmMachineChild,
3026 mapExtraDataItems);
3027 else if ( (m->sv < SettingsVersion_v1_7)
3028 && (pelmMachineChild->nameEquals("HardDiskAttachments"))
3029 )
3030 readHardDiskAttachments_pre1_7(*pelmMachineChild, storageMachine);
3031 else if ( (m->sv >= SettingsVersion_v1_7)
3032 && (pelmMachineChild->nameEquals("StorageControllers"))
3033 )
3034 readStorageControllers(*pelmMachineChild, storageMachine);
3035 else if (pelmMachineChild->nameEquals("Snapshot"))
3036 {
3037 Snapshot snap;
3038 // this will recurse into child snapshots, if necessary
3039 readSnapshot(*pelmMachineChild, snap);
3040 llFirstSnapshot.push_back(snap);
3041 }
3042 else if (pelmMachineChild->nameEquals("Description"))
3043 strDescription = pelmMachineChild->getValue();
3044 else if (pelmMachineChild->nameEquals("Teleporter"))
3045 {
3046 if (!pelmMachineChild->getAttributeValue("enabled", fTeleporterEnabled))
3047 fTeleporterEnabled = false;
3048 if (!pelmMachineChild->getAttributeValue("port", uTeleporterPort))
3049 uTeleporterPort = 0;
3050 if (!pelmMachineChild->getAttributeValue("address", strTeleporterAddress))
3051 strTeleporterAddress = "";
3052 if (!pelmMachineChild->getAttributeValue("password", strTeleporterPassword))
3053 strTeleporterPassword = "";
3054 }
3055 }
3056
3057 if (m->sv < SettingsVersion_v1_9)
3058 // go through Hardware once more to repair the settings controller structures
3059 // with data from old DVDDrive and FloppyDrive elements
3060 readDVDAndFloppies_pre1_9(*pelmHardware, storageMachine);
3061 }
3062 else
3063 throw ConfigFileError(this, &elmMachine, N_("Required Machine/@uuid or @name attributes is missing"));
3064}
3065
3066/**
3067 * Creates a <Hardware> node under elmParent and then writes out the XML
3068 * keys under that. Called for both the <Machine> node and for snapshots.
3069 * @param elmParent
3070 * @param st
3071 */
3072void MachineConfigFile::buildHardwareXML(xml::ElementNode &elmParent,
3073 const Hardware &hw,
3074 const Storage &strg)
3075{
3076 xml::ElementNode *pelmHardware = elmParent.createChild("Hardware");
3077
3078 if (m->sv >= SettingsVersion_v1_4)
3079 pelmHardware->setAttribute("version", hw.strVersion);
3080 if ( (m->sv >= SettingsVersion_v1_9)
3081 && (!hw.uuid.isEmpty())
3082 )
3083 pelmHardware->setAttribute("uuid", hw.uuid.toStringCurly());
3084
3085 xml::ElementNode *pelmCPU = pelmHardware->createChild("CPU");
3086
3087 xml::ElementNode *pelmHwVirtEx = pelmCPU->createChild("HardwareVirtEx");
3088 pelmHwVirtEx->setAttribute("enabled", hw.fHardwareVirt);
3089 if (m->sv >= SettingsVersion_v1_9)
3090 pelmHwVirtEx->setAttribute("exclusive", hw.fHardwareVirtExclusive);
3091
3092 pelmCPU->createChild("HardwareVirtExNestedPaging")->setAttribute("enabled", hw.fNestedPaging);
3093 pelmCPU->createChild("HardwareVirtExVPID")->setAttribute("enabled", hw.fVPID);
3094 pelmCPU->createChild("PAE")->setAttribute("enabled", hw.fPAE);
3095
3096 if (hw.fSyntheticCpu)
3097 pelmCPU->createChild("SyntheticCpu")->setAttribute("enabled", hw.fSyntheticCpu);
3098 pelmCPU->setAttribute("count", hw.cCPUs);
3099
3100 if (hw.fLargePages)
3101 pelmCPU->createChild("HardwareVirtExLargePages")->setAttribute("enabled", hw.fLargePages);
3102
3103 if (m->sv >= SettingsVersion_v1_10)
3104 {
3105 pelmCPU->setAttribute("hotplug", hw.fCpuHotPlug);
3106
3107 xml::ElementNode *pelmCpuTree = NULL;
3108 for (CpuList::const_iterator it = hw.llCpus.begin();
3109 it != hw.llCpus.end();
3110 ++it)
3111 {
3112 const Cpu &cpu = *it;
3113
3114 if (pelmCpuTree == NULL)
3115 pelmCpuTree = pelmCPU->createChild("CpuTree");
3116
3117 xml::ElementNode *pelmCpu = pelmCpuTree->createChild("Cpu");
3118 pelmCpu->setAttribute("id", cpu.ulId);
3119 }
3120 }
3121
3122 xml::ElementNode *pelmCpuIdTree = NULL;
3123 for (CpuIdLeafsList::const_iterator it = hw.llCpuIdLeafs.begin();
3124 it != hw.llCpuIdLeafs.end();
3125 ++it)
3126 {
3127 const CpuIdLeaf &leaf = *it;
3128
3129 if (pelmCpuIdTree == NULL)
3130 pelmCpuIdTree = pelmCPU->createChild("CpuIdTree");
3131
3132 xml::ElementNode *pelmCpuIdLeaf = pelmCpuIdTree->createChild("CpuIdLeaf");
3133 pelmCpuIdLeaf->setAttribute("id", leaf.ulId);
3134 pelmCpuIdLeaf->setAttribute("eax", leaf.ulEax);
3135 pelmCpuIdLeaf->setAttribute("ebx", leaf.ulEbx);
3136 pelmCpuIdLeaf->setAttribute("ecx", leaf.ulEcx);
3137 pelmCpuIdLeaf->setAttribute("edx", leaf.ulEdx);
3138 }
3139
3140 xml::ElementNode *pelmMemory = pelmHardware->createChild("Memory");
3141 pelmMemory->setAttribute("RAMSize", hw.ulMemorySizeMB);
3142 if (m->sv >= SettingsVersion_v1_10)
3143 {
3144 pelmMemory->setAttribute("PageFusion", hw.fPageFusionEnabled);
3145 }
3146
3147 if ( (m->sv >= SettingsVersion_v1_9)
3148 && (hw.firmwareType >= FirmwareType_EFI)
3149 )
3150 {
3151 xml::ElementNode *pelmFirmware = pelmHardware->createChild("Firmware");
3152 const char *pcszFirmware;
3153
3154 switch (hw.firmwareType)
3155 {
3156 case FirmwareType_EFI: pcszFirmware = "EFI"; break;
3157 case FirmwareType_EFI32: pcszFirmware = "EFI32"; break;
3158 case FirmwareType_EFI64: pcszFirmware = "EFI64"; break;
3159 case FirmwareType_EFIDUAL: pcszFirmware = "EFIDUAL"; break;
3160 default: pcszFirmware = "None"; break;
3161 }
3162 pelmFirmware->setAttribute("type", pcszFirmware);
3163 }
3164
3165 if ( (m->sv >= SettingsVersion_v1_10)
3166 )
3167 {
3168 xml::ElementNode *pelmHid = pelmHardware->createChild("HID");
3169 const char *pcszHid;
3170
3171 switch (hw.pointingHidType)
3172 {
3173 case PointingHidType_USBMouse: pcszHid = "USBMouse"; break;
3174 case PointingHidType_USBTablet: pcszHid = "USBTablet"; break;
3175 case PointingHidType_PS2Mouse: pcszHid = "PS2Mouse"; break;
3176 case PointingHidType_ComboMouse: pcszHid = "ComboMouse"; break;
3177 case PointingHidType_None: pcszHid = "None"; break;
3178 default: Assert(false); pcszHid = "PS2Mouse"; break;
3179 }
3180 pelmHid->setAttribute("Pointing", pcszHid);
3181
3182 switch (hw.keyboardHidType)
3183 {
3184 case KeyboardHidType_USBKeyboard: pcszHid = "USBKeyboard"; break;
3185 case KeyboardHidType_PS2Keyboard: pcszHid = "PS2Keyboard"; break;
3186 case KeyboardHidType_ComboKeyboard: pcszHid = "ComboKeyboard"; break;
3187 case KeyboardHidType_None: pcszHid = "None"; break;
3188 default: Assert(false); pcszHid = "PS2Keyboard"; break;
3189 }
3190 pelmHid->setAttribute("Keyboard", pcszHid);
3191 }
3192
3193 if ( (m->sv >= SettingsVersion_v1_10)
3194 )
3195 {
3196 xml::ElementNode *pelmHpet = pelmHardware->createChild("HPET");
3197 pelmHpet->setAttribute("enabled", hw.fHpetEnabled);
3198 }
3199
3200 xml::ElementNode *pelmBoot = pelmHardware->createChild("Boot");
3201 for (BootOrderMap::const_iterator it = hw.mapBootOrder.begin();
3202 it != hw.mapBootOrder.end();
3203 ++it)
3204 {
3205 uint32_t i = it->first;
3206 DeviceType_T type = it->second;
3207 const char *pcszDevice;
3208
3209 switch (type)
3210 {
3211 case DeviceType_Floppy: pcszDevice = "Floppy"; break;
3212 case DeviceType_DVD: pcszDevice = "DVD"; break;
3213 case DeviceType_HardDisk: pcszDevice = "HardDisk"; break;
3214 case DeviceType_Network: pcszDevice = "Network"; break;
3215 default: /*case DeviceType_Null:*/ pcszDevice = "None"; break;
3216 }
3217
3218 xml::ElementNode *pelmOrder = pelmBoot->createChild("Order");
3219 pelmOrder->setAttribute("position",
3220 i + 1); // XML is 1-based but internal data is 0-based
3221 pelmOrder->setAttribute("device", pcszDevice);
3222 }
3223
3224 xml::ElementNode *pelmDisplay = pelmHardware->createChild("Display");
3225 pelmDisplay->setAttribute("VRAMSize", hw.ulVRAMSizeMB);
3226 pelmDisplay->setAttribute("monitorCount", hw.cMonitors);
3227 pelmDisplay->setAttribute("accelerate3D", hw.fAccelerate3D);
3228
3229 if (m->sv >= SettingsVersion_v1_8)
3230 pelmDisplay->setAttribute("accelerate2DVideo", hw.fAccelerate2DVideo);
3231
3232 xml::ElementNode *pelmVRDP = pelmHardware->createChild("RemoteDisplay");
3233 pelmVRDP->setAttribute("enabled", hw.vrdpSettings.fEnabled);
3234 Utf8Str strPort = hw.vrdpSettings.strPort;
3235 if (!strPort.length())
3236 strPort = "3389";
3237 pelmVRDP->setAttribute("port", strPort);
3238 if (hw.vrdpSettings.strNetAddress.length())
3239 pelmVRDP->setAttribute("netAddress", hw.vrdpSettings.strNetAddress);
3240 const char *pcszAuthType;
3241 switch (hw.vrdpSettings.authType)
3242 {
3243 case VRDPAuthType_Guest: pcszAuthType = "Guest"; break;
3244 case VRDPAuthType_External: pcszAuthType = "External"; break;
3245 default: /*case VRDPAuthType_Null:*/ pcszAuthType = "Null"; break;
3246 }
3247 pelmVRDP->setAttribute("authType", pcszAuthType);
3248
3249 if (hw.vrdpSettings.ulAuthTimeout != 0)
3250 pelmVRDP->setAttribute("authTimeout", hw.vrdpSettings.ulAuthTimeout);
3251 if (hw.vrdpSettings.fAllowMultiConnection)
3252 pelmVRDP->setAttribute("allowMultiConnection", hw.vrdpSettings.fAllowMultiConnection);
3253 if (hw.vrdpSettings.fReuseSingleConnection)
3254 pelmVRDP->setAttribute("reuseSingleConnection", hw.vrdpSettings.fReuseSingleConnection);
3255
3256 if (m->sv >= SettingsVersion_v1_10)
3257 {
3258 xml::ElementNode *pelmVideoChannel = pelmVRDP->createChild("VideoChannel");
3259 pelmVideoChannel->setAttribute("enabled", hw.vrdpSettings.fVideoChannel);
3260 pelmVideoChannel->setAttribute("quality", hw.vrdpSettings.ulVideoChannelQuality);
3261 }
3262
3263 xml::ElementNode *pelmBIOS = pelmHardware->createChild("BIOS");
3264 pelmBIOS->createChild("ACPI")->setAttribute("enabled", hw.biosSettings.fACPIEnabled);
3265 pelmBIOS->createChild("IOAPIC")->setAttribute("enabled", hw.biosSettings.fIOAPICEnabled);
3266
3267 xml::ElementNode *pelmLogo = pelmBIOS->createChild("Logo");
3268 pelmLogo->setAttribute("fadeIn", hw.biosSettings.fLogoFadeIn);
3269 pelmLogo->setAttribute("fadeOut", hw.biosSettings.fLogoFadeOut);
3270 pelmLogo->setAttribute("displayTime", hw.biosSettings.ulLogoDisplayTime);
3271 if (hw.biosSettings.strLogoImagePath.length())
3272 pelmLogo->setAttribute("imagePath", hw.biosSettings.strLogoImagePath);
3273
3274 const char *pcszBootMenu;
3275 switch (hw.biosSettings.biosBootMenuMode)
3276 {
3277 case BIOSBootMenuMode_Disabled: pcszBootMenu = "Disabled"; break;
3278 case BIOSBootMenuMode_MenuOnly: pcszBootMenu = "MenuOnly"; break;
3279 default: /*BIOSBootMenuMode_MessageAndMenu*/ pcszBootMenu = "MessageAndMenu"; break;
3280 }
3281 pelmBIOS->createChild("BootMenu")->setAttribute("mode", pcszBootMenu);
3282 pelmBIOS->createChild("TimeOffset")->setAttribute("value", hw.biosSettings.llTimeOffset);
3283 pelmBIOS->createChild("PXEDebug")->setAttribute("enabled", hw.biosSettings.fPXEDebugEnabled);
3284
3285 if (m->sv < SettingsVersion_v1_9)
3286 {
3287 // settings formats before 1.9 had separate DVDDrive and FloppyDrive items under Hardware;
3288 // run thru the storage controllers to see if we have a DVD or floppy drives
3289 size_t cDVDs = 0;
3290 size_t cFloppies = 0;
3291
3292 xml::ElementNode *pelmDVD = pelmHardware->createChild("DVDDrive");
3293 xml::ElementNode *pelmFloppy = pelmHardware->createChild("FloppyDrive");
3294
3295 for (StorageControllersList::const_iterator it = strg.llStorageControllers.begin();
3296 it != strg.llStorageControllers.end();
3297 ++it)
3298 {
3299 const StorageController &sctl = *it;
3300 // in old settings format, the DVD drive could only have been under the IDE controller
3301 if (sctl.storageBus == StorageBus_IDE)
3302 {
3303 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
3304 it2 != sctl.llAttachedDevices.end();
3305 ++it2)
3306 {
3307 const AttachedDevice &att = *it2;
3308 if (att.deviceType == DeviceType_DVD)
3309 {
3310 if (cDVDs > 0)
3311 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one DVD drive with old settings format"));
3312
3313 ++cDVDs;
3314
3315 pelmDVD->setAttribute("passthrough", att.fPassThrough);
3316 if (!att.uuid.isEmpty())
3317 pelmDVD->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
3318 else if (att.strHostDriveSrc.length())
3319 pelmDVD->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
3320 }
3321 }
3322 }
3323 else if (sctl.storageBus == StorageBus_Floppy)
3324 {
3325 size_t cFloppiesHere = sctl.llAttachedDevices.size();
3326 if (cFloppiesHere > 1)
3327 throw ConfigFileError(this, NULL, N_("Internal error: floppy controller cannot have more than one device attachment"));
3328 if (cFloppiesHere)
3329 {
3330 const AttachedDevice &att = sctl.llAttachedDevices.front();
3331 pelmFloppy->setAttribute("enabled", true);
3332 if (!att.uuid.isEmpty())
3333 pelmFloppy->createChild("Image")->setAttribute("uuid", att.uuid.toStringCurly());
3334 else if (att.strHostDriveSrc.length())
3335 pelmFloppy->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
3336 }
3337
3338 cFloppies += cFloppiesHere;
3339 }
3340 }
3341
3342 if (cFloppies == 0)
3343 pelmFloppy->setAttribute("enabled", false);
3344 else if (cFloppies > 1)
3345 throw ConfigFileError(this, NULL, N_("Internal error: cannot save more than one floppy drive with old settings format"));
3346 }
3347
3348 xml::ElementNode *pelmUSB = pelmHardware->createChild("USBController");
3349 pelmUSB->setAttribute("enabled", hw.usbController.fEnabled);
3350 pelmUSB->setAttribute("enabledEhci", hw.usbController.fEnabledEHCI);
3351
3352 writeUSBDeviceFilters(*pelmUSB,
3353 hw.usbController.llDeviceFilters,
3354 false); // fHostMode
3355
3356 xml::ElementNode *pelmNetwork = pelmHardware->createChild("Network");
3357 for (NetworkAdaptersList::const_iterator it = hw.llNetworkAdapters.begin();
3358 it != hw.llNetworkAdapters.end();
3359 ++it)
3360 {
3361 const NetworkAdapter &nic = *it;
3362
3363 xml::ElementNode *pelmAdapter = pelmNetwork->createChild("Adapter");
3364 pelmAdapter->setAttribute("slot", nic.ulSlot);
3365 pelmAdapter->setAttribute("enabled", nic.fEnabled);
3366 pelmAdapter->setAttribute("MACAddress", nic.strMACAddress);
3367 pelmAdapter->setAttribute("cable", nic.fCableConnected);
3368 pelmAdapter->setAttribute("speed", nic.ulLineSpeed);
3369 if (nic.ulBootPriority != 0)
3370 {
3371 pelmAdapter->setAttribute("bootPriority", nic.ulBootPriority);
3372 }
3373 if (nic.fTraceEnabled)
3374 {
3375 pelmAdapter->setAttribute("trace", nic.fTraceEnabled);
3376 pelmAdapter->setAttribute("tracefile", nic.strTraceFile);
3377 }
3378
3379 const char *pcszType;
3380 switch (nic.type)
3381 {
3382 case NetworkAdapterType_Am79C973: pcszType = "Am79C973"; break;
3383 case NetworkAdapterType_I82540EM: pcszType = "82540EM"; break;
3384 case NetworkAdapterType_I82543GC: pcszType = "82543GC"; break;
3385 case NetworkAdapterType_I82545EM: pcszType = "82545EM"; break;
3386 case NetworkAdapterType_Virtio: pcszType = "virtio"; break;
3387 default: /*case NetworkAdapterType_Am79C970A:*/ pcszType = "Am79C970A"; break;
3388 }
3389 pelmAdapter->setAttribute("type", pcszType);
3390
3391 xml::ElementNode *pelmNAT;
3392 if (m->sv < SettingsVersion_v1_10)
3393 {
3394 switch (nic.mode)
3395 {
3396 case NetworkAttachmentType_NAT:
3397 pelmNAT = pelmAdapter->createChild("NAT");
3398 if (nic.nat.strNetwork.length())
3399 pelmNAT->setAttribute("network", nic.nat.strNetwork);
3400 break;
3401
3402 case NetworkAttachmentType_Bridged:
3403 pelmAdapter->createChild("BridgedInterface")->setAttribute("name", nic.strName);
3404 break;
3405
3406 case NetworkAttachmentType_Internal:
3407 pelmAdapter->createChild("InternalNetwork")->setAttribute("name", nic.strName);
3408 break;
3409
3410 case NetworkAttachmentType_HostOnly:
3411 pelmAdapter->createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
3412 break;
3413
3414#if defined(VBOX_WITH_VDE)
3415 case NetworkAttachmentType_VDE:
3416 pelmAdapter->createChild("VDE")->setAttribute("network", nic.strName);
3417 break;
3418#endif
3419
3420 default: /*case NetworkAttachmentType_Null:*/
3421 break;
3422 }
3423 }
3424 else
3425 {
3426 /* m->sv >= SettingsVersion_v1_10 */
3427 xml::ElementNode *pelmDisabledNode= NULL;
3428 if (nic.fHasDisabledNAT)
3429 pelmDisabledNode = pelmAdapter->createChild("DisabledModes");
3430 if (nic.fHasDisabledNAT)
3431 buildNetworkXML(NetworkAttachmentType_NAT, *pelmDisabledNode, nic);
3432 buildNetworkXML(nic.mode, *pelmAdapter, nic);
3433 }
3434 }
3435
3436 xml::ElementNode *pelmPorts = pelmHardware->createChild("UART");
3437 for (SerialPortsList::const_iterator it = hw.llSerialPorts.begin();
3438 it != hw.llSerialPorts.end();
3439 ++it)
3440 {
3441 const SerialPort &port = *it;
3442 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
3443 pelmPort->setAttribute("slot", port.ulSlot);
3444 pelmPort->setAttribute("enabled", port.fEnabled);
3445 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
3446 pelmPort->setAttribute("IRQ", port.ulIRQ);
3447
3448 const char *pcszHostMode;
3449 switch (port.portMode)
3450 {
3451 case PortMode_HostPipe: pcszHostMode = "HostPipe"; break;
3452 case PortMode_HostDevice: pcszHostMode = "HostDevice"; break;
3453 case PortMode_RawFile: pcszHostMode = "RawFile"; break;
3454 default: /*case PortMode_Disconnected:*/ pcszHostMode = "Disconnected"; break;
3455 }
3456 switch (port.portMode)
3457 {
3458 case PortMode_HostPipe:
3459 pelmPort->setAttribute("server", port.fServer);
3460 /* no break */
3461 case PortMode_HostDevice:
3462 case PortMode_RawFile:
3463 pelmPort->setAttribute("path", port.strPath);
3464 break;
3465
3466 default:
3467 break;
3468 }
3469 pelmPort->setAttribute("hostMode", pcszHostMode);
3470 }
3471
3472 pelmPorts = pelmHardware->createChild("LPT");
3473 for (ParallelPortsList::const_iterator it = hw.llParallelPorts.begin();
3474 it != hw.llParallelPorts.end();
3475 ++it)
3476 {
3477 const ParallelPort &port = *it;
3478 xml::ElementNode *pelmPort = pelmPorts->createChild("Port");
3479 pelmPort->setAttribute("slot", port.ulSlot);
3480 pelmPort->setAttribute("enabled", port.fEnabled);
3481 pelmPort->setAttributeHex("IOBase", port.ulIOBase);
3482 pelmPort->setAttribute("IRQ", port.ulIRQ);
3483 if (port.strPath.length())
3484 pelmPort->setAttribute("path", port.strPath);
3485 }
3486
3487 xml::ElementNode *pelmAudio = pelmHardware->createChild("AudioAdapter");
3488 const char *pcszController;
3489 switch (hw.audioAdapter.controllerType)
3490 {
3491 case AudioControllerType_SB16:
3492 pcszController = "SB16";
3493 break;
3494 case AudioControllerType_HDA:
3495 if (m->sv >= SettingsVersion_v1_11)
3496 {
3497 pcszController = "HDA";
3498 break;
3499 }
3500 /* fall through */
3501 case AudioControllerType_AC97:
3502 default:
3503 pcszController = "AC97"; break;
3504 }
3505 pelmAudio->setAttribute("controller", pcszController);
3506
3507 if (m->sv >= SettingsVersion_v1_10)
3508 {
3509 xml::ElementNode *pelmRTC = pelmHardware->createChild("RTC");
3510 pelmRTC->setAttribute("localOrUTC", fRTCUseUTC ? "UTC" : "local");
3511 }
3512
3513 const char *pcszDriver;
3514 switch (hw.audioAdapter.driverType)
3515 {
3516 case AudioDriverType_WinMM: pcszDriver = "WinMM"; break;
3517 case AudioDriverType_DirectSound: pcszDriver = "DirectSound"; break;
3518 case AudioDriverType_SolAudio: pcszDriver = "SolAudio"; break;
3519 case AudioDriverType_ALSA: pcszDriver = "ALSA"; break;
3520 case AudioDriverType_Pulse: pcszDriver = "Pulse"; break;
3521 case AudioDriverType_OSS: pcszDriver = "OSS"; break;
3522 case AudioDriverType_CoreAudio: pcszDriver = "CoreAudio"; break;
3523 case AudioDriverType_MMPM: pcszDriver = "MMPM"; break;
3524 default: /*case AudioDriverType_Null:*/ pcszDriver = "Null"; break;
3525 }
3526 pelmAudio->setAttribute("driver", pcszDriver);
3527
3528 pelmAudio->setAttribute("enabled", hw.audioAdapter.fEnabled);
3529
3530 xml::ElementNode *pelmSharedFolders = pelmHardware->createChild("SharedFolders");
3531 for (SharedFoldersList::const_iterator it = hw.llSharedFolders.begin();
3532 it != hw.llSharedFolders.end();
3533 ++it)
3534 {
3535 const SharedFolder &sf = *it;
3536 xml::ElementNode *pelmThis = pelmSharedFolders->createChild("SharedFolder");
3537 pelmThis->setAttribute("name", sf.strName);
3538 pelmThis->setAttribute("hostPath", sf.strHostPath);
3539 pelmThis->setAttribute("writable", sf.fWritable);
3540 pelmThis->setAttribute("autoMount", sf.fAutoMount);
3541 }
3542
3543 xml::ElementNode *pelmClip = pelmHardware->createChild("Clipboard");
3544 const char *pcszClip;
3545 switch (hw.clipboardMode)
3546 {
3547 case ClipboardMode_Disabled: pcszClip = "Disabled"; break;
3548 case ClipboardMode_HostToGuest: pcszClip = "HostToGuest"; break;
3549 case ClipboardMode_GuestToHost: pcszClip = "GuestToHost"; break;
3550 default: /*case ClipboardMode_Bidirectional:*/ pcszClip = "Bidirectional"; break;
3551 }
3552 pelmClip->setAttribute("mode", pcszClip);
3553
3554 if (m->sv >= SettingsVersion_v1_10)
3555 {
3556 xml::ElementNode *pelmIo = pelmHardware->createChild("IO");
3557 xml::ElementNode *pelmIoCache;
3558 xml::ElementNode *pelmIoBandwidth;
3559
3560 pelmIoCache = pelmIo->createChild("IoCache");
3561 pelmIoCache->setAttribute("enabled", hw.ioSettings.fIoCacheEnabled);
3562 pelmIoCache->setAttribute("size", hw.ioSettings.ulIoCacheSize);
3563 pelmIoBandwidth = pelmIo->createChild("IoBandwidth");
3564 pelmIoBandwidth->setAttribute("max", hw.ioSettings.ulIoBandwidthMax);
3565 }
3566
3567 xml::ElementNode *pelmGuest = pelmHardware->createChild("Guest");
3568 pelmGuest->setAttribute("memoryBalloonSize", hw.ulMemoryBalloonSize);
3569
3570 xml::ElementNode *pelmGuestProps = pelmHardware->createChild("GuestProperties");
3571 for (GuestPropertiesList::const_iterator it = hw.llGuestProperties.begin();
3572 it != hw.llGuestProperties.end();
3573 ++it)
3574 {
3575 const GuestProperty &prop = *it;
3576 xml::ElementNode *pelmProp = pelmGuestProps->createChild("GuestProperty");
3577 pelmProp->setAttribute("name", prop.strName);
3578 pelmProp->setAttribute("value", prop.strValue);
3579 pelmProp->setAttribute("timestamp", prop.timestamp);
3580 pelmProp->setAttribute("flags", prop.strFlags);
3581 }
3582
3583 if (hw.strNotificationPatterns.length())
3584 pelmGuestProps->setAttribute("notificationPatterns", hw.strNotificationPatterns);
3585}
3586
3587/**
3588 * Fill a <Network> node. Only relevant for XML version >= v1_10.
3589 * @param mode
3590 * @param elmParent
3591 * @param nice
3592 */
3593void MachineConfigFile::buildNetworkXML(NetworkAttachmentType_T mode,
3594 xml::ElementNode &elmParent,
3595 const NetworkAdapter &nic)
3596{
3597 switch (mode)
3598 {
3599 case NetworkAttachmentType_NAT:
3600 xml::ElementNode *pelmNAT;
3601 pelmNAT = elmParent.createChild("NAT");
3602
3603 if (nic.nat.strBindIP.length())
3604 pelmNAT->setAttribute("hostip", nic.nat.strBindIP);
3605 if (nic.nat.u32Mtu)
3606 pelmNAT->setAttribute("mtu", nic.nat.u32Mtu);
3607 if (nic.nat.u32SockRcv)
3608 pelmNAT->setAttribute("sockrcv", nic.nat.u32SockRcv);
3609 if (nic.nat.u32SockSnd)
3610 pelmNAT->setAttribute("socksnd", nic.nat.u32SockSnd);
3611 if (nic.nat.u32TcpRcv)
3612 pelmNAT->setAttribute("tcprcv", nic.nat.u32TcpRcv);
3613 if (nic.nat.u32TcpSnd)
3614 pelmNAT->setAttribute("tcpsnd", nic.nat.u32TcpSnd);
3615 xml::ElementNode *pelmDNS;
3616 pelmDNS = pelmNAT->createChild("DNS");
3617 pelmDNS->setAttribute("pass-domain", nic.nat.fDnsPassDomain);
3618 pelmDNS->setAttribute("use-proxy", nic.nat.fDnsProxy);
3619 pelmDNS->setAttribute("use-host-resolver", nic.nat.fDnsUseHostResolver);
3620
3621 xml::ElementNode *pelmAlias;
3622 pelmAlias = pelmNAT->createChild("Alias");
3623 pelmAlias->setAttribute("logging", nic.nat.fAliasLog);
3624 pelmAlias->setAttribute("proxy-only", nic.nat.fAliasProxyOnly);
3625 pelmAlias->setAttribute("use-same-ports", nic.nat.fAliasUseSamePorts);
3626
3627 if ( nic.nat.strTftpPrefix.length()
3628 || nic.nat.strTftpBootFile.length()
3629 || nic.nat.strTftpNextServer.length())
3630 {
3631 xml::ElementNode *pelmTFTP;
3632 pelmTFTP = pelmNAT->createChild("TFTP");
3633 if (nic.nat.strTftpPrefix.length())
3634 pelmTFTP->setAttribute("prefix", nic.nat.strTftpPrefix);
3635 if (nic.nat.strTftpBootFile.length())
3636 pelmTFTP->setAttribute("boot-file", nic.nat.strTftpBootFile);
3637 if (nic.nat.strTftpNextServer.length())
3638 pelmTFTP->setAttribute("next-server", nic.nat.strTftpNextServer);
3639 }
3640 for (NATRuleList::const_iterator rule = nic.nat.llRules.begin();
3641 rule != nic.nat.llRules.end(); ++rule)
3642 {
3643 xml::ElementNode *pelmPF;
3644 pelmPF = pelmNAT->createChild("Forwarding");
3645 if ((*rule).strName.length())
3646 pelmPF->setAttribute("name", (*rule).strName);
3647 pelmPF->setAttribute("proto", (*rule).u32Proto);
3648 if ((*rule).strHostIP.length())
3649 pelmPF->setAttribute("hostip", (*rule).strHostIP);
3650 if ((*rule).u16HostPort)
3651 pelmPF->setAttribute("hostport", (*rule).u16HostPort);
3652 if ((*rule).strGuestIP.length())
3653 pelmPF->setAttribute("guestip", (*rule).strGuestIP);
3654 if ((*rule).u16GuestPort)
3655 pelmPF->setAttribute("guestport", (*rule).u16GuestPort);
3656 }
3657 break;
3658
3659 case NetworkAttachmentType_Bridged:
3660 elmParent.createChild("BridgedInterface")->setAttribute("name", nic.strName);
3661 break;
3662
3663 case NetworkAttachmentType_Internal:
3664 elmParent.createChild("InternalNetwork")->setAttribute("name", nic.strName);
3665 break;
3666
3667 case NetworkAttachmentType_HostOnly:
3668 elmParent.createChild("HostOnlyInterface")->setAttribute("name", nic.strName);
3669 break;
3670
3671#ifdef VBOX_WITH_VDE
3672 case NetworkAttachmentType_VDE:
3673 elmParent.createChild("VDE")->setAttribute("network", nic.strName);
3674 break;
3675#endif
3676
3677 default: /*case NetworkAttachmentType_Null:*/
3678 break;
3679 }
3680}
3681
3682/**
3683 * Creates a <StorageControllers> node under elmParent and then writes out the XML
3684 * keys under that. Called for both the <Machine> node and for snapshots.
3685 * @param elmParent
3686 * @param st
3687 * @param fSkipRemovableMedia If true, DVD and floppy attachments are skipped and
3688 * an empty drive is always written instead. This is for the OVF export case.
3689 * This parameter is ignored unless the settings version is at least v1.9, which
3690 * is always the case when this gets called for OVF export.
3691 * @param pllElementsWithUuidAttributes If not NULL, must point to a list of element node
3692 * pointers to which we will append all allements that we created here that contain
3693 * UUID attributes. This allows the OVF export code to quickly replace the internal
3694 * media UUIDs with the UUIDs of the media that were exported.
3695 */
3696void MachineConfigFile::buildStorageControllersXML(xml::ElementNode &elmParent,
3697 const Storage &st,
3698 bool fSkipRemovableMedia,
3699 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
3700{
3701 xml::ElementNode *pelmStorageControllers = elmParent.createChild("StorageControllers");
3702
3703 for (StorageControllersList::const_iterator it = st.llStorageControllers.begin();
3704 it != st.llStorageControllers.end();
3705 ++it)
3706 {
3707 const StorageController &sc = *it;
3708
3709 if ( (m->sv < SettingsVersion_v1_9)
3710 && (sc.controllerType == StorageControllerType_I82078)
3711 )
3712 // floppy controller already got written into <Hardware>/<FloppyController> in writeHardware()
3713 // for pre-1.9 settings
3714 continue;
3715
3716 xml::ElementNode *pelmController = pelmStorageControllers->createChild("StorageController");
3717 com::Utf8Str name = sc.strName.raw();
3718 //
3719 if (m->sv < SettingsVersion_v1_8)
3720 {
3721 // pre-1.8 settings use shorter controller names, they are
3722 // expanded when reading the settings
3723 if (name == "IDE Controller")
3724 name = "IDE";
3725 else if (name == "SATA Controller")
3726 name = "SATA";
3727 else if (name == "SCSI Controller")
3728 name = "SCSI";
3729 }
3730 pelmController->setAttribute("name", sc.strName);
3731
3732 const char *pcszType;
3733 switch (sc.controllerType)
3734 {
3735 case StorageControllerType_IntelAhci: pcszType = "AHCI"; break;
3736 case StorageControllerType_LsiLogic: pcszType = "LsiLogic"; break;
3737 case StorageControllerType_BusLogic: pcszType = "BusLogic"; break;
3738 case StorageControllerType_PIIX4: pcszType = "PIIX4"; break;
3739 case StorageControllerType_ICH6: pcszType = "ICH6"; break;
3740 case StorageControllerType_I82078: pcszType = "I82078"; break;
3741 case StorageControllerType_LsiLogicSas: pcszType = "LsiLogicSas"; break;
3742 default: /*case StorageControllerType_PIIX3:*/ pcszType = "PIIX3"; break;
3743 }
3744 pelmController->setAttribute("type", pcszType);
3745
3746 pelmController->setAttribute("PortCount", sc.ulPortCount);
3747
3748 if (m->sv >= SettingsVersion_v1_9)
3749 if (sc.ulInstance)
3750 pelmController->setAttribute("Instance", sc.ulInstance);
3751
3752 if (m->sv >= SettingsVersion_v1_10)
3753 pelmController->setAttribute("useHostIOCache", sc.fUseHostIOCache);
3754
3755 if (sc.controllerType == StorageControllerType_IntelAhci)
3756 {
3757 pelmController->setAttribute("IDE0MasterEmulationPort", sc.lIDE0MasterEmulationPort);
3758 pelmController->setAttribute("IDE0SlaveEmulationPort", sc.lIDE0SlaveEmulationPort);
3759 pelmController->setAttribute("IDE1MasterEmulationPort", sc.lIDE1MasterEmulationPort);
3760 pelmController->setAttribute("IDE1SlaveEmulationPort", sc.lIDE1SlaveEmulationPort);
3761 }
3762
3763 for (AttachedDevicesList::const_iterator it2 = sc.llAttachedDevices.begin();
3764 it2 != sc.llAttachedDevices.end();
3765 ++it2)
3766 {
3767 const AttachedDevice &att = *it2;
3768
3769 // For settings version before 1.9, DVDs and floppies are in hardware, not storage controllers,
3770 // so we shouldn't write them here; we only get here for DVDs though because we ruled out
3771 // the floppy controller at the top of the loop
3772 if ( att.deviceType == DeviceType_DVD
3773 && m->sv < SettingsVersion_v1_9
3774 )
3775 continue;
3776
3777 xml::ElementNode *pelmDevice = pelmController->createChild("AttachedDevice");
3778
3779 pcszType = NULL;
3780
3781 switch (att.deviceType)
3782 {
3783 case DeviceType_HardDisk:
3784 pcszType = "HardDisk";
3785 break;
3786
3787 case DeviceType_DVD:
3788 pcszType = "DVD";
3789 pelmDevice->setAttribute("passthrough", att.fPassThrough);
3790 break;
3791
3792 case DeviceType_Floppy:
3793 pcszType = "Floppy";
3794 break;
3795 }
3796
3797 pelmDevice->setAttribute("type", pcszType);
3798
3799 pelmDevice->setAttribute("port", att.lPort);
3800 pelmDevice->setAttribute("device", att.lDevice);
3801
3802 // attached image, if any
3803 if ( !att.uuid.isEmpty()
3804 && ( att.deviceType == DeviceType_HardDisk
3805 || !fSkipRemovableMedia
3806 )
3807 )
3808 {
3809 xml::ElementNode *pelmImage = pelmDevice->createChild("Image");
3810 pelmImage->setAttribute("uuid", att.uuid.toStringCurly());
3811
3812 // if caller wants a list of UUID elements, give it to them
3813 if (pllElementsWithUuidAttributes)
3814 pllElementsWithUuidAttributes->push_back(pelmImage);
3815 }
3816 else if ( (m->sv >= SettingsVersion_v1_9)
3817 && (att.strHostDriveSrc.length())
3818 )
3819 pelmDevice->createChild("HostDrive")->setAttribute("src", att.strHostDriveSrc);
3820 }
3821 }
3822}
3823
3824/**
3825 * Writes a single snapshot into the DOM tree. Initially this gets called from MachineConfigFile::write()
3826 * for the root snapshot of a machine, if present; elmParent then points to the <Snapshots> node under the
3827 * <Machine> node to which <Snapshot> must be added. This may then recurse for child snapshots.
3828 * @param elmParent
3829 * @param snap
3830 */
3831void MachineConfigFile::buildSnapshotXML(xml::ElementNode &elmParent,
3832 const Snapshot &snap)
3833{
3834 xml::ElementNode *pelmSnapshot = elmParent.createChild("Snapshot");
3835
3836 pelmSnapshot->setAttribute("uuid", snap.uuid.toStringCurly());
3837 pelmSnapshot->setAttribute("name", snap.strName);
3838 pelmSnapshot->setAttribute("timeStamp", makeString(snap.timestamp));
3839
3840 if (snap.strStateFile.length())
3841 pelmSnapshot->setAttribute("stateFile", snap.strStateFile);
3842
3843 if (snap.strDescription.length())
3844 pelmSnapshot->createChild("Description")->addContent(snap.strDescription);
3845
3846 buildHardwareXML(*pelmSnapshot, snap.hardware, snap.storage);
3847 buildStorageControllersXML(*pelmSnapshot,
3848 snap.storage,
3849 false /* fSkipRemovableMedia */,
3850 NULL); /* pllElementsWithUuidAttributes */
3851 // we only skip removable media for OVF, but we never get here for OVF
3852 // since snapshots never get written then
3853
3854 if (snap.llChildSnapshots.size())
3855 {
3856 xml::ElementNode *pelmChildren = pelmSnapshot->createChild("Snapshots");
3857 for (SnapshotsList::const_iterator it = snap.llChildSnapshots.begin();
3858 it != snap.llChildSnapshots.end();
3859 ++it)
3860 {
3861 const Snapshot &child = *it;
3862 buildSnapshotXML(*pelmChildren, child);
3863 }
3864 }
3865}
3866
3867/**
3868 * Builds the XML DOM tree for the machine config under the given XML element.
3869 *
3870 * This has been separated out from write() so it can be called from elsewhere,
3871 * such as the OVF code, to build machine XML in an existing XML tree.
3872 *
3873 * As a result, this gets called from two locations:
3874 *
3875 * -- MachineConfigFile::write();
3876 *
3877 * -- Appliance::buildXMLForOneVirtualSystem()
3878 *
3879 * In fl, the following flag bits are recognized:
3880 *
3881 * -- BuildMachineXML_IncludeSnapshots: If set, descend into the snapshots tree
3882 * of the machine and write out <Snapshot> and possibly more snapshots under
3883 * that, if snapshots are present. Otherwise all snapshots are suppressed.
3884 *
3885 * -- BuildMachineXML_WriteVboxVersionAttribute: If set, add a settingsVersion
3886 * attribute to the machine tag with the vbox settings version. This is for
3887 * the OVF export case in which we don't have the settings version set in
3888 * the root element.
3889 *
3890 * -- BuildMachineXML_SkipRemovableMedia: If set, removable media attachments
3891 * (DVDs, floppies) are silently skipped. This is for the OVF export case
3892 * until we support copying ISO and RAW media as well. This flas is ignored
3893 * unless the settings version is at least v1.9, which is always the case
3894 * when this gets called for OVF export.
3895 *
3896 * @param elmMachine XML <Machine> element to add attributes and elements to.
3897 * @param fl Flags.
3898 * @param pllElementsWithUuidAttributes pointer to list that should receive UUID elements or NULL;
3899 * see buildStorageControllersXML() for details.
3900 */
3901void MachineConfigFile::buildMachineXML(xml::ElementNode &elmMachine,
3902 uint32_t fl,
3903 std::list<xml::ElementNode*> *pllElementsWithUuidAttributes)
3904{
3905 if (fl & BuildMachineXML_WriteVboxVersionAttribute)
3906 // add settings version attribute to machine element
3907 setVersionAttribute(elmMachine);
3908
3909 elmMachine.setAttribute("uuid", uuid.toStringCurly());
3910 elmMachine.setAttribute("name", strName);
3911 if (!fNameSync)
3912 elmMachine.setAttribute("nameSync", fNameSync);
3913 if (strDescription.length())
3914 elmMachine.createChild("Description")->addContent(strDescription);
3915 elmMachine.setAttribute("OSType", strOsType);
3916 if (strStateFile.length())
3917 elmMachine.setAttribute("stateFile", strStateFile);
3918 if ( (fl & BuildMachineXML_IncludeSnapshots)
3919 && !uuidCurrentSnapshot.isEmpty())
3920 elmMachine.setAttribute("currentSnapshot", uuidCurrentSnapshot.toStringCurly());
3921 if (strSnapshotFolder.length())
3922 elmMachine.setAttribute("snapshotFolder", strSnapshotFolder);
3923 if (!fCurrentStateModified)
3924 elmMachine.setAttribute("currentStateModified", fCurrentStateModified);
3925 elmMachine.setAttribute("lastStateChange", makeString(timeLastStateChange));
3926 if (fAborted)
3927 elmMachine.setAttribute("aborted", fAborted);
3928 if ( m->sv >= SettingsVersion_v1_9
3929 && ( fTeleporterEnabled
3930 || uTeleporterPort
3931 || !strTeleporterAddress.isEmpty()
3932 || !strTeleporterPassword.isEmpty()
3933 )
3934 )
3935 {
3936 xml::ElementNode *pelmTeleporter = elmMachine.createChild("Teleporter");
3937 pelmTeleporter->setAttribute("enabled", fTeleporterEnabled);
3938 pelmTeleporter->setAttribute("port", uTeleporterPort);
3939 pelmTeleporter->setAttribute("address", strTeleporterAddress);
3940 pelmTeleporter->setAttribute("password", strTeleporterPassword);
3941 }
3942
3943 writeExtraData(elmMachine, mapExtraDataItems);
3944
3945 if ( (fl & BuildMachineXML_IncludeSnapshots)
3946 && llFirstSnapshot.size())
3947 buildSnapshotXML(elmMachine, llFirstSnapshot.front());
3948
3949 buildHardwareXML(elmMachine, hardwareMachine, storageMachine);
3950 buildStorageControllersXML(elmMachine,
3951 storageMachine,
3952 !!(fl & BuildMachineXML_SkipRemovableMedia),
3953 pllElementsWithUuidAttributes);
3954}
3955
3956/**
3957 * Returns true only if the given AudioDriverType is supported on
3958 * the current host platform. For example, this would return false
3959 * for AudioDriverType_DirectSound when compiled on a Linux host.
3960 * @param drv AudioDriverType_* enum to test.
3961 * @return true only if the current host supports that driver.
3962 */
3963/*static*/
3964bool MachineConfigFile::isAudioDriverAllowedOnThisHost(AudioDriverType_T drv)
3965{
3966 switch (drv)
3967 {
3968 case AudioDriverType_Null:
3969#ifdef RT_OS_WINDOWS
3970# ifdef VBOX_WITH_WINMM
3971 case AudioDriverType_WinMM:
3972# endif
3973 case AudioDriverType_DirectSound:
3974#endif /* RT_OS_WINDOWS */
3975#ifdef RT_OS_SOLARIS
3976 case AudioDriverType_SolAudio:
3977#endif
3978#ifdef RT_OS_LINUX
3979# ifdef VBOX_WITH_ALSA
3980 case AudioDriverType_ALSA:
3981# endif
3982# ifdef VBOX_WITH_PULSE
3983 case AudioDriverType_Pulse:
3984# endif
3985#endif /* RT_OS_LINUX */
3986#if defined (RT_OS_LINUX) || defined (RT_OS_FREEBSD) || defined(VBOX_WITH_SOLARIS_OSS)
3987 case AudioDriverType_OSS:
3988#endif
3989#ifdef RT_OS_FREEBSD
3990# ifdef VBOX_WITH_PULSE
3991 case AudioDriverType_Pulse:
3992# endif
3993#endif
3994#ifdef RT_OS_DARWIN
3995 case AudioDriverType_CoreAudio:
3996#endif
3997#ifdef RT_OS_OS2
3998 case AudioDriverType_MMPM:
3999#endif
4000 return true;
4001 }
4002
4003 return false;
4004}
4005
4006/**
4007 * Returns the AudioDriverType_* which should be used by default on this
4008 * host platform. On Linux, this will check at runtime whether PulseAudio
4009 * or ALSA are actually supported on the first call.
4010 * @return
4011 */
4012/*static*/
4013AudioDriverType_T MachineConfigFile::getHostDefaultAudioDriver()
4014{
4015#if defined(RT_OS_WINDOWS)
4016# ifdef VBOX_WITH_WINMM
4017 return AudioDriverType_WinMM;
4018# else /* VBOX_WITH_WINMM */
4019 return AudioDriverType_DirectSound;
4020# endif /* !VBOX_WITH_WINMM */
4021#elif defined(RT_OS_SOLARIS)
4022 return AudioDriverType_SolAudio;
4023#elif defined(RT_OS_LINUX)
4024 // on Linux, we need to check at runtime what's actually supported...
4025 static RTLockMtx s_mtx;
4026 static AudioDriverType_T s_linuxDriver = -1;
4027 RTLock lock(s_mtx);
4028 if (s_linuxDriver == (AudioDriverType_T)-1)
4029 {
4030# if defined(VBOX_WITH_PULSE)
4031 /* Check for the pulse library & that the pulse audio daemon is running. */
4032 if (RTProcIsRunningByName("pulseaudio") &&
4033 RTLdrIsLoadable("libpulse.so.0"))
4034 s_linuxDriver = AudioDriverType_Pulse;
4035 else
4036# endif /* VBOX_WITH_PULSE */
4037# if defined(VBOX_WITH_ALSA)
4038 /* Check if we can load the ALSA library */
4039 if (RTLdrIsLoadable("libasound.so.2"))
4040 s_linuxDriver = AudioDriverType_ALSA;
4041 else
4042# endif /* VBOX_WITH_ALSA */
4043 s_linuxDriver = AudioDriverType_OSS;
4044 }
4045 return s_linuxDriver;
4046// end elif defined(RT_OS_LINUX)
4047#elif defined(RT_OS_DARWIN)
4048 return AudioDriverType_CoreAudio;
4049#elif defined(RT_OS_OS2)
4050 return AudioDriverType_MMP;
4051#elif defined(RT_OS_FREEBSD)
4052 return AudioDriverType_OSS;
4053#else
4054 return AudioDriverType_Null;
4055#endif
4056}
4057
4058/**
4059 * Called from write() before calling ConfigFileBase::createStubDocument().
4060 * This adjusts the settings version in m->sv if incompatible settings require
4061 * a settings bump, whereas otherwise we try to preserve the settings version
4062 * to avoid breaking compatibility with older versions.
4063 */
4064void MachineConfigFile::bumpSettingsVersionIfNeeded()
4065{
4066 // The hardware versions other than "1" requires settings version 1.4 (2.1+).
4067 if ( m->sv < SettingsVersion_v1_4
4068 && hardwareMachine.strVersion != "1"
4069 )
4070 m->sv = SettingsVersion_v1_4;
4071
4072 // "accelerate 2d video" requires settings version 1.8
4073 if ( (m->sv < SettingsVersion_v1_8)
4074 && (hardwareMachine.fAccelerate2DVideo)
4075 )
4076 m->sv = SettingsVersion_v1_8;
4077
4078 // all the following require settings version 1.9
4079 if ( (m->sv < SettingsVersion_v1_9)
4080 && ( (hardwareMachine.firmwareType >= FirmwareType_EFI)
4081 || (hardwareMachine.fHardwareVirtExclusive != HWVIRTEXCLUSIVEDEFAULT)
4082 || fTeleporterEnabled
4083 || uTeleporterPort
4084 || !strTeleporterAddress.isEmpty()
4085 || !strTeleporterPassword.isEmpty()
4086 || !hardwareMachine.uuid.isEmpty()
4087 )
4088 )
4089 m->sv = SettingsVersion_v1_9;
4090
4091 // settings version 1.9 is also required if there is not exactly one DVD
4092 // or more than one floppy drive present or the DVD is not at the secondary
4093 // master; this check is a bit more complicated
4094 //
4095 // settings version 1.10 is required if the host cache should be disabled
4096 if (m->sv < SettingsVersion_v1_10)
4097 {
4098 size_t cDVDs = 0;
4099 size_t cFloppies = 0;
4100
4101 // need to run thru all the storage controllers to figure this out
4102 for (StorageControllersList::const_iterator it = storageMachine.llStorageControllers.begin();
4103 it != storageMachine.llStorageControllers.end();
4104 ++it)
4105 {
4106 const StorageController &sctl = *it;
4107 for (AttachedDevicesList::const_iterator it2 = sctl.llAttachedDevices.begin();
4108 it2 != sctl.llAttachedDevices.end();
4109 ++it2)
4110 {
4111 // we can only write the StorageController/@Instance attribute with v1.9
4112 if (sctl.ulInstance != 0)
4113 {
4114 if (m->sv < SettingsVersion_v1_9)
4115 m->sv = SettingsVersion_v1_9;
4116 }
4117
4118 const AttachedDevice &att = *it2;
4119 if (att.deviceType == DeviceType_DVD)
4120 {
4121 if ( (sctl.storageBus != StorageBus_IDE) // DVD at bus other than DVD?
4122 || (att.lPort != 1) // DVDs not at secondary master?
4123 || (att.lDevice != 0)
4124 )
4125 {
4126 if (m->sv < SettingsVersion_v1_9)
4127 m->sv = SettingsVersion_v1_9;
4128 }
4129
4130 ++cDVDs;
4131 }
4132 else if (att.deviceType == DeviceType_Floppy)
4133 ++cFloppies;
4134
4135 // Disabling the host IO cache requires settings version 1.10
4136 if (!sctl.fUseHostIOCache)
4137 {
4138 m->sv = SettingsVersion_v1_10;
4139 break; /* abort the loop -- we will not raise the version further */
4140 }
4141 }
4142 }
4143
4144 // VirtualBox before 3.1 had zero or one floppy and exactly one DVD,
4145 // so any deviation from that will require settings version 1.9
4146 if ( (m->sv < SettingsVersion_v1_9)
4147 && ( (cDVDs != 1)
4148 || (cFloppies > 1)
4149 )
4150 )
4151 m->sv = SettingsVersion_v1_9;
4152 }
4153
4154 // VirtualBox 3.2 adds support for CPU hotplug, RTC timezone control, HID type and HPET
4155 if ( m->sv < SettingsVersion_v1_10
4156 && ( fRTCUseUTC
4157 || hardwareMachine.fCpuHotPlug
4158 || hardwareMachine.pointingHidType != PointingHidType_PS2Mouse
4159 || hardwareMachine.keyboardHidType != KeyboardHidType_PS2Keyboard
4160 || hardwareMachine.fHpetEnabled
4161 )
4162 )
4163 m->sv = SettingsVersion_v1_10;
4164
4165 // VirtualBox 3.2 adds support for page fusion
4166 if ( m->sv < SettingsVersion_v1_10
4167 && hardwareMachine.fPageFusionEnabled
4168 )
4169 m->sv = SettingsVersion_v1_10;
4170
4171 // VirtualBox 3.2 adds NAT and boot priority to the NIC config in Main.
4172 if (m->sv < SettingsVersion_v1_10)
4173 {
4174 NetworkAdaptersList::const_iterator netit;
4175 for (netit = hardwareMachine.llNetworkAdapters.begin();
4176 netit != hardwareMachine.llNetworkAdapters.end(); ++netit)
4177 {
4178 if ( netit->fEnabled
4179 && netit->mode == NetworkAttachmentType_NAT
4180 && ( netit->nat.u32Mtu != 0
4181 || netit->nat.u32SockRcv != 0
4182 || netit->nat.u32SockSnd != 0
4183 || netit->nat.u32TcpRcv != 0
4184 || netit->nat.u32TcpSnd != 0
4185 || !netit->nat.fDnsPassDomain
4186 || netit->nat.fDnsProxy
4187 || netit->nat.fDnsUseHostResolver
4188 || netit->nat.fAliasLog
4189 || netit->nat.fAliasProxyOnly
4190 || netit->nat.fAliasUseSamePorts
4191 || netit->nat.strTftpPrefix.length()
4192 || netit->nat.strTftpBootFile.length()
4193 || netit->nat.strTftpNextServer.length()
4194 || netit->nat.llRules.size())
4195 )
4196 {
4197 m->sv = SettingsVersion_v1_10;
4198 break;
4199 }
4200 if ( netit->fEnabled
4201 && netit->ulBootPriority != 0)
4202 {
4203 m->sv = SettingsVersion_v1_10;
4204 break;
4205 }
4206 }
4207 }
4208 // VirtualBox 3.2: Check for non default I/O settings and bump the settings version.
4209 if (m->sv < SettingsVersion_v1_10)
4210 {
4211 if ( hardwareMachine.ioSettings.fIoCacheEnabled != true
4212 || hardwareMachine.ioSettings.ulIoCacheSize != 5
4213 || hardwareMachine.ioSettings.ulIoBandwidthMax != 0)
4214 m->sv = SettingsVersion_v1_10;
4215 }
4216
4217 // VirtualBox 3.2 adds support for VRDP video channel
4218 if ( m->sv < SettingsVersion_v1_10
4219 && ( hardwareMachine.vrdpSettings.fVideoChannel
4220 )
4221 )
4222 m->sv = SettingsVersion_v1_10;
4223
4224 // VirtualBox 3.3 adds support for HD audio
4225 if ( m->sv < SettingsVersion_v1_11
4226 && ( hardwareMachine.audioAdapter.controllerType == AudioControllerType_HDA
4227 )
4228 )
4229 m->sv = SettingsVersion_v1_11;
4230}
4231
4232/**
4233 * Called from Main code to write a machine config file to disk. This builds a DOM tree from
4234 * the member variables and then writes the XML file; it throws xml::Error instances on errors,
4235 * in particular if the file cannot be written.
4236 */
4237void MachineConfigFile::write(const com::Utf8Str &strFilename)
4238{
4239 try
4240 {
4241 // createStubDocument() sets the settings version to at least 1.7; however,
4242 // we might need to enfore a later settings version if incompatible settings
4243 // are present:
4244 bumpSettingsVersionIfNeeded();
4245
4246 m->strFilename = strFilename;
4247 createStubDocument();
4248
4249 xml::ElementNode *pelmMachine = m->pelmRoot->createChild("Machine");
4250 buildMachineXML(*pelmMachine,
4251 MachineConfigFile::BuildMachineXML_IncludeSnapshots,
4252 // but not BuildMachineXML_WriteVboxVersionAttribute
4253 NULL); /* pllElementsWithUuidAttributes */
4254
4255 // now go write the XML
4256 xml::XmlFileWriter writer(*m->pDoc);
4257 writer.write(m->strFilename.c_str(), true /*fSafe*/);
4258
4259 m->fFileExists = true;
4260 clearDocument();
4261 }
4262 catch (...)
4263 {
4264 clearDocument();
4265 throw;
4266 }
4267}
4268
Note: See TracBrowser for help on using the repository browser.

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