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