Changeset 32398 in vbox for trunk/src/VBox/Main/DisplayUtils.cpp
- Timestamp:
- Sep 10, 2010 12:46:23 PM (14 years ago)
- File:
-
- 1 copied
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/VBox/Main/DisplayUtils.cpp
r32332 r32398 1 1 /* $Id$ */ 2 2 /** @file 3 * Implementation of I Machine in VBoxSVC.3 * Implementation of IDisplay helpers. 4 4 */ 5 5 6 6 /* 7 * Copyright (C) 20 06-2010 Oracle Corporation7 * Copyright (C) 2010 Oracle Corporation 8 8 * 9 9 * This file is part of VirtualBox Open Source Edition (OSE), as … … 16 16 */ 17 17 18 /* Make sure all the stdint.h macros are included - must come first! */ 19 #ifndef __STDC_LIMIT_MACROS 20 # define __STDC_LIMIT_MACROS 21 #endif 22 #ifndef __STDC_CONSTANT_MACROS 23 # define __STDC_CONSTANT_MACROS 24 #endif 18 #include <DisplayUtils.h> 25 19 26 #ifdef VBOX_WITH_SYS_V_IPC_SESSION_WATCHER 27 # include <errno.h> 28 # include <sys/types.h> 29 # include <sys/stat.h> 30 # include <sys/ipc.h> 31 # include <sys/sem.h> 32 #endif 20 #include <iprt/log.h> 21 #include <VBox/err.h> 22 #include <VBox/ssm.h> 33 23 34 #include "Logging.h" 35 #include "VirtualBoxImpl.h" 36 #include "MachineImpl.h" 37 #include "ProgressImpl.h" 38 #include "ProgressProxyImpl.h" 39 #include "MediumAttachmentImpl.h" 40 #include "MediumImpl.h" 41 #include "MediumLock.h" 42 #include "USBControllerImpl.h" 43 #include "HostImpl.h" 44 #include "SharedFolderImpl.h" 45 #include "GuestOSTypeImpl.h" 46 #include "VirtualBoxErrorInfoImpl.h" 47 #include "GuestImpl.h" 48 #include "StorageControllerImpl.h" 49 #include "DisplayImpl.h" 50 51 #ifdef VBOX_WITH_USB 52 # include "USBProxyService.h" 53 #endif 54 55 #include "AutoCaller.h" 56 #include "Performance.h" 57 58 #include <iprt/asm.h> 59 #include <iprt/path.h> 60 #include <iprt/dir.h> 61 #include <iprt/env.h> 62 #include <iprt/lockvalidator.h> 63 #include <iprt/process.h> 64 #include <iprt/cpp/utils.h> 65 #include <iprt/cpp/xml.h> /* xml::XmlFileWriter::s_psz*Suff. */ 66 #include <iprt/string.h> 67 68 #include <VBox/com/array.h> 69 70 #include <VBox/err.h> 71 #include <VBox/param.h> 72 #include <VBox/settings.h> 73 #include <VBox/ssm.h> 74 #include <VBox/feature.h> 75 76 #ifdef VBOX_WITH_GUEST_PROPS 77 # include <VBox/HostServices/GuestPropertySvc.h> 78 # include <VBox/com/array.h> 79 #endif 80 81 #include "VBox/com/MultiResult.h" 82 83 #include <algorithm> 84 85 #include <typeinfo> 86 87 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 88 # define HOSTSUFF_EXE ".exe" 89 #else /* !RT_OS_WINDOWS */ 90 # define HOSTSUFF_EXE "" 91 #endif /* !RT_OS_WINDOWS */ 92 93 // defines / prototypes 94 ///////////////////////////////////////////////////////////////////////////// 95 96 ///////////////////////////////////////////////////////////////////////////// 97 // Machine::Data structure 98 ///////////////////////////////////////////////////////////////////////////// 99 100 Machine::Data::Data() 101 { 102 mRegistered = FALSE; 103 pMachineConfigFile = NULL; 104 flModifications = 0; 105 mAccessible = FALSE; 106 /* mUuid is initialized in Machine::init() */ 107 108 mMachineState = MachineState_PoweredOff; 109 RTTimeNow(&mLastStateChange); 110 111 mMachineStateDeps = 0; 112 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI; 113 mMachineStateChangePending = 0; 114 115 mCurrentStateModified = TRUE; 116 mGuestPropertiesModified = FALSE; 117 118 mSession.mPid = NIL_RTPROCESS; 119 mSession.mState = SessionState_Unlocked; 120 } 121 122 Machine::Data::~Data() 123 { 124 if (mMachineStateDepsSem != NIL_RTSEMEVENTMULTI) 125 { 126 RTSemEventMultiDestroy(mMachineStateDepsSem); 127 mMachineStateDepsSem = NIL_RTSEMEVENTMULTI; 128 } 129 if (pMachineConfigFile) 130 { 131 delete pMachineConfigFile; 132 pMachineConfigFile = NULL; 133 } 134 } 135 136 ///////////////////////////////////////////////////////////////////////////// 137 // Machine::HWData structure 138 ///////////////////////////////////////////////////////////////////////////// 139 140 Machine::HWData::HWData() 141 { 142 /* default values for a newly created machine */ 143 mHWVersion = "2"; /** @todo get the default from the schema if that is possible. */ 144 mMemorySize = 128; 145 mCPUCount = 1; 146 mCPUHotPlugEnabled = false; 147 mMemoryBalloonSize = 0; 148 mPageFusionEnabled = false; 149 mVRAMSize = 8; 150 mAccelerate3DEnabled = false; 151 mAccelerate2DVideoEnabled = false; 152 mMonitorCount = 1; 153 mHWVirtExEnabled = true; 154 mHWVirtExNestedPagingEnabled = true; 155 #if HC_ARCH_BITS == 64 156 /* Default value decision pending. */ 157 mHWVirtExLargePagesEnabled = false; 158 #else 159 /* Not supported on 32 bits hosts. */ 160 mHWVirtExLargePagesEnabled = false; 161 #endif 162 mHWVirtExVPIDEnabled = true; 163 mHWVirtExForceEnabled = false; 164 #if defined(RT_OS_DARWIN) || defined(RT_OS_WINDOWS) 165 mHWVirtExExclusive = false; 166 #else 167 mHWVirtExExclusive = true; 168 #endif 169 #if HC_ARCH_BITS == 64 || defined(RT_OS_WINDOWS) || defined(RT_OS_DARWIN) 170 mPAEEnabled = true; 171 #else 172 mPAEEnabled = false; 173 #endif 174 mSyntheticCpu = false; 175 mHpetEnabled = false; 176 177 /* default boot order: floppy - DVD - HDD */ 178 mBootOrder[0] = DeviceType_Floppy; 179 mBootOrder[1] = DeviceType_DVD; 180 mBootOrder[2] = DeviceType_HardDisk; 181 for (size_t i = 3; i < RT_ELEMENTS(mBootOrder); ++i) 182 mBootOrder[i] = DeviceType_Null; 183 184 mClipboardMode = ClipboardMode_Bidirectional; 185 mGuestPropertyNotificationPatterns = ""; 186 187 mFirmwareType = FirmwareType_BIOS; 188 mKeyboardHidType = KeyboardHidType_PS2Keyboard; 189 mPointingHidType = PointingHidType_PS2Mouse; 190 mChipsetType = ChipsetType_PIIX3; 191 192 for (size_t i = 0; i < RT_ELEMENTS(mCPUAttached); i++) 193 mCPUAttached[i] = false; 194 195 mIoCacheEnabled = true; 196 mIoCacheSize = 5; /* 5MB */ 197 198 /* Maximum CPU priority by default. */ 199 mCpuPriority = 100; 200 } 201 202 Machine::HWData::~HWData() 203 { 204 } 205 206 ///////////////////////////////////////////////////////////////////////////// 207 // Machine::HDData structure 208 ///////////////////////////////////////////////////////////////////////////// 209 210 Machine::MediaData::MediaData() 211 { 212 } 213 214 Machine::MediaData::~MediaData() 215 { 216 } 217 218 ///////////////////////////////////////////////////////////////////////////// 219 // Machine class 220 ///////////////////////////////////////////////////////////////////////////// 221 222 // constructor / destructor 223 ///////////////////////////////////////////////////////////////////////////// 224 225 Machine::Machine() 226 : mGuestHAL(NULL), 227 mPeer(NULL), 228 mParent(NULL) 229 {} 230 231 Machine::~Machine() 232 {} 233 234 HRESULT Machine::FinalConstruct() 235 { 236 LogFlowThisFunc(("\n")); 237 return S_OK; 238 } 239 240 void Machine::FinalRelease() 241 { 242 LogFlowThisFunc(("\n")); 243 uninit(); 244 } 245 246 /** 247 * Initializes a new machine instance; this init() variant creates a new, empty machine. 248 * This gets called from VirtualBox::CreateMachine() or VirtualBox::CreateLegacyMachine(). 249 * 250 * @param aParent Associated parent object 251 * @param strConfigFile Local file system path to the VM settings file (can 252 * be relative to the VirtualBox config directory). 253 * @param strName name for the machine 254 * @param aId UUID for the new machine. 255 * @param aOsType Optional OS Type of this machine. 256 * @param aOverride |TRUE| to override VM config file existence checks. 257 * |FALSE| refuses to overwrite existing VM configs. 258 * @param aNameSync |TRUE| to automatically sync settings dir and file 259 * name with the machine name. |FALSE| is used for legacy 260 * machines where the file name is specified by the 261 * user and should never change. 262 * 263 * @return Success indicator. if not S_OK, the machine object is invalid 264 */ 265 HRESULT Machine::init(VirtualBox *aParent, 266 const Utf8Str &strConfigFile, 267 const Utf8Str &strName, 268 const Guid &aId, 269 GuestOSType *aOsType /* = NULL */, 270 BOOL aOverride /* = FALSE */, 271 BOOL aNameSync /* = TRUE */) 272 { 273 LogFlowThisFuncEnter(); 274 LogFlowThisFunc(("(Init_New) aConfigFile='%s'\n", strConfigFile.c_str())); 275 276 /* Enclose the state transition NotReady->InInit->Ready */ 277 AutoInitSpan autoInitSpan(this); 278 AssertReturn(autoInitSpan.isOk(), E_FAIL); 279 280 HRESULT rc = initImpl(aParent, strConfigFile); 281 if (FAILED(rc)) return rc; 282 283 rc = tryCreateMachineConfigFile(aOverride); 284 if (FAILED(rc)) return rc; 285 286 if (SUCCEEDED(rc)) 287 { 288 // create an empty machine config 289 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL); 290 291 rc = initDataAndChildObjects(); 292 } 293 294 if (SUCCEEDED(rc)) 295 { 296 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 297 mData->mAccessible = TRUE; 298 299 unconst(mData->mUuid) = aId; 300 301 mUserData->s.strName = strName; 302 mUserData->s.fNameSync = aNameSync; 303 304 /* initialize the default snapshots folder 305 * (note: depends on the name value set above!) */ 306 rc = COMSETTER(SnapshotFolder)(NULL); 307 AssertComRC(rc); 308 309 if (aOsType) 310 { 311 /* Store OS type */ 312 mUserData->s.strOsType = aOsType->id(); 313 314 /* Apply BIOS defaults */ 315 mBIOSSettings->applyDefaults(aOsType); 316 317 /* Apply network adapters defaults */ 318 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); ++slot) 319 mNetworkAdapters[slot]->applyDefaults(aOsType); 320 321 /* Apply serial port defaults */ 322 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); ++slot) 323 mSerialPorts[slot]->applyDefaults(aOsType); 324 } 325 326 /* commit all changes made during the initialization */ 327 commit(); 328 } 329 330 /* Confirm a successful initialization when it's the case */ 331 if (SUCCEEDED(rc)) 332 { 333 if (mData->mAccessible) 334 autoInitSpan.setSucceeded(); 335 else 336 autoInitSpan.setLimited(); 337 } 338 339 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool, rc=%08X\n", 340 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 341 mData->mRegistered, 342 mData->mAccessible, 343 rc)); 344 345 LogFlowThisFuncLeave(); 346 347 return rc; 348 } 349 350 /** 351 * Initializes a new instance with data from machine XML (formerly Init_Registered). 352 * Gets called in two modes: 353 * 354 * -- from VirtualBox::initMachines() during VirtualBox startup; in that case, the 355 * UUID is specified and we mark the machine as "registered"; 356 * 357 * -- from the public VirtualBox::OpenMachine() API, in which case the UUID is NULL 358 * and the machine remains unregistered until RegisterMachine() is called. 359 * 360 * @param aParent Associated parent object 361 * @param aConfigFile Local file system path to the VM settings file (can 362 * be relative to the VirtualBox config directory). 363 * @param aId UUID of the machine or NULL (see above). 364 * 365 * @return Success indicator. if not S_OK, the machine object is invalid 366 */ 367 HRESULT Machine::init(VirtualBox *aParent, 368 const Utf8Str &strConfigFile, 369 const Guid *aId) 370 { 371 LogFlowThisFuncEnter(); 372 LogFlowThisFunc(("(Init_Registered) aConfigFile='%s\n", strConfigFile.c_str())); 373 374 /* Enclose the state transition NotReady->InInit->Ready */ 375 AutoInitSpan autoInitSpan(this); 376 AssertReturn(autoInitSpan.isOk(), E_FAIL); 377 378 HRESULT rc = initImpl(aParent, strConfigFile); 379 if (FAILED(rc)) return rc; 380 381 if (aId) 382 { 383 // loading a registered VM: 384 unconst(mData->mUuid) = *aId; 385 mData->mRegistered = TRUE; 386 // now load the settings from XML: 387 rc = registeredInit(); 388 // this calls initDataAndChildObjects() and loadSettings() 389 } 390 else 391 { 392 // opening an unregistered VM (VirtualBox::OpenMachine()): 393 rc = initDataAndChildObjects(); 394 395 if (SUCCEEDED(rc)) 396 { 397 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 398 mData->mAccessible = TRUE; 399 400 try 401 { 402 // load and parse machine XML; this will throw on XML or logic errors 403 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull); 404 405 // use UUID from machine config 406 unconst(mData->mUuid) = mData->pMachineConfigFile->uuid; 407 408 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile); 409 if (FAILED(rc)) throw rc; 410 411 commit(); 412 } 413 catch (HRESULT err) 414 { 415 /* we assume that error info is set by the thrower */ 416 rc = err; 417 } 418 catch (...) 419 { 420 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 421 } 422 } 423 } 424 425 /* Confirm a successful initialization when it's the case */ 426 if (SUCCEEDED(rc)) 427 { 428 if (mData->mAccessible) 429 autoInitSpan.setSucceeded(); 430 else 431 { 432 autoInitSpan.setLimited(); 433 434 // uninit media from this machine's media registry, or else 435 // reloading the settings will fail 436 mParent->unregisterMachineMedia(getId()); 437 } 438 } 439 440 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool " 441 "rc=%08X\n", 442 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 443 mData->mRegistered, mData->mAccessible, rc)); 444 445 LogFlowThisFuncLeave(); 446 447 return rc; 448 } 449 450 /** 451 * Initializes a new instance from a machine config that is already in memory 452 * (import OVF case). Since we are importing, the UUID in the machine 453 * config is ignored and we always generate a fresh one. 454 * 455 * @param strName Name for the new machine; this overrides what is specified in config and is used 456 * for the settings file as well. 457 * @param config Machine configuration loaded and parsed from XML. 458 * 459 * @return Success indicator. if not S_OK, the machine object is invalid 460 */ 461 HRESULT Machine::init(VirtualBox *aParent, 462 const Utf8Str &strName, 463 const settings::MachineConfigFile &config) 464 { 465 LogFlowThisFuncEnter(); 466 467 /* Enclose the state transition NotReady->InInit->Ready */ 468 AutoInitSpan autoInitSpan(this); 469 AssertReturn(autoInitSpan.isOk(), E_FAIL); 470 471 Utf8Str strConfigFile; 472 aParent->getDefaultMachineFolder(strConfigFile); 473 strConfigFile.append(RTPATH_DELIMITER); 474 strConfigFile.append(strName); 475 strConfigFile.append(RTPATH_DELIMITER); 476 strConfigFile.append(strName); 477 strConfigFile.append(".xml"); 478 479 HRESULT rc = initImpl(aParent, strConfigFile); 480 if (FAILED(rc)) return rc; 481 482 rc = tryCreateMachineConfigFile(FALSE /* aOverride */); 483 if (FAILED(rc)) return rc; 484 485 rc = initDataAndChildObjects(); 486 487 if (SUCCEEDED(rc)) 488 { 489 // set to true now to cause uninit() to call uninitDataAndChildObjects() on failure 490 mData->mAccessible = TRUE; 491 492 // create empty machine config for instance data 493 mData->pMachineConfigFile = new settings::MachineConfigFile(NULL); 494 495 // generate fresh UUID, ignore machine config 496 unconst(mData->mUuid).create(); 497 498 rc = loadMachineDataFromSettings(config); 499 500 // override VM name as well, it may be different 501 mUserData->s.strName = strName; 502 503 /* commit all changes made during the initialization */ 504 if (SUCCEEDED(rc)) 505 commit(); 506 } 507 508 /* Confirm a successful initialization when it's the case */ 509 if (SUCCEEDED(rc)) 510 { 511 if (mData->mAccessible) 512 autoInitSpan.setSucceeded(); 513 else 514 { 515 autoInitSpan.setLimited(); 516 517 // uninit media from this machine's media registry, or else 518 // reloading the settings will fail 519 mParent->unregisterMachineMedia(getId()); 520 } 521 } 522 523 LogFlowThisFunc(("mName='%s', mRegistered=%RTbool, mAccessible=%RTbool " 524 "rc=%08X\n", 525 !!mUserData ? mUserData->s.strName.c_str() : "NULL", 526 mData->mRegistered, mData->mAccessible, rc)); 527 528 LogFlowThisFuncLeave(); 529 530 return rc; 531 } 532 533 /** 534 * Shared code between the various init() implementations. 535 * @param aParent 536 * @return 537 */ 538 HRESULT Machine::initImpl(VirtualBox *aParent, 539 const Utf8Str &strConfigFile) 540 { 541 LogFlowThisFuncEnter(); 542 543 AssertReturn(aParent, E_INVALIDARG); 544 AssertReturn(!strConfigFile.isEmpty(), E_INVALIDARG); 545 546 HRESULT rc = S_OK; 547 548 /* share the parent weakly */ 549 unconst(mParent) = aParent; 550 551 /* allocate the essential machine data structure (the rest will be 552 * allocated later by initDataAndChildObjects() */ 553 mData.allocate(); 554 555 /* memorize the config file name (as provided) */ 556 mData->m_strConfigFile = strConfigFile; 557 558 /* get the full file name */ 559 int vrc1 = mParent->calculateFullPath(strConfigFile, mData->m_strConfigFileFull); 560 if (RT_FAILURE(vrc1)) 561 return setError(VBOX_E_FILE_ERROR, 562 tr("Invalid machine settings file name '%s' (%Rrc)"), 563 strConfigFile.c_str(), 564 vrc1); 565 566 LogFlowThisFuncLeave(); 567 568 return rc; 569 } 570 571 /** 572 * Tries to create a machine settings file in the path stored in the machine 573 * instance data. Used when a new machine is created to fail gracefully if 574 * the settings file could not be written (e.g. because machine dir is read-only). 575 * @return 576 */ 577 HRESULT Machine::tryCreateMachineConfigFile(BOOL aOverride) 578 { 579 HRESULT rc = S_OK; 580 581 // when we create a new machine, we must be able to create the settings file 582 RTFILE f = NIL_RTFILE; 583 int vrc = RTFileOpen(&f, mData->m_strConfigFileFull.c_str(), RTFILE_O_READ | RTFILE_O_OPEN | RTFILE_O_DENY_NONE); 584 if ( RT_SUCCESS(vrc) 585 || vrc == VERR_SHARING_VIOLATION 586 ) 587 { 588 if (RT_SUCCESS(vrc)) 589 RTFileClose(f); 590 if (!aOverride) 591 rc = setError(VBOX_E_FILE_ERROR, 592 tr("Machine settings file '%s' already exists"), 593 mData->m_strConfigFileFull.c_str()); 594 else 595 { 596 /* try to delete the config file, as otherwise the creation 597 * of a new settings file will fail. */ 598 int vrc2 = RTFileDelete(mData->m_strConfigFileFull.c_str()); 599 if (RT_FAILURE(vrc2)) 600 rc = setError(VBOX_E_FILE_ERROR, 601 tr("Could not delete the existing settings file '%s' (%Rrc)"), 602 mData->m_strConfigFileFull.c_str(), vrc2); 603 } 604 } 605 else if ( vrc != VERR_FILE_NOT_FOUND 606 && vrc != VERR_PATH_NOT_FOUND 607 ) 608 rc = setError(VBOX_E_FILE_ERROR, 609 tr("Invalid machine settings file name '%s' (%Rrc)"), 610 mData->m_strConfigFileFull.c_str(), 611 vrc); 612 return rc; 613 } 614 615 /** 616 * Initializes the registered machine by loading the settings file. 617 * This method is separated from #init() in order to make it possible to 618 * retry the operation after VirtualBox startup instead of refusing to 619 * startup the whole VirtualBox server in case if the settings file of some 620 * registered VM is invalid or inaccessible. 621 * 622 * @note Must be always called from this object's write lock 623 * (unless called from #init() that doesn't need any locking). 624 * @note Locks the mUSBController method for writing. 625 * @note Subclasses must not call this method. 626 */ 627 HRESULT Machine::registeredInit() 628 { 629 AssertReturn(!isSessionMachine(), E_FAIL); 630 AssertReturn(!isSnapshotMachine(), E_FAIL); 631 AssertReturn(!mData->mUuid.isEmpty(), E_FAIL); 632 AssertReturn(!mData->mAccessible, E_FAIL); 633 634 HRESULT rc = initDataAndChildObjects(); 635 636 if (SUCCEEDED(rc)) 637 { 638 /* Temporarily reset the registered flag in order to let setters 639 * potentially called from loadSettings() succeed (isMutable() used in 640 * all setters will return FALSE for a Machine instance if mRegistered 641 * is TRUE). */ 642 mData->mRegistered = FALSE; 643 644 try 645 { 646 // load and parse machine XML; this will throw on XML or logic errors 647 mData->pMachineConfigFile = new settings::MachineConfigFile(&mData->m_strConfigFileFull); 648 649 if (mData->mUuid != mData->pMachineConfigFile->uuid) 650 throw setError(E_FAIL, 651 tr("Machine UUID {%RTuuid} in '%s' doesn't match its UUID {%s} in the registry file '%s'"), 652 mData->pMachineConfigFile->uuid.raw(), 653 mData->m_strConfigFileFull.c_str(), 654 mData->mUuid.toString().c_str(), 655 mParent->settingsFilePath().c_str()); 656 657 rc = loadMachineDataFromSettings(*mData->pMachineConfigFile); 658 if (FAILED(rc)) throw rc; 659 } 660 catch (HRESULT err) 661 { 662 /* we assume that error info is set by the thrower */ 663 rc = err; 664 } 665 catch (...) 666 { 667 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 668 } 669 670 /* Restore the registered flag (even on failure) */ 671 mData->mRegistered = TRUE; 672 } 673 674 if (SUCCEEDED(rc)) 675 { 676 /* Set mAccessible to TRUE only if we successfully locked and loaded 677 * the settings file */ 678 mData->mAccessible = TRUE; 679 680 /* commit all changes made during loading the settings file */ 681 commit(); // @todo r=dj why do we need a commit during init?!? this is very expensive 682 } 683 else 684 { 685 /* If the machine is registered, then, instead of returning a 686 * failure, we mark it as inaccessible and set the result to 687 * success to give it a try later */ 688 689 /* fetch the current error info */ 690 mData->mAccessError = com::ErrorInfo(); 691 LogWarning(("Machine {%RTuuid} is inaccessible! [%ls]\n", 692 mData->mUuid.raw(), 693 mData->mAccessError.getText().raw())); 694 695 /* rollback all changes */ 696 rollback(false /* aNotify */); 697 698 // uninit media from this machine's media registry, or else 699 // reloading the settings will fail 700 mParent->unregisterMachineMedia(getId()); 701 702 /* uninitialize the common part to make sure all data is reset to 703 * default (null) values */ 704 uninitDataAndChildObjects(); 705 706 rc = S_OK; 707 } 708 709 return rc; 710 } 711 712 /** 713 * Uninitializes the instance. 714 * Called either from FinalRelease() or by the parent when it gets destroyed. 715 * 716 * @note The caller of this method must make sure that this object 717 * a) doesn't have active callers on the current thread and b) is not locked 718 * by the current thread; otherwise uninit() will hang either a) due to 719 * AutoUninitSpan waiting for a number of calls to drop to zero or b) due to 720 * a dead-lock caused by this thread waiting for all callers on the other 721 * threads are done but preventing them from doing so by holding a lock. 722 */ 723 void Machine::uninit() 724 { 725 LogFlowThisFuncEnter(); 726 727 Assert(!isWriteLockOnCurrentThread()); 728 729 /* Enclose the state transition Ready->InUninit->NotReady */ 730 AutoUninitSpan autoUninitSpan(this); 731 if (autoUninitSpan.uninitDone()) 732 return; 733 734 Assert(!isSnapshotMachine()); 735 Assert(!isSessionMachine()); 736 Assert(!!mData); 737 738 LogFlowThisFunc(("initFailed()=%d\n", autoUninitSpan.initFailed())); 739 LogFlowThisFunc(("mRegistered=%d\n", mData->mRegistered)); 740 741 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 742 743 if (!mData->mSession.mMachine.isNull()) 744 { 745 /* Theoretically, this can only happen if the VirtualBox server has been 746 * terminated while there were clients running that owned open direct 747 * sessions. Since in this case we are definitely called by 748 * VirtualBox::uninit(), we may be sure that SessionMachine::uninit() 749 * won't happen on the client watcher thread (because it does 750 * VirtualBox::addCaller() for the duration of the 751 * SessionMachine::checkForDeath() call, so that VirtualBox::uninit() 752 * cannot happen until the VirtualBox caller is released). This is 753 * important, because SessionMachine::uninit() cannot correctly operate 754 * after we return from this method (it expects the Machine instance is 755 * still valid). We'll call it ourselves below. 756 */ 757 LogWarningThisFunc(("Session machine is not NULL (%p), the direct session is still open!\n", 758 (SessionMachine*)mData->mSession.mMachine)); 759 760 if (Global::IsOnlineOrTransient(mData->mMachineState)) 761 { 762 LogWarningThisFunc(("Setting state to Aborted!\n")); 763 /* set machine state using SessionMachine reimplementation */ 764 static_cast<Machine*>(mData->mSession.mMachine)->setMachineState(MachineState_Aborted); 765 } 766 767 /* 768 * Uninitialize SessionMachine using public uninit() to indicate 769 * an unexpected uninitialization. 770 */ 771 mData->mSession.mMachine->uninit(); 772 /* SessionMachine::uninit() must set mSession.mMachine to null */ 773 Assert(mData->mSession.mMachine.isNull()); 774 } 775 776 // uninit media from this machine's media registry, if they're still there 777 mParent->unregisterMachineMedia(getId()); 778 779 /* the lock is no more necessary (SessionMachine is uninitialized) */ 780 alock.leave(); 781 782 // has machine been modified? 783 if (mData->flModifications) 784 { 785 LogWarningThisFunc(("Discarding unsaved settings changes!\n")); 786 rollback(false /* aNotify */); 787 } 788 789 if (mData->mAccessible) 790 uninitDataAndChildObjects(); 791 792 /* free the essential data structure last */ 793 mData.free(); 794 795 LogFlowThisFuncLeave(); 796 } 797 798 // IMachine properties 799 ///////////////////////////////////////////////////////////////////////////// 800 801 STDMETHODIMP Machine::COMGETTER(Parent)(IVirtualBox **aParent) 802 { 803 CheckComArgOutPointerValid(aParent); 804 805 AutoLimitedCaller autoCaller(this); 806 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 807 808 /* mParent is constant during life time, no need to lock */ 809 ComObjPtr<VirtualBox> pVirtualBox(mParent); 810 pVirtualBox.queryInterfaceTo(aParent); 811 812 return S_OK; 813 } 814 815 STDMETHODIMP Machine::COMGETTER(Accessible)(BOOL *aAccessible) 816 { 817 CheckComArgOutPointerValid(aAccessible); 818 819 AutoLimitedCaller autoCaller(this); 820 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 821 822 LogFlowThisFunc(("ENTER\n")); 823 824 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 825 826 HRESULT rc = S_OK; 827 828 if (!mData->mAccessible) 829 { 830 /* try to initialize the VM once more if not accessible */ 831 832 AutoReinitSpan autoReinitSpan(this); 833 AssertReturn(autoReinitSpan.isOk(), E_FAIL); 834 835 #ifdef DEBUG 836 LogFlowThisFunc(("Dumping media backreferences\n")); 837 mParent->dumpAllBackRefs(); 838 #endif 839 840 if (mData->pMachineConfigFile) 841 { 842 // reset the XML file to force loadSettings() (called from registeredInit()) 843 // to parse it again; the file might have changed 844 delete mData->pMachineConfigFile; 845 mData->pMachineConfigFile = NULL; 846 } 847 848 rc = registeredInit(); 849 850 if (SUCCEEDED(rc) && mData->mAccessible) 851 { 852 autoReinitSpan.setSucceeded(); 853 854 /* make sure interesting parties will notice the accessibility 855 * state change */ 856 mParent->onMachineStateChange(mData->mUuid, mData->mMachineState); 857 mParent->onMachineDataChange(mData->mUuid); 858 } 859 } 860 861 if (SUCCEEDED(rc)) 862 *aAccessible = mData->mAccessible; 863 864 LogFlowThisFuncLeave(); 865 866 return rc; 867 } 868 869 STDMETHODIMP Machine::COMGETTER(AccessError)(IVirtualBoxErrorInfo **aAccessError) 870 { 871 CheckComArgOutPointerValid(aAccessError); 872 873 AutoLimitedCaller autoCaller(this); 874 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 875 876 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 877 878 if (mData->mAccessible || !mData->mAccessError.isBasicAvailable()) 879 { 880 /* return shortly */ 881 aAccessError = NULL; 882 return S_OK; 883 } 884 885 HRESULT rc = S_OK; 886 887 ComObjPtr<VirtualBoxErrorInfo> errorInfo; 888 rc = errorInfo.createObject(); 889 if (SUCCEEDED(rc)) 890 { 891 errorInfo->init(mData->mAccessError.getResultCode(), 892 mData->mAccessError.getInterfaceID(), 893 Utf8Str(mData->mAccessError.getComponent()).c_str(), 894 Utf8Str(mData->mAccessError.getText())); 895 rc = errorInfo.queryInterfaceTo(aAccessError); 896 } 897 898 return rc; 899 } 900 901 STDMETHODIMP Machine::COMGETTER(Name)(BSTR *aName) 902 { 903 CheckComArgOutPointerValid(aName); 904 905 AutoCaller autoCaller(this); 906 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 907 908 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 909 910 mUserData->s.strName.cloneTo(aName); 911 912 return S_OK; 913 } 914 915 STDMETHODIMP Machine::COMSETTER(Name)(IN_BSTR aName) 916 { 917 CheckComArgStrNotEmptyOrNull(aName); 918 919 AutoCaller autoCaller(this); 920 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 921 922 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 923 924 HRESULT rc = checkStateDependency(MutableStateDep); 925 if (FAILED(rc)) return rc; 926 927 setModified(IsModified_MachineData); 928 mUserData.backup(); 929 mUserData->s.strName = aName; 930 931 return S_OK; 932 } 933 934 STDMETHODIMP Machine::COMGETTER(Description)(BSTR *aDescription) 935 { 936 CheckComArgOutPointerValid(aDescription); 937 938 AutoCaller autoCaller(this); 939 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 940 941 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 942 943 mUserData->s.strDescription.cloneTo(aDescription); 944 945 return S_OK; 946 } 947 948 STDMETHODIMP Machine::COMSETTER(Description)(IN_BSTR aDescription) 949 { 950 AutoCaller autoCaller(this); 951 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 952 953 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 954 955 HRESULT rc = checkStateDependency(MutableStateDep); 956 if (FAILED(rc)) return rc; 957 958 setModified(IsModified_MachineData); 959 mUserData.backup(); 960 mUserData->s.strDescription = aDescription; 961 962 return S_OK; 963 } 964 965 STDMETHODIMP Machine::COMGETTER(Id)(BSTR *aId) 966 { 967 CheckComArgOutPointerValid(aId); 968 969 AutoLimitedCaller autoCaller(this); 970 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 971 972 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 973 974 mData->mUuid.toUtf16().cloneTo(aId); 975 976 return S_OK; 977 } 978 979 STDMETHODIMP Machine::COMGETTER(OSTypeId)(BSTR *aOSTypeId) 980 { 981 CheckComArgOutPointerValid(aOSTypeId); 982 983 AutoCaller autoCaller(this); 984 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 985 986 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 987 988 mUserData->s.strOsType.cloneTo(aOSTypeId); 989 990 return S_OK; 991 } 992 993 STDMETHODIMP Machine::COMSETTER(OSTypeId)(IN_BSTR aOSTypeId) 994 { 995 CheckComArgStrNotEmptyOrNull(aOSTypeId); 996 997 AutoCaller autoCaller(this); 998 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 999 1000 /* look up the object by Id to check it is valid */ 1001 ComPtr<IGuestOSType> guestOSType; 1002 HRESULT rc = mParent->GetGuestOSType(aOSTypeId, guestOSType.asOutParam()); 1003 if (FAILED(rc)) return rc; 1004 1005 /* when setting, always use the "etalon" value for consistency -- lookup 1006 * by ID is case-insensitive and the input value may have different case */ 1007 Bstr osTypeId; 1008 rc = guestOSType->COMGETTER(Id)(osTypeId.asOutParam()); 1009 if (FAILED(rc)) return rc; 1010 1011 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1012 1013 rc = checkStateDependency(MutableStateDep); 1014 if (FAILED(rc)) return rc; 1015 1016 setModified(IsModified_MachineData); 1017 mUserData.backup(); 1018 mUserData->s.strOsType = osTypeId; 1019 1020 return S_OK; 1021 } 1022 1023 1024 STDMETHODIMP Machine::COMGETTER(FirmwareType)(FirmwareType_T *aFirmwareType) 1025 { 1026 CheckComArgOutPointerValid(aFirmwareType); 1027 1028 AutoCaller autoCaller(this); 1029 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1030 1031 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1032 1033 *aFirmwareType = mHWData->mFirmwareType; 1034 1035 return S_OK; 1036 } 1037 1038 STDMETHODIMP Machine::COMSETTER(FirmwareType)(FirmwareType_T aFirmwareType) 1039 { 1040 AutoCaller autoCaller(this); 1041 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1042 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1043 1044 int rc = checkStateDependency(MutableStateDep); 1045 if (FAILED(rc)) return rc; 1046 1047 setModified(IsModified_MachineData); 1048 mHWData.backup(); 1049 mHWData->mFirmwareType = aFirmwareType; 1050 1051 return S_OK; 1052 } 1053 1054 STDMETHODIMP Machine::COMGETTER(KeyboardHidType)(KeyboardHidType_T *aKeyboardHidType) 1055 { 1056 CheckComArgOutPointerValid(aKeyboardHidType); 1057 1058 AutoCaller autoCaller(this); 1059 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1060 1061 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1062 1063 *aKeyboardHidType = mHWData->mKeyboardHidType; 1064 1065 return S_OK; 1066 } 1067 1068 STDMETHODIMP Machine::COMSETTER(KeyboardHidType)(KeyboardHidType_T aKeyboardHidType) 1069 { 1070 AutoCaller autoCaller(this); 1071 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1073 1074 int rc = checkStateDependency(MutableStateDep); 1075 if (FAILED(rc)) return rc; 1076 1077 setModified(IsModified_MachineData); 1078 mHWData.backup(); 1079 mHWData->mKeyboardHidType = aKeyboardHidType; 1080 1081 return S_OK; 1082 } 1083 1084 STDMETHODIMP Machine::COMGETTER(PointingHidType)(PointingHidType_T *aPointingHidType) 1085 { 1086 CheckComArgOutPointerValid(aPointingHidType); 1087 1088 AutoCaller autoCaller(this); 1089 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1090 1091 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1092 1093 *aPointingHidType = mHWData->mPointingHidType; 1094 1095 return S_OK; 1096 } 1097 1098 STDMETHODIMP Machine::COMSETTER(PointingHidType)(PointingHidType_T aPointingHidType) 1099 { 1100 AutoCaller autoCaller(this); 1101 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1102 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1103 1104 int rc = checkStateDependency(MutableStateDep); 1105 if (FAILED(rc)) return rc; 1106 1107 setModified(IsModified_MachineData); 1108 mHWData.backup(); 1109 mHWData->mPointingHidType = aPointingHidType; 1110 1111 return S_OK; 1112 } 1113 1114 STDMETHODIMP Machine::COMGETTER(ChipsetType)(ChipsetType_T *aChipsetType) 1115 { 1116 CheckComArgOutPointerValid(aChipsetType); 1117 1118 AutoCaller autoCaller(this); 1119 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1120 1121 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1122 1123 *aChipsetType = mHWData->mChipsetType; 1124 1125 return S_OK; 1126 } 1127 1128 STDMETHODIMP Machine::COMSETTER(ChipsetType)(ChipsetType_T aChipsetType) 1129 { 1130 AutoCaller autoCaller(this); 1131 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1132 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1133 1134 int rc = checkStateDependency(MutableStateDep); 1135 if (FAILED(rc)) return rc; 1136 1137 setModified(IsModified_MachineData); 1138 mHWData.backup(); 1139 mHWData->mChipsetType = aChipsetType; 1140 1141 return S_OK; 1142 } 1143 1144 STDMETHODIMP Machine::COMGETTER(HardwareVersion)(BSTR *aHWVersion) 1145 { 1146 if (!aHWVersion) 1147 return E_POINTER; 1148 1149 AutoCaller autoCaller(this); 1150 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1151 1152 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1153 1154 mHWData->mHWVersion.cloneTo(aHWVersion); 1155 1156 return S_OK; 1157 } 1158 1159 STDMETHODIMP Machine::COMSETTER(HardwareVersion)(IN_BSTR aHWVersion) 1160 { 1161 /* check known version */ 1162 Utf8Str hwVersion = aHWVersion; 1163 if ( hwVersion.compare("1") != 0 1164 && hwVersion.compare("2") != 0) 1165 return setError(E_INVALIDARG, 1166 tr("Invalid hardware version: %ls\n"), aHWVersion); 1167 1168 AutoCaller autoCaller(this); 1169 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1170 1171 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1172 1173 HRESULT rc = checkStateDependency(MutableStateDep); 1174 if (FAILED(rc)) return rc; 1175 1176 setModified(IsModified_MachineData); 1177 mHWData.backup(); 1178 mHWData->mHWVersion = hwVersion; 1179 1180 return S_OK; 1181 } 1182 1183 STDMETHODIMP Machine::COMGETTER(HardwareUUID)(BSTR *aUUID) 1184 { 1185 CheckComArgOutPointerValid(aUUID); 1186 1187 AutoCaller autoCaller(this); 1188 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1189 1190 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1191 1192 if (!mHWData->mHardwareUUID.isEmpty()) 1193 mHWData->mHardwareUUID.toUtf16().cloneTo(aUUID); 1194 else 1195 mData->mUuid.toUtf16().cloneTo(aUUID); 1196 1197 return S_OK; 1198 } 1199 1200 STDMETHODIMP Machine::COMSETTER(HardwareUUID)(IN_BSTR aUUID) 1201 { 1202 Guid hardwareUUID(aUUID); 1203 if (hardwareUUID.isEmpty()) 1204 return E_INVALIDARG; 1205 1206 AutoCaller autoCaller(this); 1207 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1208 1209 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1210 1211 HRESULT rc = checkStateDependency(MutableStateDep); 1212 if (FAILED(rc)) return rc; 1213 1214 setModified(IsModified_MachineData); 1215 mHWData.backup(); 1216 if (hardwareUUID == mData->mUuid) 1217 mHWData->mHardwareUUID.clear(); 1218 else 1219 mHWData->mHardwareUUID = hardwareUUID; 1220 1221 return S_OK; 1222 } 1223 1224 STDMETHODIMP Machine::COMGETTER(MemorySize)(ULONG *memorySize) 1225 { 1226 if (!memorySize) 1227 return E_POINTER; 1228 1229 AutoCaller autoCaller(this); 1230 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1231 1232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1233 1234 *memorySize = mHWData->mMemorySize; 1235 1236 return S_OK; 1237 } 1238 1239 STDMETHODIMP Machine::COMSETTER(MemorySize)(ULONG memorySize) 1240 { 1241 /* check RAM limits */ 1242 if ( memorySize < MM_RAM_MIN_IN_MB 1243 || memorySize > MM_RAM_MAX_IN_MB 1244 ) 1245 return setError(E_INVALIDARG, 1246 tr("Invalid RAM size: %lu MB (must be in range [%lu, %lu] MB)"), 1247 memorySize, MM_RAM_MIN_IN_MB, MM_RAM_MAX_IN_MB); 1248 1249 AutoCaller autoCaller(this); 1250 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1251 1252 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1253 1254 HRESULT rc = checkStateDependency(MutableStateDep); 1255 if (FAILED(rc)) return rc; 1256 1257 setModified(IsModified_MachineData); 1258 mHWData.backup(); 1259 mHWData->mMemorySize = memorySize; 1260 1261 return S_OK; 1262 } 1263 1264 STDMETHODIMP Machine::COMGETTER(CPUCount)(ULONG *CPUCount) 1265 { 1266 if (!CPUCount) 1267 return E_POINTER; 1268 1269 AutoCaller autoCaller(this); 1270 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1271 1272 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1273 1274 *CPUCount = mHWData->mCPUCount; 1275 1276 return S_OK; 1277 } 1278 1279 STDMETHODIMP Machine::COMSETTER(CPUCount)(ULONG CPUCount) 1280 { 1281 /* check CPU limits */ 1282 if ( CPUCount < SchemaDefs::MinCPUCount 1283 || CPUCount > SchemaDefs::MaxCPUCount 1284 ) 1285 return setError(E_INVALIDARG, 1286 tr("Invalid virtual CPU count: %lu (must be in range [%lu, %lu])"), 1287 CPUCount, SchemaDefs::MinCPUCount, SchemaDefs::MaxCPUCount); 1288 1289 AutoCaller autoCaller(this); 1290 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1291 1292 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1293 1294 /* We cant go below the current number of CPUs if hotplug is enabled*/ 1295 if (mHWData->mCPUHotPlugEnabled) 1296 { 1297 for (unsigned idx = CPUCount; idx < SchemaDefs::MaxCPUCount; idx++) 1298 { 1299 if (mHWData->mCPUAttached[idx]) 1300 return setError(E_INVALIDARG, 1301 tr(": %lu (must be higher than or equal to %lu)"), 1302 CPUCount, idx+1); 1303 } 1304 } 1305 1306 HRESULT rc = checkStateDependency(MutableStateDep); 1307 if (FAILED(rc)) return rc; 1308 1309 setModified(IsModified_MachineData); 1310 mHWData.backup(); 1311 mHWData->mCPUCount = CPUCount; 1312 1313 return S_OK; 1314 } 1315 1316 STDMETHODIMP Machine::COMGETTER(CPUPriority)(ULONG *aPriority) 1317 { 1318 if (!aPriority) 1319 return E_POINTER; 1320 1321 AutoCaller autoCaller(this); 1322 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1323 1324 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1325 1326 *aPriority = mHWData->mCpuPriority; 1327 1328 return S_OK; 1329 } 1330 1331 STDMETHODIMP Machine::COMSETTER(CPUPriority)(ULONG aPriority) 1332 { 1333 HRESULT rc = S_OK; 1334 1335 /* check priority limits */ 1336 if ( aPriority < 1 1337 || aPriority > 100 1338 ) 1339 return setError(E_INVALIDARG, 1340 tr("Invalid CPU priority: %lu (must be in range [%lu, %lu])"), 1341 aPriority, 1, 100); 1342 1343 AutoCaller autoCaller(this); 1344 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1345 1346 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1347 1348 alock.release(); 1349 rc = onCPUPriorityChange(aPriority); 1350 alock.acquire(); 1351 if (FAILED(rc)) return rc; 1352 1353 setModified(IsModified_MachineData); 1354 mHWData.backup(); 1355 mHWData->mCpuPriority = aPriority; 1356 1357 /* Save settings if online - todo why is this required?? */ 1358 if (Global::IsOnline(mData->mMachineState)) 1359 saveSettings(NULL); 1360 1361 return S_OK; 1362 } 1363 1364 1365 STDMETHODIMP Machine::COMGETTER(CPUHotPlugEnabled)(BOOL *enabled) 1366 { 1367 if (!enabled) 1368 return E_POINTER; 1369 1370 AutoCaller autoCaller(this); 1371 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1372 1373 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1374 1375 *enabled = mHWData->mCPUHotPlugEnabled; 1376 1377 return S_OK; 1378 } 1379 1380 STDMETHODIMP Machine::COMSETTER(CPUHotPlugEnabled)(BOOL enabled) 1381 { 1382 HRESULT rc = S_OK; 1383 1384 AutoCaller autoCaller(this); 1385 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1386 1387 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1388 1389 rc = checkStateDependency(MutableStateDep); 1390 if (FAILED(rc)) return rc; 1391 1392 if (mHWData->mCPUHotPlugEnabled != enabled) 1393 { 1394 if (enabled) 1395 { 1396 setModified(IsModified_MachineData); 1397 mHWData.backup(); 1398 1399 /* Add the amount of CPUs currently attached */ 1400 for (unsigned i = 0; i < mHWData->mCPUCount; i++) 1401 { 1402 mHWData->mCPUAttached[i] = true; 1403 } 1404 } 1405 else 1406 { 1407 /* 1408 * We can disable hotplug only if the amount of maximum CPUs is equal 1409 * to the amount of attached CPUs 1410 */ 1411 unsigned cCpusAttached = 0; 1412 unsigned iHighestId = 0; 1413 1414 for (unsigned i = 0; i < SchemaDefs::MaxCPUCount; i++) 1415 { 1416 if (mHWData->mCPUAttached[i]) 1417 { 1418 cCpusAttached++; 1419 iHighestId = i; 1420 } 1421 } 1422 1423 if ( (cCpusAttached != mHWData->mCPUCount) 1424 || (iHighestId >= mHWData->mCPUCount)) 1425 return setError(E_INVALIDARG, 1426 tr("CPU hotplugging can't be disabled because the maximum number of CPUs is not equal to the amount of CPUs attached\n")); 1427 1428 setModified(IsModified_MachineData); 1429 mHWData.backup(); 1430 } 1431 } 1432 1433 mHWData->mCPUHotPlugEnabled = enabled; 1434 1435 return rc; 1436 } 1437 1438 STDMETHODIMP Machine::COMGETTER(HpetEnabled)(BOOL *enabled) 1439 { 1440 CheckComArgOutPointerValid(enabled); 1441 1442 AutoCaller autoCaller(this); 1443 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1444 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1445 1446 *enabled = mHWData->mHpetEnabled; 1447 1448 return S_OK; 1449 } 1450 1451 STDMETHODIMP Machine::COMSETTER(HpetEnabled)(BOOL enabled) 1452 { 1453 HRESULT rc = S_OK; 1454 1455 AutoCaller autoCaller(this); 1456 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1458 1459 rc = checkStateDependency(MutableStateDep); 1460 if (FAILED(rc)) return rc; 1461 1462 setModified(IsModified_MachineData); 1463 mHWData.backup(); 1464 1465 mHWData->mHpetEnabled = enabled; 1466 1467 return rc; 1468 } 1469 1470 STDMETHODIMP Machine::COMGETTER(VRAMSize)(ULONG *memorySize) 1471 { 1472 if (!memorySize) 1473 return E_POINTER; 1474 1475 AutoCaller autoCaller(this); 1476 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1477 1478 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1479 1480 *memorySize = mHWData->mVRAMSize; 1481 1482 return S_OK; 1483 } 1484 1485 STDMETHODIMP Machine::COMSETTER(VRAMSize)(ULONG memorySize) 1486 { 1487 /* check VRAM limits */ 1488 if (memorySize < SchemaDefs::MinGuestVRAM || 1489 memorySize > SchemaDefs::MaxGuestVRAM) 1490 return setError(E_INVALIDARG, 1491 tr("Invalid VRAM size: %lu MB (must be in range [%lu, %lu] MB)"), 1492 memorySize, SchemaDefs::MinGuestVRAM, SchemaDefs::MaxGuestVRAM); 1493 1494 AutoCaller autoCaller(this); 1495 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1496 1497 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1498 1499 HRESULT rc = checkStateDependency(MutableStateDep); 1500 if (FAILED(rc)) return rc; 1501 1502 setModified(IsModified_MachineData); 1503 mHWData.backup(); 1504 mHWData->mVRAMSize = memorySize; 1505 1506 return S_OK; 1507 } 1508 1509 /** @todo this method should not be public */ 1510 STDMETHODIMP Machine::COMGETTER(MemoryBalloonSize)(ULONG *memoryBalloonSize) 1511 { 1512 if (!memoryBalloonSize) 1513 return E_POINTER; 1514 1515 AutoCaller autoCaller(this); 1516 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1517 1518 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1519 1520 *memoryBalloonSize = mHWData->mMemoryBalloonSize; 1521 1522 return S_OK; 1523 } 1524 1525 /** 1526 * Set the memory balloon size. 1527 * 1528 * This method is also called from IGuest::COMSETTER(MemoryBalloonSize) so 1529 * we have to make sure that we never call IGuest from here. 1530 */ 1531 STDMETHODIMP Machine::COMSETTER(MemoryBalloonSize)(ULONG memoryBalloonSize) 1532 { 1533 /* This must match GMMR0Init; currently we only support memory ballooning on all 64-bit hosts except Mac OS X */ 1534 #if HC_ARCH_BITS == 64 && (defined(RT_OS_WINDOWS) || defined(RT_OS_SOLARIS) || defined(RT_OS_LINUX) || defined(RT_OS_FREEBSD)) 1535 /* check limits */ 1536 if (memoryBalloonSize >= VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize)) 1537 return setError(E_INVALIDARG, 1538 tr("Invalid memory balloon size: %lu MB (must be in range [%lu, %lu] MB)"), 1539 memoryBalloonSize, 0, VMMDEV_MAX_MEMORY_BALLOON(mHWData->mMemorySize)); 1540 1541 AutoCaller autoCaller(this); 1542 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1543 1544 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1545 1546 setModified(IsModified_MachineData); 1547 mHWData.backup(); 1548 mHWData->mMemoryBalloonSize = memoryBalloonSize; 1549 1550 return S_OK; 1551 #else 1552 NOREF(memoryBalloonSize); 1553 return setError(E_NOTIMPL, tr("Memory ballooning is only supported on 64-bit hosts")); 1554 #endif 1555 } 1556 1557 STDMETHODIMP Machine::COMGETTER(PageFusionEnabled) (BOOL *enabled) 1558 { 1559 if (!enabled) 1560 return E_POINTER; 1561 1562 AutoCaller autoCaller(this); 1563 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1564 1565 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1566 1567 *enabled = mHWData->mPageFusionEnabled; 1568 return S_OK; 1569 } 1570 1571 STDMETHODIMP Machine::COMSETTER(PageFusionEnabled) (BOOL enabled) 1572 { 1573 #ifdef VBOX_WITH_PAGE_SHARING 1574 AutoCaller autoCaller(this); 1575 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1576 1577 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1578 1579 /** @todo must support changes for running vms and keep this in sync with IGuest. */ 1580 setModified(IsModified_MachineData); 1581 mHWData.backup(); 1582 mHWData->mPageFusionEnabled = enabled; 1583 return S_OK; 1584 #else 1585 NOREF(enabled); 1586 return setError(E_NOTIMPL, tr("Page fusion is only supported on 64-bit hosts")); 1587 #endif 1588 } 1589 1590 STDMETHODIMP Machine::COMGETTER(Accelerate3DEnabled)(BOOL *enabled) 1591 { 1592 if (!enabled) 1593 return E_POINTER; 1594 1595 AutoCaller autoCaller(this); 1596 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1597 1598 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1599 1600 *enabled = mHWData->mAccelerate3DEnabled; 1601 1602 return S_OK; 1603 } 1604 1605 STDMETHODIMP Machine::COMSETTER(Accelerate3DEnabled)(BOOL enable) 1606 { 1607 AutoCaller autoCaller(this); 1608 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1609 1610 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1611 1612 HRESULT rc = checkStateDependency(MutableStateDep); 1613 if (FAILED(rc)) return rc; 1614 1615 /** @todo check validity! */ 1616 1617 setModified(IsModified_MachineData); 1618 mHWData.backup(); 1619 mHWData->mAccelerate3DEnabled = enable; 1620 1621 return S_OK; 1622 } 1623 1624 1625 STDMETHODIMP Machine::COMGETTER(Accelerate2DVideoEnabled)(BOOL *enabled) 1626 { 1627 if (!enabled) 1628 return E_POINTER; 1629 1630 AutoCaller autoCaller(this); 1631 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1632 1633 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1634 1635 *enabled = mHWData->mAccelerate2DVideoEnabled; 1636 1637 return S_OK; 1638 } 1639 1640 STDMETHODIMP Machine::COMSETTER(Accelerate2DVideoEnabled)(BOOL enable) 1641 { 1642 AutoCaller autoCaller(this); 1643 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1644 1645 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1646 1647 HRESULT rc = checkStateDependency(MutableStateDep); 1648 if (FAILED(rc)) return rc; 1649 1650 /** @todo check validity! */ 1651 1652 setModified(IsModified_MachineData); 1653 mHWData.backup(); 1654 mHWData->mAccelerate2DVideoEnabled = enable; 1655 1656 return S_OK; 1657 } 1658 1659 STDMETHODIMP Machine::COMGETTER(MonitorCount)(ULONG *monitorCount) 1660 { 1661 if (!monitorCount) 1662 return E_POINTER; 1663 1664 AutoCaller autoCaller(this); 1665 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1666 1667 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1668 1669 *monitorCount = mHWData->mMonitorCount; 1670 1671 return S_OK; 1672 } 1673 1674 STDMETHODIMP Machine::COMSETTER(MonitorCount)(ULONG monitorCount) 1675 { 1676 /* make sure monitor count is a sensible number */ 1677 if (monitorCount < 1 || monitorCount > SchemaDefs::MaxGuestMonitors) 1678 return setError(E_INVALIDARG, 1679 tr("Invalid monitor count: %lu (must be in range [%lu, %lu])"), 1680 monitorCount, 1, SchemaDefs::MaxGuestMonitors); 1681 1682 AutoCaller autoCaller(this); 1683 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1684 1685 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1686 1687 HRESULT rc = checkStateDependency(MutableStateDep); 1688 if (FAILED(rc)) return rc; 1689 1690 setModified(IsModified_MachineData); 1691 mHWData.backup(); 1692 mHWData->mMonitorCount = monitorCount; 1693 1694 return S_OK; 1695 } 1696 1697 STDMETHODIMP Machine::COMGETTER(BIOSSettings)(IBIOSSettings **biosSettings) 1698 { 1699 if (!biosSettings) 1700 return E_POINTER; 1701 1702 AutoCaller autoCaller(this); 1703 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1704 1705 /* mBIOSSettings is constant during life time, no need to lock */ 1706 mBIOSSettings.queryInterfaceTo(biosSettings); 1707 1708 return S_OK; 1709 } 1710 1711 STDMETHODIMP Machine::GetCPUProperty(CPUPropertyType_T property, BOOL *aVal) 1712 { 1713 if (!aVal) 1714 return E_POINTER; 1715 1716 AutoCaller autoCaller(this); 1717 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1718 1719 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1720 1721 switch(property) 1722 { 1723 case CPUPropertyType_PAE: 1724 *aVal = mHWData->mPAEEnabled; 1725 break; 1726 1727 case CPUPropertyType_Synthetic: 1728 *aVal = mHWData->mSyntheticCpu; 1729 break; 1730 1731 default: 1732 return E_INVALIDARG; 1733 } 1734 return S_OK; 1735 } 1736 1737 STDMETHODIMP Machine::SetCPUProperty(CPUPropertyType_T property, BOOL aVal) 1738 { 1739 AutoCaller autoCaller(this); 1740 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1741 1742 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1743 1744 HRESULT rc = checkStateDependency(MutableStateDep); 1745 if (FAILED(rc)) return rc; 1746 1747 switch(property) 1748 { 1749 case CPUPropertyType_PAE: 1750 setModified(IsModified_MachineData); 1751 mHWData.backup(); 1752 mHWData->mPAEEnabled = !!aVal; 1753 break; 1754 1755 case CPUPropertyType_Synthetic: 1756 setModified(IsModified_MachineData); 1757 mHWData.backup(); 1758 mHWData->mSyntheticCpu = !!aVal; 1759 break; 1760 1761 default: 1762 return E_INVALIDARG; 1763 } 1764 return S_OK; 1765 } 1766 1767 STDMETHODIMP Machine::GetCPUIDLeaf(ULONG aId, ULONG *aValEax, ULONG *aValEbx, ULONG *aValEcx, ULONG *aValEdx) 1768 { 1769 CheckComArgOutPointerValid(aValEax); 1770 CheckComArgOutPointerValid(aValEbx); 1771 CheckComArgOutPointerValid(aValEcx); 1772 CheckComArgOutPointerValid(aValEdx); 1773 1774 AutoCaller autoCaller(this); 1775 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1776 1777 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1778 1779 switch(aId) 1780 { 1781 case 0x0: 1782 case 0x1: 1783 case 0x2: 1784 case 0x3: 1785 case 0x4: 1786 case 0x5: 1787 case 0x6: 1788 case 0x7: 1789 case 0x8: 1790 case 0x9: 1791 case 0xA: 1792 if (mHWData->mCpuIdStdLeafs[aId].ulId != aId) 1793 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId); 1794 1795 *aValEax = mHWData->mCpuIdStdLeafs[aId].ulEax; 1796 *aValEbx = mHWData->mCpuIdStdLeafs[aId].ulEbx; 1797 *aValEcx = mHWData->mCpuIdStdLeafs[aId].ulEcx; 1798 *aValEdx = mHWData->mCpuIdStdLeafs[aId].ulEdx; 1799 break; 1800 1801 case 0x80000000: 1802 case 0x80000001: 1803 case 0x80000002: 1804 case 0x80000003: 1805 case 0x80000004: 1806 case 0x80000005: 1807 case 0x80000006: 1808 case 0x80000007: 1809 case 0x80000008: 1810 case 0x80000009: 1811 case 0x8000000A: 1812 if (mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId != aId) 1813 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is not set"), aId); 1814 1815 *aValEax = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax; 1816 *aValEbx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx; 1817 *aValEcx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx; 1818 *aValEdx = mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx; 1819 break; 1820 1821 default: 1822 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 1823 } 1824 return S_OK; 1825 } 1826 1827 STDMETHODIMP Machine::SetCPUIDLeaf(ULONG aId, ULONG aValEax, ULONG aValEbx, ULONG aValEcx, ULONG aValEdx) 1828 { 1829 AutoCaller autoCaller(this); 1830 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1831 1832 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1833 1834 HRESULT rc = checkStateDependency(MutableStateDep); 1835 if (FAILED(rc)) return rc; 1836 1837 switch(aId) 1838 { 1839 case 0x0: 1840 case 0x1: 1841 case 0x2: 1842 case 0x3: 1843 case 0x4: 1844 case 0x5: 1845 case 0x6: 1846 case 0x7: 1847 case 0x8: 1848 case 0x9: 1849 case 0xA: 1850 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA); 1851 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs)); 1852 setModified(IsModified_MachineData); 1853 mHWData.backup(); 1854 mHWData->mCpuIdStdLeafs[aId].ulId = aId; 1855 mHWData->mCpuIdStdLeafs[aId].ulEax = aValEax; 1856 mHWData->mCpuIdStdLeafs[aId].ulEbx = aValEbx; 1857 mHWData->mCpuIdStdLeafs[aId].ulEcx = aValEcx; 1858 mHWData->mCpuIdStdLeafs[aId].ulEdx = aValEdx; 1859 break; 1860 1861 case 0x80000000: 1862 case 0x80000001: 1863 case 0x80000002: 1864 case 0x80000003: 1865 case 0x80000004: 1866 case 0x80000005: 1867 case 0x80000006: 1868 case 0x80000007: 1869 case 0x80000008: 1870 case 0x80000009: 1871 case 0x8000000A: 1872 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA); 1873 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs)); 1874 setModified(IsModified_MachineData); 1875 mHWData.backup(); 1876 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = aId; 1877 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEax = aValEax; 1878 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEbx = aValEbx; 1879 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEcx = aValEcx; 1880 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulEdx = aValEdx; 1881 break; 1882 1883 default: 1884 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 1885 } 1886 return S_OK; 1887 } 1888 1889 STDMETHODIMP Machine::RemoveCPUIDLeaf(ULONG aId) 1890 { 1891 AutoCaller autoCaller(this); 1892 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1893 1894 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1895 1896 HRESULT rc = checkStateDependency(MutableStateDep); 1897 if (FAILED(rc)) return rc; 1898 1899 switch(aId) 1900 { 1901 case 0x0: 1902 case 0x1: 1903 case 0x2: 1904 case 0x3: 1905 case 0x4: 1906 case 0x5: 1907 case 0x6: 1908 case 0x7: 1909 case 0x8: 1910 case 0x9: 1911 case 0xA: 1912 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdStdLeafs) == 0xA); 1913 AssertRelease(aId < RT_ELEMENTS(mHWData->mCpuIdStdLeafs)); 1914 setModified(IsModified_MachineData); 1915 mHWData.backup(); 1916 /* Invalidate leaf. */ 1917 mHWData->mCpuIdStdLeafs[aId].ulId = UINT32_MAX; 1918 break; 1919 1920 case 0x80000000: 1921 case 0x80000001: 1922 case 0x80000002: 1923 case 0x80000003: 1924 case 0x80000004: 1925 case 0x80000005: 1926 case 0x80000006: 1927 case 0x80000007: 1928 case 0x80000008: 1929 case 0x80000009: 1930 case 0x8000000A: 1931 AssertCompile(RT_ELEMENTS(mHWData->mCpuIdExtLeafs) == 0xA); 1932 AssertRelease(aId - 0x80000000 < RT_ELEMENTS(mHWData->mCpuIdExtLeafs)); 1933 setModified(IsModified_MachineData); 1934 mHWData.backup(); 1935 /* Invalidate leaf. */ 1936 mHWData->mCpuIdExtLeafs[aId - 0x80000000].ulId = UINT32_MAX; 1937 break; 1938 1939 default: 1940 return setError(E_INVALIDARG, tr("CpuId override leaf %#x is out of range"), aId); 1941 } 1942 return S_OK; 1943 } 1944 1945 STDMETHODIMP Machine::RemoveAllCPUIDLeaves() 1946 { 1947 AutoCaller autoCaller(this); 1948 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1949 1950 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 1951 1952 HRESULT rc = checkStateDependency(MutableStateDep); 1953 if (FAILED(rc)) return rc; 1954 1955 setModified(IsModified_MachineData); 1956 mHWData.backup(); 1957 1958 /* Invalidate all standard leafs. */ 1959 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); i++) 1960 mHWData->mCpuIdStdLeafs[i].ulId = UINT32_MAX; 1961 1962 /* Invalidate all extended leafs. */ 1963 for (unsigned i = 0; i < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); i++) 1964 mHWData->mCpuIdExtLeafs[i].ulId = UINT32_MAX; 1965 1966 return S_OK; 1967 } 1968 1969 STDMETHODIMP Machine::GetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL *aVal) 1970 { 1971 if (!aVal) 1972 return E_POINTER; 1973 1974 AutoCaller autoCaller(this); 1975 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 1976 1977 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 1978 1979 switch(property) 1980 { 1981 case HWVirtExPropertyType_Enabled: 1982 *aVal = mHWData->mHWVirtExEnabled; 1983 break; 1984 1985 case HWVirtExPropertyType_Exclusive: 1986 *aVal = mHWData->mHWVirtExExclusive; 1987 break; 1988 1989 case HWVirtExPropertyType_VPID: 1990 *aVal = mHWData->mHWVirtExVPIDEnabled; 1991 break; 1992 1993 case HWVirtExPropertyType_NestedPaging: 1994 *aVal = mHWData->mHWVirtExNestedPagingEnabled; 1995 break; 1996 1997 case HWVirtExPropertyType_LargePages: 1998 *aVal = mHWData->mHWVirtExLargePagesEnabled; 1999 break; 2000 2001 case HWVirtExPropertyType_Force: 2002 *aVal = mHWData->mHWVirtExForceEnabled; 2003 break; 2004 2005 default: 2006 return E_INVALIDARG; 2007 } 2008 return S_OK; 2009 } 2010 2011 STDMETHODIMP Machine::SetHWVirtExProperty(HWVirtExPropertyType_T property, BOOL aVal) 2012 { 2013 AutoCaller autoCaller(this); 2014 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2015 2016 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2017 2018 HRESULT rc = checkStateDependency(MutableStateDep); 2019 if (FAILED(rc)) return rc; 2020 2021 switch(property) 2022 { 2023 case HWVirtExPropertyType_Enabled: 2024 setModified(IsModified_MachineData); 2025 mHWData.backup(); 2026 mHWData->mHWVirtExEnabled = !!aVal; 2027 break; 2028 2029 case HWVirtExPropertyType_Exclusive: 2030 setModified(IsModified_MachineData); 2031 mHWData.backup(); 2032 mHWData->mHWVirtExExclusive = !!aVal; 2033 break; 2034 2035 case HWVirtExPropertyType_VPID: 2036 setModified(IsModified_MachineData); 2037 mHWData.backup(); 2038 mHWData->mHWVirtExVPIDEnabled = !!aVal; 2039 break; 2040 2041 case HWVirtExPropertyType_NestedPaging: 2042 setModified(IsModified_MachineData); 2043 mHWData.backup(); 2044 mHWData->mHWVirtExNestedPagingEnabled = !!aVal; 2045 break; 2046 2047 case HWVirtExPropertyType_LargePages: 2048 setModified(IsModified_MachineData); 2049 mHWData.backup(); 2050 mHWData->mHWVirtExLargePagesEnabled = !!aVal; 2051 break; 2052 2053 case HWVirtExPropertyType_Force: 2054 setModified(IsModified_MachineData); 2055 mHWData.backup(); 2056 mHWData->mHWVirtExForceEnabled = !!aVal; 2057 break; 2058 2059 default: 2060 return E_INVALIDARG; 2061 } 2062 2063 return S_OK; 2064 } 2065 2066 STDMETHODIMP Machine::COMGETTER(SnapshotFolder)(BSTR *aSnapshotFolder) 2067 { 2068 CheckComArgOutPointerValid(aSnapshotFolder); 2069 2070 AutoCaller autoCaller(this); 2071 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2072 2073 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2074 2075 mUserData->m_strSnapshotFolderFull.cloneTo(aSnapshotFolder); 2076 2077 return S_OK; 2078 } 2079 2080 STDMETHODIMP Machine::COMSETTER(SnapshotFolder)(IN_BSTR aSnapshotFolder) 2081 { 2082 /* @todo (r=dmik): 2083 * 1. Allow to change the name of the snapshot folder containing snapshots 2084 * 2. Rename the folder on disk instead of just changing the property 2085 * value (to be smart and not to leave garbage). Note that it cannot be 2086 * done here because the change may be rolled back. Thus, the right 2087 * place is #saveSettings(). 2088 */ 2089 2090 AutoCaller autoCaller(this); 2091 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2092 2093 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2094 2095 HRESULT rc = checkStateDependency(MutableStateDep); 2096 if (FAILED(rc)) return rc; 2097 2098 if (!mData->mCurrentSnapshot.isNull()) 2099 return setError(E_FAIL, 2100 tr("The snapshot folder of a machine with snapshots cannot be changed (please delete all snapshots first)")); 2101 2102 Utf8Str strSnapshotFolder0(aSnapshotFolder); // keep original 2103 Utf8Str strSnapshotFolder(strSnapshotFolder0); 2104 2105 if (strSnapshotFolder.isEmpty()) 2106 { 2107 if (isInOwnDir()) 2108 /* the default snapshots folder is 'Snapshots' in the machine dir */ 2109 strSnapshotFolder = "Snapshots"; 2110 else 2111 /* the default snapshots folder is {UUID}, for backwards 2112 * compatibility and to resolve conflicts */ 2113 strSnapshotFolder = Utf8StrFmt("{%RTuuid}", mData->mUuid.raw()); 2114 } 2115 2116 int vrc = calculateFullPath(strSnapshotFolder, strSnapshotFolder); 2117 if (RT_FAILURE(vrc)) 2118 return setError(E_FAIL, 2119 tr("Invalid snapshot folder '%ls' (%Rrc)"), 2120 aSnapshotFolder, vrc); 2121 2122 setModified(IsModified_MachineData); 2123 mUserData.backup(); 2124 mUserData->s.strSnapshotFolder = strSnapshotFolder0; 2125 mUserData->m_strSnapshotFolderFull = strSnapshotFolder; 2126 2127 return S_OK; 2128 } 2129 2130 STDMETHODIMP Machine::COMGETTER(MediumAttachments)(ComSafeArrayOut(IMediumAttachment*, aAttachments)) 2131 { 2132 if (ComSafeArrayOutIsNull(aAttachments)) 2133 return E_POINTER; 2134 2135 AutoCaller autoCaller(this); 2136 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2137 2138 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2139 2140 SafeIfaceArray<IMediumAttachment> attachments(mMediaData->mAttachments); 2141 attachments.detachTo(ComSafeArrayOutArg(aAttachments)); 2142 2143 return S_OK; 2144 } 2145 2146 STDMETHODIMP Machine::COMGETTER(VRDPServer)(IVRDPServer **vrdpServer) 2147 { 2148 #ifdef VBOX_WITH_VRDP 2149 if (!vrdpServer) 2150 return E_POINTER; 2151 2152 AutoCaller autoCaller(this); 2153 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2154 2155 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2156 2157 Assert(!!mVRDPServer); 2158 mVRDPServer.queryInterfaceTo(vrdpServer); 2159 2160 return S_OK; 2161 #else 2162 NOREF(vrdpServer); 2163 ReturnComNotImplemented(); 2164 #endif 2165 } 2166 2167 STDMETHODIMP Machine::COMGETTER(AudioAdapter)(IAudioAdapter **audioAdapter) 2168 { 2169 if (!audioAdapter) 2170 return E_POINTER; 2171 2172 AutoCaller autoCaller(this); 2173 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2174 2175 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2176 2177 mAudioAdapter.queryInterfaceTo(audioAdapter); 2178 return S_OK; 2179 } 2180 2181 STDMETHODIMP Machine::COMGETTER(USBController)(IUSBController **aUSBController) 2182 { 2183 #ifdef VBOX_WITH_VUSB 2184 CheckComArgOutPointerValid(aUSBController); 2185 2186 AutoCaller autoCaller(this); 2187 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2188 MultiResult rc(S_OK); 2189 2190 # ifdef VBOX_WITH_USB 2191 rc = mParent->host()->checkUSBProxyService(); 2192 if (FAILED(rc)) return rc; 2193 # endif 2194 2195 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2196 2197 return rc = mUSBController.queryInterfaceTo(aUSBController); 2198 #else 2199 /* Note: The GUI depends on this method returning E_NOTIMPL with no 2200 * extended error info to indicate that USB is simply not available 2201 * (w/o treting it as a failure), for example, as in OSE */ 2202 NOREF(aUSBController); 2203 ReturnComNotImplemented(); 2204 #endif /* VBOX_WITH_VUSB */ 2205 } 2206 2207 STDMETHODIMP Machine::COMGETTER(SettingsFilePath)(BSTR *aFilePath) 2208 { 2209 CheckComArgOutPointerValid(aFilePath); 2210 2211 AutoLimitedCaller autoCaller(this); 2212 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2213 2214 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2215 2216 mData->m_strConfigFileFull.cloneTo(aFilePath); 2217 return S_OK; 2218 } 2219 2220 STDMETHODIMP Machine::COMGETTER(SettingsModified)(BOOL *aModified) 2221 { 2222 CheckComArgOutPointerValid(aModified); 2223 2224 AutoCaller autoCaller(this); 2225 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2226 2227 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2228 2229 HRESULT rc = checkStateDependency(MutableStateDep); 2230 if (FAILED(rc)) return rc; 2231 2232 if (!mData->pMachineConfigFile->fileExists()) 2233 // this is a new machine, and no config file exists yet: 2234 *aModified = TRUE; 2235 else 2236 *aModified = (mData->flModifications != 0); 2237 2238 return S_OK; 2239 } 2240 2241 STDMETHODIMP Machine::COMGETTER(SessionState)(SessionState_T *aSessionState) 2242 { 2243 CheckComArgOutPointerValid(aSessionState); 2244 2245 AutoCaller autoCaller(this); 2246 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2247 2248 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2249 2250 *aSessionState = mData->mSession.mState; 2251 2252 return S_OK; 2253 } 2254 2255 STDMETHODIMP Machine::COMGETTER(SessionType)(BSTR *aSessionType) 2256 { 2257 CheckComArgOutPointerValid(aSessionType); 2258 2259 AutoCaller autoCaller(this); 2260 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2261 2262 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2263 2264 mData->mSession.mType.cloneTo(aSessionType); 2265 2266 return S_OK; 2267 } 2268 2269 STDMETHODIMP Machine::COMGETTER(SessionPid)(ULONG *aSessionPid) 2270 { 2271 CheckComArgOutPointerValid(aSessionPid); 2272 2273 AutoCaller autoCaller(this); 2274 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2275 2276 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2277 2278 *aSessionPid = mData->mSession.mPid; 2279 2280 return S_OK; 2281 } 2282 2283 STDMETHODIMP Machine::COMGETTER(State)(MachineState_T *machineState) 2284 { 2285 if (!machineState) 2286 return E_POINTER; 2287 2288 AutoCaller autoCaller(this); 2289 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2290 2291 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2292 2293 *machineState = mData->mMachineState; 2294 2295 return S_OK; 2296 } 2297 2298 STDMETHODIMP Machine::COMGETTER(LastStateChange)(LONG64 *aLastStateChange) 2299 { 2300 CheckComArgOutPointerValid(aLastStateChange); 2301 2302 AutoCaller autoCaller(this); 2303 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2304 2305 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2306 2307 *aLastStateChange = RTTimeSpecGetMilli(&mData->mLastStateChange); 2308 2309 return S_OK; 2310 } 2311 2312 STDMETHODIMP Machine::COMGETTER(StateFilePath)(BSTR *aStateFilePath) 2313 { 2314 CheckComArgOutPointerValid(aStateFilePath); 2315 2316 AutoCaller autoCaller(this); 2317 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2318 2319 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2320 2321 mSSData->mStateFilePath.cloneTo(aStateFilePath); 2322 2323 return S_OK; 2324 } 2325 2326 STDMETHODIMP Machine::COMGETTER(LogFolder)(BSTR *aLogFolder) 2327 { 2328 CheckComArgOutPointerValid(aLogFolder); 2329 2330 AutoCaller autoCaller(this); 2331 AssertComRCReturnRC(autoCaller.rc()); 2332 2333 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2334 2335 Utf8Str logFolder; 2336 getLogFolder(logFolder); 2337 2338 Bstr (logFolder).cloneTo(aLogFolder); 2339 2340 return S_OK; 2341 } 2342 2343 STDMETHODIMP Machine::COMGETTER(CurrentSnapshot) (ISnapshot **aCurrentSnapshot) 2344 { 2345 CheckComArgOutPointerValid(aCurrentSnapshot); 2346 2347 AutoCaller autoCaller(this); 2348 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2349 2350 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2351 2352 mData->mCurrentSnapshot.queryInterfaceTo(aCurrentSnapshot); 2353 2354 return S_OK; 2355 } 2356 2357 STDMETHODIMP Machine::COMGETTER(SnapshotCount)(ULONG *aSnapshotCount) 2358 { 2359 CheckComArgOutPointerValid(aSnapshotCount); 2360 2361 AutoCaller autoCaller(this); 2362 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2363 2364 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2365 2366 *aSnapshotCount = mData->mFirstSnapshot.isNull() 2367 ? 0 2368 : mData->mFirstSnapshot->getAllChildrenCount() + 1; 2369 2370 return S_OK; 2371 } 2372 2373 STDMETHODIMP Machine::COMGETTER(CurrentStateModified)(BOOL *aCurrentStateModified) 2374 { 2375 CheckComArgOutPointerValid(aCurrentStateModified); 2376 2377 AutoCaller autoCaller(this); 2378 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2379 2380 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2381 2382 /* Note: for machines with no snapshots, we always return FALSE 2383 * (mData->mCurrentStateModified will be TRUE in this case, for historical 2384 * reasons :) */ 2385 2386 *aCurrentStateModified = mData->mFirstSnapshot.isNull() 2387 ? FALSE 2388 : mData->mCurrentStateModified; 2389 2390 return S_OK; 2391 } 2392 2393 STDMETHODIMP Machine::COMGETTER(SharedFolders)(ComSafeArrayOut(ISharedFolder *, aSharedFolders)) 2394 { 2395 CheckComArgOutSafeArrayPointerValid(aSharedFolders); 2396 2397 AutoCaller autoCaller(this); 2398 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2399 2400 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2401 2402 SafeIfaceArray<ISharedFolder> folders(mHWData->mSharedFolders); 2403 folders.detachTo(ComSafeArrayOutArg(aSharedFolders)); 2404 2405 return S_OK; 2406 } 2407 2408 STDMETHODIMP Machine::COMGETTER(ClipboardMode)(ClipboardMode_T *aClipboardMode) 2409 { 2410 CheckComArgOutPointerValid(aClipboardMode); 2411 2412 AutoCaller autoCaller(this); 2413 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2414 2415 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2416 2417 *aClipboardMode = mHWData->mClipboardMode; 2418 2419 return S_OK; 2420 } 2421 2422 STDMETHODIMP 2423 Machine::COMSETTER(ClipboardMode)(ClipboardMode_T aClipboardMode) 2424 { 2425 AutoCaller autoCaller(this); 2426 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2427 2428 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2429 2430 HRESULT rc = checkStateDependency(MutableStateDep); 2431 if (FAILED(rc)) return rc; 2432 2433 setModified(IsModified_MachineData); 2434 mHWData.backup(); 2435 mHWData->mClipboardMode = aClipboardMode; 2436 2437 return S_OK; 2438 } 2439 2440 STDMETHODIMP 2441 Machine::COMGETTER(GuestPropertyNotificationPatterns)(BSTR *aPatterns) 2442 { 2443 CheckComArgOutPointerValid(aPatterns); 2444 2445 AutoCaller autoCaller(this); 2446 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2447 2448 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2449 2450 try 2451 { 2452 mHWData->mGuestPropertyNotificationPatterns.cloneTo(aPatterns); 2453 } 2454 catch (...) 2455 { 2456 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 2457 } 2458 2459 return S_OK; 2460 } 2461 2462 STDMETHODIMP 2463 Machine::COMSETTER(GuestPropertyNotificationPatterns)(IN_BSTR aPatterns) 2464 { 2465 AutoCaller autoCaller(this); 2466 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2467 2468 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2469 2470 HRESULT rc = checkStateDependency(MutableStateDep); 2471 if (FAILED(rc)) return rc; 2472 2473 setModified(IsModified_MachineData); 2474 mHWData.backup(); 2475 mHWData->mGuestPropertyNotificationPatterns = aPatterns; 2476 return rc; 2477 } 2478 2479 STDMETHODIMP 2480 Machine::COMGETTER(StorageControllers)(ComSafeArrayOut(IStorageController *, aStorageControllers)) 2481 { 2482 CheckComArgOutSafeArrayPointerValid(aStorageControllers); 2483 2484 AutoCaller autoCaller(this); 2485 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2486 2487 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2488 2489 SafeIfaceArray<IStorageController> ctrls(*mStorageControllers.data()); 2490 ctrls.detachTo(ComSafeArrayOutArg(aStorageControllers)); 2491 2492 return S_OK; 2493 } 2494 2495 STDMETHODIMP 2496 Machine::COMGETTER(TeleporterEnabled)(BOOL *aEnabled) 2497 { 2498 CheckComArgOutPointerValid(aEnabled); 2499 2500 AutoCaller autoCaller(this); 2501 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2502 2503 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2504 2505 *aEnabled = mUserData->s.fTeleporterEnabled; 2506 2507 return S_OK; 2508 } 2509 2510 STDMETHODIMP Machine::COMSETTER(TeleporterEnabled)(BOOL aEnabled) 2511 { 2512 AutoCaller autoCaller(this); 2513 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2514 2515 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2516 2517 /* Only allow it to be set to true when PoweredOff or Aborted. 2518 (Clearing it is always permitted.) */ 2519 if ( aEnabled 2520 && mData->mRegistered 2521 && ( !isSessionMachine() 2522 || ( mData->mMachineState != MachineState_PoweredOff 2523 && mData->mMachineState != MachineState_Teleported 2524 && mData->mMachineState != MachineState_Aborted 2525 ) 2526 ) 2527 ) 2528 return setError(VBOX_E_INVALID_VM_STATE, 2529 tr("The machine is not powered off (state is %s)"), 2530 Global::stringifyMachineState(mData->mMachineState)); 2531 2532 setModified(IsModified_MachineData); 2533 mUserData.backup(); 2534 mUserData->s.fTeleporterEnabled = !!aEnabled; 2535 2536 return S_OK; 2537 } 2538 2539 STDMETHODIMP Machine::COMGETTER(TeleporterPort)(ULONG *aPort) 2540 { 2541 CheckComArgOutPointerValid(aPort); 2542 2543 AutoCaller autoCaller(this); 2544 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2545 2546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2547 2548 *aPort = (ULONG)mUserData->s.uTeleporterPort; 2549 2550 return S_OK; 2551 } 2552 2553 STDMETHODIMP Machine::COMSETTER(TeleporterPort)(ULONG aPort) 2554 { 2555 if (aPort >= _64K) 2556 return setError(E_INVALIDARG, tr("Invalid port number %d"), aPort); 2557 2558 AutoCaller autoCaller(this); 2559 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2560 2561 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2562 2563 HRESULT rc = checkStateDependency(MutableStateDep); 2564 if (FAILED(rc)) return rc; 2565 2566 setModified(IsModified_MachineData); 2567 mUserData.backup(); 2568 mUserData->s.uTeleporterPort = (uint32_t)aPort; 2569 2570 return S_OK; 2571 } 2572 2573 STDMETHODIMP Machine::COMGETTER(TeleporterAddress)(BSTR *aAddress) 2574 { 2575 CheckComArgOutPointerValid(aAddress); 2576 2577 AutoCaller autoCaller(this); 2578 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2579 2580 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2581 2582 mUserData->s.strTeleporterAddress.cloneTo(aAddress); 2583 2584 return S_OK; 2585 } 2586 2587 STDMETHODIMP Machine::COMSETTER(TeleporterAddress)(IN_BSTR aAddress) 2588 { 2589 AutoCaller autoCaller(this); 2590 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2591 2592 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2593 2594 HRESULT rc = checkStateDependency(MutableStateDep); 2595 if (FAILED(rc)) return rc; 2596 2597 setModified(IsModified_MachineData); 2598 mUserData.backup(); 2599 mUserData->s.strTeleporterAddress = aAddress; 2600 2601 return S_OK; 2602 } 2603 2604 STDMETHODIMP Machine::COMGETTER(TeleporterPassword)(BSTR *aPassword) 2605 { 2606 CheckComArgOutPointerValid(aPassword); 2607 2608 AutoCaller autoCaller(this); 2609 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2610 2611 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2612 2613 mUserData->s.strTeleporterPassword.cloneTo(aPassword); 2614 2615 return S_OK; 2616 } 2617 2618 STDMETHODIMP Machine::COMSETTER(TeleporterPassword)(IN_BSTR aPassword) 2619 { 2620 AutoCaller autoCaller(this); 2621 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2622 2623 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2624 2625 HRESULT rc = checkStateDependency(MutableStateDep); 2626 if (FAILED(rc)) return rc; 2627 2628 setModified(IsModified_MachineData); 2629 mUserData.backup(); 2630 mUserData->s.strTeleporterPassword = aPassword; 2631 2632 return S_OK; 2633 } 2634 2635 STDMETHODIMP Machine::COMGETTER(FaultToleranceState)(FaultToleranceState_T *aState) 2636 { 2637 CheckComArgOutPointerValid(aState); 2638 2639 AutoCaller autoCaller(this); 2640 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2641 2642 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2643 2644 *aState = mUserData->s.enmFaultToleranceState; 2645 return S_OK; 2646 } 2647 2648 STDMETHODIMP Machine::COMSETTER(FaultToleranceState)(FaultToleranceState_T aState) 2649 { 2650 AutoCaller autoCaller(this); 2651 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2652 2653 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2654 2655 /* @todo deal with running state change. */ 2656 HRESULT rc = checkStateDependency(MutableStateDep); 2657 if (FAILED(rc)) return rc; 2658 2659 setModified(IsModified_MachineData); 2660 mUserData.backup(); 2661 mUserData->s.enmFaultToleranceState = aState; 2662 return S_OK; 2663 } 2664 2665 STDMETHODIMP Machine::COMGETTER(FaultToleranceAddress)(BSTR *aAddress) 2666 { 2667 CheckComArgOutPointerValid(aAddress); 2668 2669 AutoCaller autoCaller(this); 2670 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2671 2672 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2673 2674 mUserData->s.strFaultToleranceAddress.cloneTo(aAddress); 2675 return S_OK; 2676 } 2677 2678 STDMETHODIMP Machine::COMSETTER(FaultToleranceAddress)(IN_BSTR aAddress) 2679 { 2680 AutoCaller autoCaller(this); 2681 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2682 2683 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2684 2685 /* @todo deal with running state change. */ 2686 HRESULT rc = checkStateDependency(MutableStateDep); 2687 if (FAILED(rc)) return rc; 2688 2689 setModified(IsModified_MachineData); 2690 mUserData.backup(); 2691 mUserData->s.strFaultToleranceAddress = aAddress; 2692 return S_OK; 2693 } 2694 2695 STDMETHODIMP Machine::COMGETTER(FaultTolerancePort)(ULONG *aPort) 2696 { 2697 CheckComArgOutPointerValid(aPort); 2698 2699 AutoCaller autoCaller(this); 2700 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2701 2702 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2703 2704 *aPort = mUserData->s.uFaultTolerancePort; 2705 return S_OK; 2706 } 2707 2708 STDMETHODIMP Machine::COMSETTER(FaultTolerancePort)(ULONG aPort) 2709 { 2710 AutoCaller autoCaller(this); 2711 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2712 2713 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2714 2715 /* @todo deal with running state change. */ 2716 HRESULT rc = checkStateDependency(MutableStateDep); 2717 if (FAILED(rc)) return rc; 2718 2719 setModified(IsModified_MachineData); 2720 mUserData.backup(); 2721 mUserData->s.uFaultTolerancePort = aPort; 2722 return S_OK; 2723 } 2724 2725 STDMETHODIMP Machine::COMGETTER(FaultTolerancePassword)(BSTR *aPassword) 2726 { 2727 CheckComArgOutPointerValid(aPassword); 2728 2729 AutoCaller autoCaller(this); 2730 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2731 2732 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2733 2734 mUserData->s.strFaultTolerancePassword.cloneTo(aPassword); 2735 2736 return S_OK; 2737 } 2738 2739 STDMETHODIMP Machine::COMSETTER(FaultTolerancePassword)(IN_BSTR aPassword) 2740 { 2741 AutoCaller autoCaller(this); 2742 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2743 2744 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2745 2746 /* @todo deal with running state change. */ 2747 HRESULT rc = checkStateDependency(MutableStateDep); 2748 if (FAILED(rc)) return rc; 2749 2750 setModified(IsModified_MachineData); 2751 mUserData.backup(); 2752 mUserData->s.strFaultTolerancePassword = aPassword; 2753 2754 return S_OK; 2755 } 2756 2757 STDMETHODIMP Machine::COMGETTER(FaultToleranceSyncInterval)(ULONG *aInterval) 2758 { 2759 CheckComArgOutPointerValid(aInterval); 2760 2761 AutoCaller autoCaller(this); 2762 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2763 2764 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2765 2766 *aInterval = mUserData->s.uFaultToleranceInterval; 2767 return S_OK; 2768 } 2769 2770 STDMETHODIMP Machine::COMSETTER(FaultToleranceSyncInterval)(ULONG aInterval) 2771 { 2772 AutoCaller autoCaller(this); 2773 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2774 2775 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2776 2777 /* @todo deal with running state change. */ 2778 HRESULT rc = checkStateDependency(MutableStateDep); 2779 if (FAILED(rc)) return rc; 2780 2781 setModified(IsModified_MachineData); 2782 mUserData.backup(); 2783 mUserData->s.uFaultToleranceInterval = aInterval; 2784 return S_OK; 2785 } 2786 2787 STDMETHODIMP Machine::COMGETTER(RTCUseUTC)(BOOL *aEnabled) 2788 { 2789 CheckComArgOutPointerValid(aEnabled); 2790 2791 AutoCaller autoCaller(this); 2792 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2793 2794 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2795 2796 *aEnabled = mUserData->s.fRTCUseUTC; 2797 2798 return S_OK; 2799 } 2800 2801 STDMETHODIMP Machine::COMSETTER(RTCUseUTC)(BOOL aEnabled) 2802 { 2803 AutoCaller autoCaller(this); 2804 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2805 2806 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2807 2808 /* Only allow it to be set to true when PoweredOff or Aborted. 2809 (Clearing it is always permitted.) */ 2810 if ( aEnabled 2811 && mData->mRegistered 2812 && ( !isSessionMachine() 2813 || ( mData->mMachineState != MachineState_PoweredOff 2814 && mData->mMachineState != MachineState_Teleported 2815 && mData->mMachineState != MachineState_Aborted 2816 ) 2817 ) 2818 ) 2819 return setError(VBOX_E_INVALID_VM_STATE, 2820 tr("The machine is not powered off (state is %s)"), 2821 Global::stringifyMachineState(mData->mMachineState)); 2822 2823 setModified(IsModified_MachineData); 2824 mUserData.backup(); 2825 mUserData->s.fRTCUseUTC = !!aEnabled; 2826 2827 return S_OK; 2828 } 2829 2830 STDMETHODIMP Machine::COMGETTER(IoCacheEnabled)(BOOL *aEnabled) 2831 { 2832 CheckComArgOutPointerValid(aEnabled); 2833 2834 AutoCaller autoCaller(this); 2835 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2836 2837 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2838 2839 *aEnabled = mHWData->mIoCacheEnabled; 2840 2841 return S_OK; 2842 } 2843 2844 STDMETHODIMP Machine::COMSETTER(IoCacheEnabled)(BOOL aEnabled) 2845 { 2846 AutoCaller autoCaller(this); 2847 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2848 2849 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2850 2851 HRESULT rc = checkStateDependency(MutableStateDep); 2852 if (FAILED(rc)) return rc; 2853 2854 setModified(IsModified_MachineData); 2855 mHWData.backup(); 2856 mHWData->mIoCacheEnabled = aEnabled; 2857 2858 return S_OK; 2859 } 2860 2861 STDMETHODIMP Machine::COMGETTER(IoCacheSize)(ULONG *aIoCacheSize) 2862 { 2863 CheckComArgOutPointerValid(aIoCacheSize); 2864 2865 AutoCaller autoCaller(this); 2866 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2867 2868 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 2869 2870 *aIoCacheSize = mHWData->mIoCacheSize; 2871 2872 return S_OK; 2873 } 2874 2875 STDMETHODIMP Machine::COMSETTER(IoCacheSize)(ULONG aIoCacheSize) 2876 { 2877 AutoCaller autoCaller(this); 2878 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2879 2880 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2881 2882 HRESULT rc = checkStateDependency(MutableStateDep); 2883 if (FAILED(rc)) return rc; 2884 2885 setModified(IsModified_MachineData); 2886 mHWData.backup(); 2887 mHWData->mIoCacheSize = aIoCacheSize; 2888 2889 return S_OK; 2890 } 2891 2892 2893 /** 2894 * @note Locks objects! 2895 */ 2896 STDMETHODIMP Machine::LockMachine(ISession *aSession, 2897 LockType_T lockType) 2898 { 2899 CheckComArgNotNull(aSession); 2900 2901 AutoCaller autoCaller(this); 2902 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 2903 2904 /* check the session state */ 2905 SessionState_T state; 2906 HRESULT rc = aSession->COMGETTER(State)(&state); 2907 if (FAILED(rc)) return rc; 2908 2909 if (state != SessionState_Unlocked) 2910 return setError(VBOX_E_INVALID_OBJECT_STATE, 2911 tr("The given session is busy")); 2912 2913 // get the client's IInternalSessionControl interface 2914 ComPtr<IInternalSessionControl> pSessionControl = aSession; 2915 ComAssertMsgRet(!!pSessionControl, ("No IInternalSessionControl interface"), 2916 E_INVALIDARG); 2917 2918 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 2919 2920 if (!mData->mRegistered) 2921 return setError(E_UNEXPECTED, 2922 tr("The machine '%s' is not registered"), 2923 mUserData->s.strName.c_str()); 2924 2925 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState))); 2926 2927 SessionState_T oldState = mData->mSession.mState; 2928 /* Hack: in case the session is closing and there is a progress object 2929 * which allows waiting for the session to be closed, take the opportunity 2930 * and do a limited wait (max. 1 second). This helps a lot when the system 2931 * is busy and thus session closing can take a little while. */ 2932 if ( mData->mSession.mState == SessionState_Unlocking 2933 && mData->mSession.mProgress) 2934 { 2935 alock.release(); 2936 mData->mSession.mProgress->WaitForCompletion(1000); 2937 alock.acquire(); 2938 LogFlowThisFunc(("after waiting: mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState))); 2939 } 2940 2941 // try again now 2942 if ( (mData->mSession.mState == SessionState_Locked) // machine is write-locked already (i.e. session machine exists) 2943 && (lockType == LockType_Shared) // caller wants a shared link to the existing session that holds the write lock: 2944 ) 2945 { 2946 // OK, share the session... we are now dealing with three processes: 2947 // 1) VBoxSVC (where this code runs); 2948 // 2) process C: the caller's client process (who wants a shared session); 2949 // 3) process W: the process which already holds the write lock on the machine (write-locking session) 2950 2951 // copy pointers to W (the write-locking session) before leaving lock (these must not be NULL) 2952 ComPtr<IInternalSessionControl> pSessionW = mData->mSession.mDirectControl; 2953 ComAssertRet(!pSessionW.isNull(), E_FAIL); 2954 ComObjPtr<SessionMachine> pSessionMachine = mData->mSession.mMachine; 2955 AssertReturn(!pSessionMachine.isNull(), E_FAIL); 2956 2957 /* 2958 * Leave the lock before calling the client process. It's safe here 2959 * since the only thing to do after we get the lock again is to add 2960 * the remote control to the list (which doesn't directly influence 2961 * anything). 2962 */ 2963 alock.leave(); 2964 2965 // get the console of the session holding the write lock (this is a remote call) 2966 ComPtr<IConsole> pConsoleW; 2967 LogFlowThisFunc(("Calling GetRemoteConsole()...\n")); 2968 rc = pSessionW->GetRemoteConsole(pConsoleW.asOutParam()); 2969 LogFlowThisFunc(("GetRemoteConsole() returned %08X\n", rc)); 2970 if (FAILED(rc)) 2971 // the failure may occur w/o any error info (from RPC), so provide one 2972 return setError(VBOX_E_VM_ERROR, 2973 tr("Failed to get a console object from the direct session (%Rrc)"), rc); 2974 2975 ComAssertRet(!pConsoleW.isNull(), E_FAIL); 2976 2977 // share the session machine and W's console with the caller's session 2978 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n")); 2979 rc = pSessionControl->AssignRemoteMachine(pSessionMachine, pConsoleW); 2980 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc)); 2981 2982 if (FAILED(rc)) 2983 // the failure may occur w/o any error info (from RPC), so provide one 2984 return setError(VBOX_E_VM_ERROR, 2985 tr("Failed to assign the machine to the session (%Rrc)"), rc); 2986 alock.enter(); 2987 2988 // need to revalidate the state after entering the lock again 2989 if (mData->mSession.mState != SessionState_Locked) 2990 { 2991 pSessionControl->Uninitialize(); 2992 return setError(VBOX_E_INVALID_SESSION_STATE, 2993 tr("The machine '%s' was unlocked unexpectedly while attempting to share its session"), 2994 mUserData->s.strName.c_str()); 2995 } 2996 2997 // add the caller's session to the list 2998 mData->mSession.mRemoteControls.push_back(pSessionControl); 2999 } 3000 else if ( mData->mSession.mState == SessionState_Locked 3001 || mData->mSession.mState == SessionState_Unlocking 3002 ) 3003 { 3004 // sharing not permitted, or machine still unlocking: 3005 return setError(VBOX_E_INVALID_OBJECT_STATE, 3006 tr("The machine '%s' is already locked for a session (or being unlocked)"), 3007 mUserData->s.strName.c_str()); 3008 } 3009 else 3010 { 3011 // machine is not locked: then write-lock the machine (create the session machine) 3012 3013 // must not be busy 3014 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL); 3015 3016 // get the caller's session PID 3017 RTPROCESS pid = NIL_RTPROCESS; 3018 AssertCompile(sizeof(ULONG) == sizeof(RTPROCESS)); 3019 pSessionControl->GetPID((ULONG*)&pid); 3020 Assert(pid != NIL_RTPROCESS); 3021 3022 bool fLaunchingVMProcess = (mData->mSession.mState == SessionState_Spawning); 3023 3024 if (fLaunchingVMProcess) 3025 { 3026 // this machine is awaiting for a spawning session to be opened: 3027 // then the calling process must be the one that got started by 3028 // launchVMProcess() 3029 3030 LogFlowThisFunc(("mSession.mPid=%d(0x%x)\n", mData->mSession.mPid, mData->mSession.mPid)); 3031 LogFlowThisFunc(("session.pid=%d(0x%x)\n", pid, pid)); 3032 3033 if (mData->mSession.mPid != pid) 3034 return setError(E_ACCESSDENIED, 3035 tr("An unexpected process (PID=0x%08X) has tried to lock the " 3036 "machine '%s', while only the process started by launchVMProcess (PID=0x%08X) is allowed"), 3037 pid, mUserData->s.strName.c_str(), mData->mSession.mPid); 3038 } 3039 3040 // create the mutable SessionMachine from the current machine 3041 ComObjPtr<SessionMachine> sessionMachine; 3042 sessionMachine.createObject(); 3043 rc = sessionMachine->init(this); 3044 AssertComRC(rc); 3045 3046 /* NOTE: doing return from this function after this point but 3047 * before the end is forbidden since it may call SessionMachine::uninit() 3048 * (through the ComObjPtr's destructor) which requests the VirtualBox write 3049 * lock while still holding the Machine lock in alock so that a deadlock 3050 * is possible due to the wrong lock order. */ 3051 3052 if (SUCCEEDED(rc)) 3053 { 3054 /* 3055 * Set the session state to Spawning to protect against subsequent 3056 * attempts to open a session and to unregister the machine after 3057 * we leave the lock. 3058 */ 3059 SessionState_T origState = mData->mSession.mState; 3060 mData->mSession.mState = SessionState_Spawning; 3061 3062 /* 3063 * Leave the lock before calling the client process -- it will call 3064 * Machine/SessionMachine methods. Leaving the lock here is quite safe 3065 * because the state is Spawning, so that openRemotesession() and 3066 * openExistingSession() calls will fail. This method, called before we 3067 * enter the lock again, will fail because of the wrong PID. 3068 * 3069 * Note that mData->mSession.mRemoteControls accessed outside 3070 * the lock may not be modified when state is Spawning, so it's safe. 3071 */ 3072 alock.leave(); 3073 3074 LogFlowThisFunc(("Calling AssignMachine()...\n")); 3075 rc = pSessionControl->AssignMachine(sessionMachine); 3076 LogFlowThisFunc(("AssignMachine() returned %08X\n", rc)); 3077 3078 /* The failure may occur w/o any error info (from RPC), so provide one */ 3079 if (FAILED(rc)) 3080 setError(VBOX_E_VM_ERROR, 3081 tr("Failed to assign the machine to the session (%Rrc)"), rc); 3082 3083 if ( SUCCEEDED(rc) 3084 && fLaunchingVMProcess 3085 ) 3086 { 3087 /* complete the remote session initialization */ 3088 3089 /* get the console from the direct session */ 3090 ComPtr<IConsole> console; 3091 rc = pSessionControl->GetRemoteConsole(console.asOutParam()); 3092 ComAssertComRC(rc); 3093 3094 if (SUCCEEDED(rc) && !console) 3095 { 3096 ComAssert(!!console); 3097 rc = E_FAIL; 3098 } 3099 3100 /* assign machine & console to the remote session */ 3101 if (SUCCEEDED(rc)) 3102 { 3103 /* 3104 * after openRemoteSession(), the first and the only 3105 * entry in remoteControls is that remote session 3106 */ 3107 LogFlowThisFunc(("Calling AssignRemoteMachine()...\n")); 3108 rc = mData->mSession.mRemoteControls.front()->AssignRemoteMachine(sessionMachine, console); 3109 LogFlowThisFunc(("AssignRemoteMachine() returned %08X\n", rc)); 3110 3111 /* The failure may occur w/o any error info (from RPC), so provide one */ 3112 if (FAILED(rc)) 3113 setError(VBOX_E_VM_ERROR, 3114 tr("Failed to assign the machine to the remote session (%Rrc)"), rc); 3115 } 3116 3117 if (FAILED(rc)) 3118 pSessionControl->Uninitialize(); 3119 } 3120 3121 /* enter the lock again */ 3122 alock.enter(); 3123 3124 /* Restore the session state */ 3125 mData->mSession.mState = origState; 3126 } 3127 3128 // finalize spawning anyway (this is why we don't return on errors above) 3129 if (fLaunchingVMProcess) 3130 { 3131 /* Note that the progress object is finalized later */ 3132 /** @todo Consider checking mData->mSession.mProgress for cancellation 3133 * around here. */ 3134 3135 /* We don't reset mSession.mPid here because it is necessary for 3136 * SessionMachine::uninit() to reap the child process later. */ 3137 3138 if (FAILED(rc)) 3139 { 3140 /* Close the remote session, remove the remote control from the list 3141 * and reset session state to Closed (@note keep the code in sync 3142 * with the relevant part in openSession()). */ 3143 3144 Assert(mData->mSession.mRemoteControls.size() == 1); 3145 if (mData->mSession.mRemoteControls.size() == 1) 3146 { 3147 ErrorInfoKeeper eik; 3148 mData->mSession.mRemoteControls.front()->Uninitialize(); 3149 } 3150 3151 mData->mSession.mRemoteControls.clear(); 3152 mData->mSession.mState = SessionState_Unlocked; 3153 } 3154 } 3155 else 3156 { 3157 /* memorize PID of the directly opened session */ 3158 if (SUCCEEDED(rc)) 3159 mData->mSession.mPid = pid; 3160 } 3161 3162 if (SUCCEEDED(rc)) 3163 { 3164 /* memorize the direct session control and cache IUnknown for it */ 3165 mData->mSession.mDirectControl = pSessionControl; 3166 mData->mSession.mState = SessionState_Locked; 3167 /* associate the SessionMachine with this Machine */ 3168 mData->mSession.mMachine = sessionMachine; 3169 3170 /* request an IUnknown pointer early from the remote party for later 3171 * identity checks (it will be internally cached within mDirectControl 3172 * at least on XPCOM) */ 3173 ComPtr<IUnknown> unk = mData->mSession.mDirectControl; 3174 NOREF(unk); 3175 } 3176 3177 /* Leave the lock since SessionMachine::uninit() locks VirtualBox which 3178 * would break the lock order */ 3179 alock.leave(); 3180 3181 /* uninitialize the created session machine on failure */ 3182 if (FAILED(rc)) 3183 sessionMachine->uninit(); 3184 3185 } 3186 3187 if (SUCCEEDED(rc)) 3188 { 3189 /* 3190 * tell the client watcher thread to update the set of 3191 * machines that have open sessions 3192 */ 3193 mParent->updateClientWatcher(); 3194 3195 if (oldState != SessionState_Locked) 3196 /* fire an event */ 3197 mParent->onSessionStateChange(getId(), SessionState_Locked); 3198 } 3199 3200 return rc; 3201 } 3202 3203 /** 3204 * @note Locks objects! 3205 */ 3206 STDMETHODIMP Machine::LaunchVMProcess(ISession *aSession, 3207 IN_BSTR aType, 3208 IN_BSTR aEnvironment, 3209 IProgress **aProgress) 3210 { 3211 CheckComArgNotNull(aSession); 3212 CheckComArgStrNotEmptyOrNull(aType); 3213 CheckComArgOutSafeArrayPointerValid(aProgress); 3214 3215 AutoCaller autoCaller(this); 3216 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3217 3218 /* check the session state */ 3219 SessionState_T state; 3220 HRESULT rc = aSession->COMGETTER(State)(&state); 3221 if (FAILED(rc)) return rc; 3222 3223 if (state != SessionState_Unlocked) 3224 return setError(VBOX_E_INVALID_OBJECT_STATE, 3225 tr("The given session is busy")); 3226 3227 /* get the IInternalSessionControl interface */ 3228 ComPtr<IInternalSessionControl> control = aSession; 3229 ComAssertMsgRet(!!control, ("No IInternalSessionControl interface"), 3230 E_INVALIDARG); 3231 3232 /* get the teleporter enable state for the progress object init. */ 3233 BOOL fTeleporterEnabled; 3234 rc = COMGETTER(TeleporterEnabled)(&fTeleporterEnabled); 3235 if (FAILED(rc)) 3236 return rc; 3237 3238 /* create a progress object */ 3239 ComObjPtr<ProgressProxy> progress; 3240 progress.createObject(); 3241 rc = progress->init(mParent, 3242 static_cast<IMachine*>(this), 3243 Bstr(tr("Spawning session")), 3244 TRUE /* aCancelable */, 3245 fTeleporterEnabled ? 20 : 10 /* uTotalOperationsWeight */, 3246 Bstr(tr("Spawning session")), 3247 2 /* uFirstOperationWeight */, 3248 fTeleporterEnabled ? 3 : 1 /* cOtherProgressObjectOperations */); 3249 if (SUCCEEDED(rc)) 3250 { 3251 rc = openRemoteSession(control, aType, aEnvironment, progress); 3252 if (SUCCEEDED(rc)) 3253 { 3254 progress.queryInterfaceTo(aProgress); 3255 3256 /* signal the client watcher thread */ 3257 mParent->updateClientWatcher(); 3258 3259 /* fire an event */ 3260 mParent->onSessionStateChange(getId(), SessionState_Spawning); 3261 } 3262 } 3263 3264 return rc; 3265 } 3266 3267 STDMETHODIMP Machine::SetBootOrder(ULONG aPosition, DeviceType_T aDevice) 3268 { 3269 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition) 3270 return setError(E_INVALIDARG, 3271 tr("Invalid boot position: %lu (must be in range [1, %lu])"), 3272 aPosition, SchemaDefs::MaxBootPosition); 3273 3274 if (aDevice == DeviceType_USB) 3275 return setError(E_NOTIMPL, 3276 tr("Booting from USB device is currently not supported")); 3277 3278 AutoCaller autoCaller(this); 3279 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3280 3281 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3282 3283 HRESULT rc = checkStateDependency(MutableStateDep); 3284 if (FAILED(rc)) return rc; 3285 3286 setModified(IsModified_MachineData); 3287 mHWData.backup(); 3288 mHWData->mBootOrder[aPosition - 1] = aDevice; 3289 3290 return S_OK; 3291 } 3292 3293 STDMETHODIMP Machine::GetBootOrder(ULONG aPosition, DeviceType_T *aDevice) 3294 { 3295 if (aPosition < 1 || aPosition > SchemaDefs::MaxBootPosition) 3296 return setError(E_INVALIDARG, 3297 tr("Invalid boot position: %lu (must be in range [1, %lu])"), 3298 aPosition, SchemaDefs::MaxBootPosition); 3299 3300 AutoCaller autoCaller(this); 3301 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3302 3303 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3304 3305 *aDevice = mHWData->mBootOrder[aPosition - 1]; 3306 3307 return S_OK; 3308 } 3309 3310 STDMETHODIMP Machine::AttachDevice(IN_BSTR aControllerName, 3311 LONG aControllerPort, 3312 LONG aDevice, 3313 DeviceType_T aType, 3314 IMedium *aMedium) 3315 { 3316 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d aType=%d aId=\"%ls\"\n", 3317 aControllerName, aControllerPort, aDevice, aType, aMedium)); 3318 3319 CheckComArgStrNotEmptyOrNull(aControllerName); 3320 3321 AutoCaller autoCaller(this); 3322 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3323 3324 // if this becomes true then we need to call saveSettings in the end 3325 // @todo r=dj there is no error handling so far... 3326 bool fNeedsGlobalSaveSettings = false; 3327 3328 // request the host lock first, since might be calling Host methods for getting host drives; 3329 // next, protect the media tree all the while we're in here, as well as our member variables 3330 AutoMultiWriteLock2 alock(mParent->host()->lockHandle(), 3331 this->lockHandle() COMMA_LOCKVAL_SRC_POS); 3332 AutoWriteLock treeLock(&mParent->getMediaTreeLockHandle() COMMA_LOCKVAL_SRC_POS); 3333 3334 HRESULT rc = checkStateDependency(MutableStateDep); 3335 if (FAILED(rc)) return rc; 3336 3337 /// @todo NEWMEDIA implicit machine registration 3338 if (!mData->mRegistered) 3339 return setError(VBOX_E_INVALID_OBJECT_STATE, 3340 tr("Cannot attach storage devices to an unregistered machine")); 3341 3342 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 3343 3344 if (Global::IsOnlineOrTransient(mData->mMachineState)) 3345 return setError(VBOX_E_INVALID_VM_STATE, 3346 tr("Invalid machine state: %s"), 3347 Global::stringifyMachineState(mData->mMachineState)); 3348 3349 /* Check for an existing controller. */ 3350 ComObjPtr<StorageController> ctl; 3351 rc = getStorageControllerByName(aControllerName, ctl, true /* aSetError */); 3352 if (FAILED(rc)) return rc; 3353 3354 // check that the port and device are not out of range 3355 rc = ctl->checkPortAndDeviceValid(aControllerPort, aDevice); 3356 if (FAILED(rc)) return rc; 3357 3358 /* check if the device slot is already busy */ 3359 MediumAttachment *pAttachTemp; 3360 if ((pAttachTemp = findAttachment(mMediaData->mAttachments, 3361 aControllerName, 3362 aControllerPort, 3363 aDevice))) 3364 { 3365 Medium *pMedium = pAttachTemp->getMedium(); 3366 if (pMedium) 3367 { 3368 AutoReadLock mediumLock(pMedium COMMA_LOCKVAL_SRC_POS); 3369 return setError(VBOX_E_OBJECT_IN_USE, 3370 tr("Medium '%s' is already attached to port %d, device %d of controller '%ls' of this virtual machine"), 3371 pMedium->getLocationFull().c_str(), 3372 aControllerPort, 3373 aDevice, 3374 aControllerName); 3375 } 3376 else 3377 return setError(VBOX_E_OBJECT_IN_USE, 3378 tr("Device is already attached to port %d, device %d of controller '%ls' of this virtual machine"), 3379 aControllerPort, aDevice, aControllerName); 3380 } 3381 3382 ComObjPtr<Medium> medium = static_cast<Medium*>(aMedium); 3383 if (aMedium && medium.isNull()) 3384 return setError(E_INVALIDARG, "The given medium pointer is invalid"); 3385 3386 AutoCaller mediumCaller(medium); 3387 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 3388 3389 AutoWriteLock mediumLock(medium COMMA_LOCKVAL_SRC_POS); 3390 3391 if ( (pAttachTemp = findAttachment(mMediaData->mAttachments, medium)) 3392 && !medium.isNull() 3393 ) 3394 return setError(VBOX_E_OBJECT_IN_USE, 3395 tr("Medium '%s' is already attached to this virtual machine"), 3396 medium->getLocationFull().c_str()); 3397 3398 bool fIndirect = false; 3399 if (!medium.isNull()) 3400 fIndirect = medium->isReadOnly(); 3401 bool associate = true; 3402 3403 do 3404 { 3405 if ( aType == DeviceType_HardDisk 3406 && mMediaData.isBackedUp()) 3407 { 3408 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 3409 3410 /* check if the medium was attached to the VM before we started 3411 * changing attachments in which case the attachment just needs to 3412 * be restored */ 3413 if ((pAttachTemp = findAttachment(oldAtts, medium))) 3414 { 3415 AssertReturn(!fIndirect, E_FAIL); 3416 3417 /* see if it's the same bus/channel/device */ 3418 if (pAttachTemp->matches(aControllerName, aControllerPort, aDevice)) 3419 { 3420 /* the simplest case: restore the whole attachment 3421 * and return, nothing else to do */ 3422 mMediaData->mAttachments.push_back(pAttachTemp); 3423 return S_OK; 3424 } 3425 3426 /* bus/channel/device differ; we need a new attachment object, 3427 * but don't try to associate it again */ 3428 associate = false; 3429 break; 3430 } 3431 } 3432 3433 /* go further only if the attachment is to be indirect */ 3434 if (!fIndirect) 3435 break; 3436 3437 /* perform the so called smart attachment logic for indirect 3438 * attachments. Note that smart attachment is only applicable to base 3439 * hard disks. */ 3440 3441 if (medium->getParent().isNull()) 3442 { 3443 /* first, investigate the backup copy of the current hard disk 3444 * attachments to make it possible to re-attach existing diffs to 3445 * another device slot w/o losing their contents */ 3446 if (mMediaData.isBackedUp()) 3447 { 3448 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments; 3449 3450 MediaData::AttachmentList::const_iterator foundIt = oldAtts.end(); 3451 uint32_t foundLevel = 0; 3452 3453 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin(); 3454 it != oldAtts.end(); 3455 ++it) 3456 { 3457 uint32_t level = 0; 3458 MediumAttachment *pAttach = *it; 3459 ComObjPtr<Medium> pMedium = pAttach->getMedium(); 3460 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk); 3461 if (pMedium.isNull()) 3462 continue; 3463 3464 if (pMedium->getBase(&level) == medium) 3465 { 3466 /* skip the hard disk if its currently attached (we 3467 * cannot attach the same hard disk twice) */ 3468 if (findAttachment(mMediaData->mAttachments, 3469 pMedium)) 3470 continue; 3471 3472 /* matched device, channel and bus (i.e. attached to the 3473 * same place) will win and immediately stop the search; 3474 * otherwise the attachment that has the youngest 3475 * descendant of medium will be used 3476 */ 3477 if (pAttach->matches(aControllerName, aControllerPort, aDevice)) 3478 { 3479 /* the simplest case: restore the whole attachment 3480 * and return, nothing else to do */ 3481 mMediaData->mAttachments.push_back(*it); 3482 return S_OK; 3483 } 3484 else if ( foundIt == oldAtts.end() 3485 || level > foundLevel /* prefer younger */ 3486 ) 3487 { 3488 foundIt = it; 3489 foundLevel = level; 3490 } 3491 } 3492 } 3493 3494 if (foundIt != oldAtts.end()) 3495 { 3496 /* use the previously attached hard disk */ 3497 medium = (*foundIt)->getMedium(); 3498 mediumCaller.attach(medium); 3499 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 3500 mediumLock.attach(medium); 3501 /* not implicit, doesn't require association with this VM */ 3502 fIndirect = false; 3503 associate = false; 3504 /* go right to the MediumAttachment creation */ 3505 break; 3506 } 3507 } 3508 3509 /* must give up the medium lock and medium tree lock as below we 3510 * go over snapshots, which needs a lock with higher lock order. */ 3511 mediumLock.release(); 3512 treeLock.release(); 3513 3514 /* then, search through snapshots for the best diff in the given 3515 * hard disk's chain to base the new diff on */ 3516 3517 ComObjPtr<Medium> base; 3518 ComObjPtr<Snapshot> snap = mData->mCurrentSnapshot; 3519 while (snap) 3520 { 3521 AutoReadLock snapLock(snap COMMA_LOCKVAL_SRC_POS); 3522 3523 const MediaData::AttachmentList &snapAtts = snap->getSnapshotMachine()->mMediaData->mAttachments; 3524 3525 MediumAttachment *pAttachFound = NULL; 3526 uint32_t foundLevel = 0; 3527 3528 for (MediaData::AttachmentList::const_iterator it = snapAtts.begin(); 3529 it != snapAtts.end(); 3530 ++it) 3531 { 3532 MediumAttachment *pAttach = *it; 3533 ComObjPtr<Medium> pMedium = pAttach->getMedium(); 3534 Assert(!pMedium.isNull() || pAttach->getType() != DeviceType_HardDisk); 3535 if (pMedium.isNull()) 3536 continue; 3537 3538 uint32_t level = 0; 3539 if (pMedium->getBase(&level) == medium) 3540 { 3541 /* matched device, channel and bus (i.e. attached to the 3542 * same place) will win and immediately stop the search; 3543 * otherwise the attachment that has the youngest 3544 * descendant of medium will be used 3545 */ 3546 if ( pAttach->getDevice() == aDevice 3547 && pAttach->getPort() == aControllerPort 3548 && pAttach->getControllerName() == aControllerName 3549 ) 3550 { 3551 pAttachFound = pAttach; 3552 break; 3553 } 3554 else if ( !pAttachFound 3555 || level > foundLevel /* prefer younger */ 3556 ) 3557 { 3558 pAttachFound = pAttach; 3559 foundLevel = level; 3560 } 3561 } 3562 } 3563 3564 if (pAttachFound) 3565 { 3566 base = pAttachFound->getMedium(); 3567 break; 3568 } 3569 3570 snap = snap->getParent(); 3571 } 3572 3573 /* re-lock medium tree and the medium, as we need it below */ 3574 treeLock.acquire(); 3575 mediumLock.acquire(); 3576 3577 /* found a suitable diff, use it as a base */ 3578 if (!base.isNull()) 3579 { 3580 medium = base; 3581 mediumCaller.attach(medium); 3582 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 3583 mediumLock.attach(medium); 3584 } 3585 } 3586 3587 ComObjPtr<Medium> diff; 3588 diff.createObject(); 3589 rc = diff->init(mParent, 3590 medium->getPreferredDiffFormat(), 3591 Utf8Str(mUserData->m_strSnapshotFolderFull).append(RTPATH_SLASH_STR), 3592 medium->getRegistryMachineId(), // store this diff in the same registry as the parent 3593 &fNeedsGlobalSaveSettings); 3594 if (FAILED(rc)) return rc; 3595 3596 /* Apply the normal locking logic to the entire chain. */ 3597 MediumLockList *pMediumLockList(new MediumLockList()); 3598 rc = diff->createMediumLockList(true /* fFailIfInaccessible */, 3599 true /* fMediumLockWrite */, 3600 medium, 3601 *pMediumLockList); 3602 if (SUCCEEDED(rc)) 3603 { 3604 rc = pMediumLockList->Lock(); 3605 if (FAILED(rc)) 3606 setError(rc, 3607 tr("Could not lock medium when creating diff '%s'"), 3608 diff->getLocationFull().c_str()); 3609 else 3610 { 3611 /* will leave the lock before the potentially lengthy operation, so 3612 * protect with the special state */ 3613 MachineState_T oldState = mData->mMachineState; 3614 setMachineState(MachineState_SettingUp); 3615 3616 mediumLock.leave(); 3617 treeLock.leave(); 3618 alock.leave(); 3619 3620 rc = medium->createDiffStorage(diff, 3621 MediumVariant_Standard, 3622 pMediumLockList, 3623 NULL /* aProgress */, 3624 true /* aWait */, 3625 &fNeedsGlobalSaveSettings); 3626 3627 alock.enter(); 3628 treeLock.enter(); 3629 mediumLock.enter(); 3630 3631 setMachineState(oldState); 3632 } 3633 } 3634 3635 /* Unlock the media and free the associated memory. */ 3636 delete pMediumLockList; 3637 3638 if (FAILED(rc)) return rc; 3639 3640 /* use the created diff for the actual attachment */ 3641 medium = diff; 3642 mediumCaller.attach(medium); 3643 if (FAILED(mediumCaller.rc())) return mediumCaller.rc(); 3644 mediumLock.attach(medium); 3645 } 3646 while (0); 3647 3648 ComObjPtr<MediumAttachment> attachment; 3649 attachment.createObject(); 3650 rc = attachment->init(this, 3651 medium, 3652 aControllerName, 3653 aControllerPort, 3654 aDevice, 3655 aType, 3656 fIndirect, 3657 0 /* No bandwidth limit */); 3658 if (FAILED(rc)) return rc; 3659 3660 if (associate && !medium.isNull()) 3661 { 3662 // as the last step, associate the medium to the VM 3663 rc = medium->addBackReference(mData->mUuid); 3664 // here we can fail because of Deleting, or being in process of creating a Diff 3665 if (FAILED(rc)) return rc; 3666 3667 // and decide which medium registry to use now that the medium is attached: 3668 if (mData->pMachineConfigFile->canHaveOwnMediaRegistry()) 3669 // machine XML is VirtualBox 4.0 or higher: 3670 medium->setRegistryIdIfFirst(getId()); // machine UUID 3671 else 3672 medium->setRegistryIdIfFirst(mParent->getGlobalRegistryId()); // VirtualBox global registry UUID 3673 } 3674 3675 /* success: finally remember the attachment */ 3676 setModified(IsModified_Storage); 3677 mMediaData.backup(); 3678 mMediaData->mAttachments.push_back(attachment); 3679 3680 if (fNeedsGlobalSaveSettings) 3681 { 3682 // save the global settings; for that we should hold only the VirtualBox lock 3683 mediumLock.release(); 3684 treeLock.leave(); 3685 alock.release(); 3686 3687 AutoWriteLock vboxLock(mParent COMMA_LOCKVAL_SRC_POS); 3688 mParent->saveSettings(); 3689 } 3690 3691 return rc; 3692 } 3693 3694 STDMETHODIMP Machine::DetachDevice(IN_BSTR aControllerName, LONG aControllerPort, 3695 LONG aDevice) 3696 { 3697 CheckComArgStrNotEmptyOrNull(aControllerName); 3698 3699 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n", 3700 aControllerName, aControllerPort, aDevice)); 3701 3702 AutoCaller autoCaller(this); 3703 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3704 3705 bool fNeedsSaveSettings = false; 3706 3707 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3708 3709 HRESULT rc = checkStateDependency(MutableStateDep); 3710 if (FAILED(rc)) return rc; 3711 3712 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 3713 3714 if (Global::IsOnlineOrTransient(mData->mMachineState)) 3715 return setError(VBOX_E_INVALID_VM_STATE, 3716 tr("Invalid machine state: %s"), 3717 Global::stringifyMachineState(mData->mMachineState)); 3718 3719 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 3720 aControllerName, 3721 aControllerPort, 3722 aDevice); 3723 if (!pAttach) 3724 return setError(VBOX_E_OBJECT_NOT_FOUND, 3725 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 3726 aDevice, aControllerPort, aControllerName); 3727 3728 rc = detachDevice(pAttach, alock, NULL /* pSnapshot */, &fNeedsSaveSettings); 3729 3730 if (fNeedsSaveSettings) 3731 { 3732 bool fNeedsGlobalSaveSettings = false; 3733 saveSettings(&fNeedsGlobalSaveSettings); 3734 3735 if (fNeedsGlobalSaveSettings) 3736 { 3737 // save the global settings; for that we should hold only the VirtualBox lock 3738 alock.release(); 3739 AutoWriteLock vboxlock(this COMMA_LOCKVAL_SRC_POS); 3740 mParent->saveSettings(); 3741 } 3742 } 3743 3744 return S_OK; 3745 } 3746 3747 STDMETHODIMP Machine::PassthroughDevice(IN_BSTR aControllerName, LONG aControllerPort, 3748 LONG aDevice, BOOL aPassthrough) 3749 { 3750 CheckComArgStrNotEmptyOrNull(aControllerName); 3751 3752 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aPassthrough=%d\n", 3753 aControllerName, aControllerPort, aDevice, aPassthrough)); 3754 3755 AutoCaller autoCaller(this); 3756 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3757 3758 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 3759 3760 HRESULT rc = checkStateDependency(MutableStateDep); 3761 if (FAILED(rc)) return rc; 3762 3763 AssertReturn(mData->mMachineState != MachineState_Saved, E_FAIL); 3764 3765 if (Global::IsOnlineOrTransient(mData->mMachineState)) 3766 return setError(VBOX_E_INVALID_VM_STATE, 3767 tr("Invalid machine state: %s"), 3768 Global::stringifyMachineState(mData->mMachineState)); 3769 3770 MediumAttachment *pAttach = findAttachment(mMediaData->mAttachments, 3771 aControllerName, 3772 aControllerPort, 3773 aDevice); 3774 if (!pAttach) 3775 return setError(VBOX_E_OBJECT_NOT_FOUND, 3776 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 3777 aDevice, aControllerPort, aControllerName); 3778 3779 3780 setModified(IsModified_Storage); 3781 mMediaData.backup(); 3782 3783 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 3784 3785 if (pAttach->getType() != DeviceType_DVD) 3786 return setError(E_INVALIDARG, 3787 tr("Setting passthrough rejected as the device attached to device slot %d on port %d of controller '%ls' is not a DVD"), 3788 aDevice, aControllerPort, aControllerName); 3789 pAttach->updatePassthrough(!!aPassthrough); 3790 3791 return S_OK; 3792 } 3793 3794 STDMETHODIMP Machine::MountMedium(IN_BSTR aControllerName, 3795 LONG aControllerPort, 3796 LONG aDevice, 3797 IN_BSTR aId, 3798 BOOL aForce) 3799 { 3800 int rc = S_OK; 3801 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld aForce=%d\n", 3802 aControllerName, aControllerPort, aDevice, aForce)); 3803 3804 CheckComArgStrNotEmptyOrNull(aControllerName); 3805 3806 AutoCaller autoCaller(this); 3807 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3808 3809 // we're calling host methods for getting DVD and floppy drives so lock host first 3810 AutoMultiWriteLock2 alock(mParent->host(), this COMMA_LOCKVAL_SRC_POS); 3811 3812 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 3813 aControllerName, 3814 aControllerPort, 3815 aDevice); 3816 if (pAttach.isNull()) 3817 return setError(VBOX_E_OBJECT_NOT_FOUND, 3818 tr("No drive attached to device slot %d on port %d of controller '%ls'"), 3819 aDevice, aControllerPort, aControllerName); 3820 3821 /* Remember previously mounted medium. The medium before taking the 3822 * backup is not necessarily the same thing. */ 3823 ComObjPtr<Medium> oldmedium; 3824 oldmedium = pAttach->getMedium(); 3825 3826 Guid uuid(aId); 3827 ComObjPtr<Medium> medium; 3828 DeviceType_T mediumType = pAttach->getType(); 3829 switch (mediumType) 3830 { 3831 case DeviceType_DVD: 3832 case DeviceType_Floppy: 3833 rc = mParent->findRemoveableMedium(mediumType, uuid, true /* fRefresh */, medium); 3834 if (FAILED(rc)) return rc; 3835 break; 3836 3837 default: 3838 return setError(VBOX_E_INVALID_OBJECT_STATE, 3839 tr("Cannot change medium attached to device slot %d on port %d of controller '%ls'"), 3840 aDevice, aControllerPort, aControllerName); 3841 } 3842 3843 if (SUCCEEDED(rc)) 3844 { 3845 setModified(IsModified_Storage); 3846 mMediaData.backup(); 3847 3848 /* The backup operation makes the pAttach reference point to the 3849 * old settings. Re-get the correct reference. */ 3850 pAttach = findAttachment(mMediaData->mAttachments, 3851 aControllerName, 3852 aControllerPort, 3853 aDevice); 3854 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 3855 /* For non-hard disk media, detach straight away. */ 3856 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull()) 3857 oldmedium->removeBackReference(mData->mUuid); 3858 if (!medium.isNull()) 3859 medium->addBackReference(mData->mUuid); 3860 pAttach->updateMedium(medium, false /* aImplicit */); 3861 setModified(IsModified_Storage); 3862 } 3863 3864 alock.leave(); 3865 rc = onMediumChange(pAttach, aForce); 3866 alock.enter(); 3867 3868 /* On error roll back this change only. */ 3869 if (FAILED(rc)) 3870 { 3871 if (!medium.isNull()) 3872 medium->removeBackReference(mData->mUuid); 3873 pAttach = findAttachment(mMediaData->mAttachments, 3874 aControllerName, 3875 aControllerPort, 3876 aDevice); 3877 /* If the attachment is gone in the mean time, bail out. */ 3878 if (pAttach.isNull()) 3879 return rc; 3880 AutoWriteLock attLock(pAttach COMMA_LOCKVAL_SRC_POS); 3881 /* For non-hard disk media, re-attach straight away. */ 3882 if (mediumType != DeviceType_HardDisk && !oldmedium.isNull()) 3883 oldmedium->addBackReference(mData->mUuid); 3884 pAttach->updateMedium(oldmedium, false /* aImplicit */); 3885 } 3886 3887 return rc; 3888 } 3889 3890 STDMETHODIMP Machine::GetMedium(IN_BSTR aControllerName, 3891 LONG aControllerPort, 3892 LONG aDevice, 3893 IMedium **aMedium) 3894 { 3895 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%ld aDevice=%ld\n", 3896 aControllerName, aControllerPort, aDevice)); 3897 3898 CheckComArgStrNotEmptyOrNull(aControllerName); 3899 CheckComArgOutPointerValid(aMedium); 3900 3901 AutoCaller autoCaller(this); 3902 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3903 3904 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3905 3906 *aMedium = NULL; 3907 3908 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 3909 aControllerName, 3910 aControllerPort, 3911 aDevice); 3912 if (pAttach.isNull()) 3913 return setError(VBOX_E_OBJECT_NOT_FOUND, 3914 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 3915 aDevice, aControllerPort, aControllerName); 3916 3917 pAttach->getMedium().queryInterfaceTo(aMedium); 3918 3919 return S_OK; 3920 } 3921 3922 STDMETHODIMP Machine::GetSerialPort(ULONG slot, ISerialPort **port) 3923 { 3924 CheckComArgOutPointerValid(port); 3925 CheckComArgExpr(slot, slot < RT_ELEMENTS(mSerialPorts)); 3926 3927 AutoCaller autoCaller(this); 3928 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3929 3930 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3931 3932 mSerialPorts[slot].queryInterfaceTo(port); 3933 3934 return S_OK; 3935 } 3936 3937 STDMETHODIMP Machine::GetParallelPort(ULONG slot, IParallelPort **port) 3938 { 3939 CheckComArgOutPointerValid(port); 3940 CheckComArgExpr(slot, slot < RT_ELEMENTS(mParallelPorts)); 3941 3942 AutoCaller autoCaller(this); 3943 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3944 3945 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3946 3947 mParallelPorts[slot].queryInterfaceTo(port); 3948 3949 return S_OK; 3950 } 3951 3952 STDMETHODIMP Machine::GetNetworkAdapter(ULONG slot, INetworkAdapter **adapter) 3953 { 3954 CheckComArgOutPointerValid(adapter); 3955 CheckComArgExpr(slot, slot < RT_ELEMENTS(mNetworkAdapters)); 3956 3957 AutoCaller autoCaller(this); 3958 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3959 3960 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3961 3962 mNetworkAdapters[slot].queryInterfaceTo(adapter); 3963 3964 return S_OK; 3965 } 3966 3967 STDMETHODIMP Machine::GetExtraDataKeys(ComSafeArrayOut(BSTR, aKeys)) 3968 { 3969 if (ComSafeArrayOutIsNull(aKeys)) 3970 return E_POINTER; 3971 3972 AutoCaller autoCaller(this); 3973 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 3974 3975 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 3976 3977 com::SafeArray<BSTR> saKeys(mData->pMachineConfigFile->mapExtraDataItems.size()); 3978 int i = 0; 3979 for (settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.begin(); 3980 it != mData->pMachineConfigFile->mapExtraDataItems.end(); 3981 ++it, ++i) 3982 { 3983 const Utf8Str &strKey = it->first; 3984 strKey.cloneTo(&saKeys[i]); 3985 } 3986 saKeys.detachTo(ComSafeArrayOutArg(aKeys)); 3987 3988 return S_OK; 3989 } 3990 3991 /** 3992 * @note Locks this object for reading. 3993 */ 3994 STDMETHODIMP Machine::GetExtraData(IN_BSTR aKey, 3995 BSTR *aValue) 3996 { 3997 CheckComArgStrNotEmptyOrNull(aKey); 3998 CheckComArgOutPointerValid(aValue); 3999 4000 AutoCaller autoCaller(this); 4001 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4002 4003 /* start with nothing found */ 4004 Bstr bstrResult(""); 4005 4006 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4007 4008 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(Utf8Str(aKey)); 4009 if (it != mData->pMachineConfigFile->mapExtraDataItems.end()) 4010 // found: 4011 bstrResult = it->second; // source is a Utf8Str 4012 4013 /* return the result to caller (may be empty) */ 4014 bstrResult.cloneTo(aValue); 4015 4016 return S_OK; 4017 } 4018 4019 /** 4020 * @note Locks mParent for writing + this object for writing. 4021 */ 4022 STDMETHODIMP Machine::SetExtraData(IN_BSTR aKey, IN_BSTR aValue) 4023 { 4024 CheckComArgStrNotEmptyOrNull(aKey); 4025 4026 AutoCaller autoCaller(this); 4027 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4028 4029 Utf8Str strKey(aKey); 4030 Utf8Str strValue(aValue); 4031 Utf8Str strOldValue; // empty 4032 4033 // locking note: we only hold the read lock briefly to look up the old value, 4034 // then release it and call the onExtraCanChange callbacks. There is a small 4035 // chance of a race insofar as the callback might be called twice if two callers 4036 // change the same key at the same time, but that's a much better solution 4037 // than the deadlock we had here before. The actual changing of the extradata 4038 // is then performed under the write lock and race-free. 4039 4040 // look up the old value first; if nothing's changed then we need not do anything 4041 { 4042 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); // hold read lock only while looking up 4043 settings::StringsMap::const_iterator it = mData->pMachineConfigFile->mapExtraDataItems.find(strKey); 4044 if (it != mData->pMachineConfigFile->mapExtraDataItems.end()) 4045 strOldValue = it->second; 4046 } 4047 4048 bool fChanged; 4049 if ((fChanged = (strOldValue != strValue))) 4050 { 4051 // ask for permission from all listeners outside the locks; 4052 // onExtraDataCanChange() only briefly requests the VirtualBox 4053 // lock to copy the list of callbacks to invoke 4054 Bstr error; 4055 Bstr bstrValue(aValue); 4056 4057 if (!mParent->onExtraDataCanChange(mData->mUuid, aKey, bstrValue, error)) 4058 { 4059 const char *sep = error.isEmpty() ? "" : ": "; 4060 CBSTR err = error.raw(); 4061 LogWarningFunc(("Someone vetoed! Change refused%s%ls\n", 4062 sep, err)); 4063 return setError(E_ACCESSDENIED, 4064 tr("Could not set extra data because someone refused the requested change of '%ls' to '%ls'%s%ls"), 4065 aKey, 4066 bstrValue.raw(), 4067 sep, 4068 err); 4069 } 4070 4071 // data is changing and change not vetoed: then write it out under the lock 4072 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4073 4074 if (isSnapshotMachine()) 4075 { 4076 HRESULT rc = checkStateDependency(MutableStateDep); 4077 if (FAILED(rc)) return rc; 4078 } 4079 4080 if (strValue.isEmpty()) 4081 mData->pMachineConfigFile->mapExtraDataItems.erase(strKey); 4082 else 4083 mData->pMachineConfigFile->mapExtraDataItems[strKey] = strValue; 4084 // creates a new key if needed 4085 4086 bool fNeedsGlobalSaveSettings = false; 4087 saveSettings(&fNeedsGlobalSaveSettings); 4088 4089 if (fNeedsGlobalSaveSettings) 4090 { 4091 // save the global settings; for that we should hold only the VirtualBox lock 4092 alock.release(); 4093 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS); 4094 mParent->saveSettings(); 4095 } 4096 } 4097 4098 // fire notification outside the lock 4099 if (fChanged) 4100 mParent->onExtraDataChange(mData->mUuid, aKey, aValue); 4101 4102 return S_OK; 4103 } 4104 4105 STDMETHODIMP Machine::SaveSettings() 4106 { 4107 AutoCaller autoCaller(this); 4108 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4109 4110 AutoWriteLock mlock(this COMMA_LOCKVAL_SRC_POS); 4111 4112 /* when there was auto-conversion, we want to save the file even if 4113 * the VM is saved */ 4114 HRESULT rc = checkStateDependency(MutableStateDep); 4115 if (FAILED(rc)) return rc; 4116 4117 /* the settings file path may never be null */ 4118 ComAssertRet(!mData->m_strConfigFileFull.isEmpty(), E_FAIL); 4119 4120 /* save all VM data excluding snapshots */ 4121 bool fNeedsGlobalSaveSettings = false; 4122 rc = saveSettings(&fNeedsGlobalSaveSettings); 4123 mlock.release(); 4124 4125 if (SUCCEEDED(rc) && fNeedsGlobalSaveSettings) 4126 { 4127 // save the global settings; for that we should hold only the VirtualBox lock 4128 AutoWriteLock vlock(mParent COMMA_LOCKVAL_SRC_POS); 4129 rc = mParent->saveSettings(); 4130 } 4131 4132 return rc; 4133 } 4134 4135 STDMETHODIMP Machine::DiscardSettings() 4136 { 4137 AutoCaller autoCaller(this); 4138 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4139 4140 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4141 4142 HRESULT rc = checkStateDependency(MutableStateDep); 4143 if (FAILED(rc)) return rc; 4144 4145 /* 4146 * during this rollback, the session will be notified if data has 4147 * been actually changed 4148 */ 4149 rollback(true /* aNotify */); 4150 4151 return S_OK; 4152 } 4153 4154 /** @note Locks objects! */ 4155 STDMETHODIMP Machine::Unregister(CleanupMode_T cleanupMode, 4156 ComSafeArrayOut(IMedium*, aMedia)) 4157 { 4158 // use AutoLimitedCaller because this call is valid on inaccessible machines as well 4159 AutoLimitedCaller autoCaller(this); 4160 AssertComRCReturnRC(autoCaller.rc()); 4161 4162 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4163 4164 Guid id(getId()); 4165 4166 if (mData->mSession.mState != SessionState_Unlocked) 4167 return setError(VBOX_E_INVALID_OBJECT_STATE, 4168 tr("Cannot unregister the machine '%s' while it is locked"), 4169 mUserData->s.strName.c_str()); 4170 4171 // wait for state dependants to drop to zero 4172 ensureNoStateDependencies(); 4173 4174 if (!mData->mAccessible) 4175 { 4176 // inaccessible maschines can only be unregistered; uninitialize ourselves 4177 // here because currently there may be no unregistered that are inaccessible 4178 // (this state combination is not supported). Note releasing the caller and 4179 // leaving the lock before alling uninit() 4180 alock.leave(); 4181 autoCaller.release(); 4182 4183 uninit(); 4184 4185 mParent->unregisterMachine(this, id); 4186 // calls VirtualBox::saveSettings() 4187 4188 return S_OK; 4189 } 4190 4191 HRESULT rc = S_OK; 4192 4193 // discard saved state 4194 if (mData->mMachineState == MachineState_Saved) 4195 { 4196 // add the saved state file to the list of files the caller should delete 4197 Assert(!mSSData->mStateFilePath.isEmpty()); 4198 mData->llFilesToDelete.push_back(mSSData->mStateFilePath); 4199 4200 mSSData->mStateFilePath.setNull(); 4201 4202 // unconditionally set the machine state to powered off, we now 4203 // know no session has locked the machine 4204 mData->mMachineState = MachineState_PoweredOff; 4205 } 4206 4207 size_t cSnapshots = 0; 4208 if (mData->mFirstSnapshot) 4209 cSnapshots = mData->mFirstSnapshot->getAllChildrenCount() + 1; 4210 if (cSnapshots && cleanupMode == CleanupMode_UnregisterOnly) 4211 // fail now before we start detaching media 4212 return setError(VBOX_E_INVALID_OBJECT_STATE, 4213 tr("Cannot unregister the machine '%s' because it has %d snapshots"), 4214 mUserData->s.strName.c_str(), cSnapshots); 4215 4216 // this list collects the medium objects from all medium attachments 4217 // which got detached from the machine and its snapshots, in the following 4218 // order: 4219 // 1) media from machine attachments (these have the "leaf" attachments with snapshots 4220 // and must be closed first, or closing the parents will fail because they will 4221 // children); 4222 // 2) media from the youngest snapshots followed those from the parent snapshots until 4223 // the root ("first") snapshot of the machine 4224 // This order allows for closing the media on this list from the beginning to the end 4225 // without getting "media in use" errors. 4226 MediaList llMedia; 4227 4228 if ( !mMediaData.isNull() // can be NULL if machine is inaccessible 4229 && mMediaData->mAttachments.size() 4230 ) 4231 { 4232 // we have media attachments: detach them all and add the Medium objects to our list 4233 if (cleanupMode != CleanupMode_UnregisterOnly) 4234 detachAllMedia(alock, NULL /* pSnapshot */, cleanupMode, llMedia); 4235 else 4236 return setError(VBOX_E_INVALID_OBJECT_STATE, 4237 tr("Cannot unregister the machine '%s' because it has %d media attachments"), 4238 mUserData->s.strName.c_str(), mMediaData->mAttachments.size()); 4239 } 4240 4241 if (cSnapshots) 4242 { 4243 // autoCleanup must be true here, or we would have failed above 4244 4245 // add the media from the medium attachments of the snapshots to llMedia 4246 // as well, after the "main" machine media; Snapshot::uninitRecursively() 4247 // calls Machine::detachAllMedia() for the snapshot machine, recursing 4248 // into the children first 4249 4250 // Snapshot::beginDeletingSnapshot() asserts if the machine state is not this 4251 MachineState_T oldState = mData->mMachineState; 4252 mData->mMachineState = MachineState_DeletingSnapshot; 4253 4254 // make a copy of the first snapshot so the refcount does not drop to 0 4255 // in beginDeletingSnapshot, which sets pFirstSnapshot to 0 (that hangs 4256 // because of the AutoCaller voodoo) 4257 ComObjPtr<Snapshot> pFirstSnapshot = mData->mFirstSnapshot; 4258 4259 // GO! 4260 pFirstSnapshot->uninitRecursively(alock, cleanupMode, llMedia, mData->llFilesToDelete); 4261 4262 mData->mMachineState = oldState; 4263 } 4264 4265 if (FAILED(rc)) 4266 { 4267 rollbackMedia(); 4268 return rc; 4269 } 4270 4271 // commit all the media changes made above 4272 commitMedia(); 4273 4274 mData->mRegistered = false; 4275 4276 // machine lock no longer needed 4277 alock.release(); 4278 4279 // return media to caller 4280 SafeIfaceArray<IMedium> sfaMedia(llMedia); 4281 sfaMedia.detachTo(ComSafeArrayOutArg(aMedia)); 4282 4283 mParent->unregisterMachine(this, id); 4284 // calls VirtualBox::saveSettings() 4285 4286 return S_OK; 4287 } 4288 4289 struct Machine::DeleteTask 4290 { 4291 ComObjPtr<Machine> pMachine; 4292 std::list<Utf8Str> llFilesToDelete; 4293 ComObjPtr<Progress> pProgress; 4294 bool fNeedsGlobalSaveSettings; 4295 }; 4296 4297 STDMETHODIMP Machine::Delete(ComSafeArrayIn(IMedium*, aMedia), IProgress **aProgress) 4298 { 4299 LogFlowFuncEnter(); 4300 4301 AutoCaller autoCaller(this); 4302 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4303 4304 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4305 4306 HRESULT rc = checkStateDependency(MutableStateDep); 4307 if (FAILED(rc)) return rc; 4308 4309 if (mData->mRegistered) 4310 return setError(VBOX_E_INVALID_VM_STATE, 4311 tr("Cannot delete settings of a registered machine")); 4312 4313 DeleteTask *pTask = new DeleteTask; 4314 pTask->pMachine = this; 4315 com::SafeIfaceArray<IMedium> sfaMedia(ComSafeArrayInArg(aMedia)); 4316 4317 // collect files to delete 4318 pTask->llFilesToDelete = mData->llFilesToDelete; // saved states pushed here by Unregister() 4319 4320 pTask->fNeedsGlobalSaveSettings = false; 4321 for (size_t i = 0; i < sfaMedia.size(); ++i) 4322 { 4323 IMedium *pIMedium(sfaMedia[i]); 4324 Medium *pMedium = static_cast<Medium*>(pIMedium); 4325 AutoCaller mediumAutoCaller(pMedium); 4326 if (FAILED(mediumAutoCaller.rc())) return mediumAutoCaller.rc(); 4327 4328 Utf8Str bstrLocation = pMedium->getLocationFull(); 4329 // close the medium now; if that succeeds, then that means the medium is no longer 4330 // in use and we can add it to the list of files to delete 4331 rc = pMedium->close(&pTask->fNeedsGlobalSaveSettings, 4332 mediumAutoCaller); 4333 if (SUCCEEDED(rc)) 4334 pTask->llFilesToDelete.push_back(bstrLocation); 4335 } 4336 if (mData->pMachineConfigFile->fileExists()) 4337 pTask->llFilesToDelete.push_back(mData->m_strConfigFileFull); 4338 4339 pTask->pProgress.createObject(); 4340 pTask->pProgress->init(getVirtualBox(), 4341 static_cast<IMachine*>(this) /* aInitiator */, 4342 Bstr(tr("Deleting files")), 4343 true /* fCancellable */, 4344 pTask->llFilesToDelete.size() + 1, // cOperations 4345 BstrFmt(tr("Deleting '%s'"), pTask->llFilesToDelete.front().c_str())); 4346 4347 int vrc = RTThreadCreate(NULL, 4348 Machine::deleteThread, 4349 (void*)pTask, 4350 0, 4351 RTTHREADTYPE_MAIN_WORKER, 4352 0, 4353 "MachineDelete"); 4354 4355 pTask->pProgress.queryInterfaceTo(aProgress); 4356 4357 if (RT_FAILURE(vrc)) 4358 { 4359 delete pTask; 4360 return setError(E_FAIL, "Could not create MachineDelete thread (%Rrc)", vrc); 4361 } 4362 4363 LogFlowFuncLeave(); 4364 4365 return S_OK; 4366 } 4367 4368 /** 4369 * Static task wrapper passed to RTThreadCreate() in Machine::Delete() which then 4370 * calls Machine::deleteTaskWorker() on the actual machine object. 4371 * @param Thread 4372 * @param pvUser 4373 * @return 4374 */ 4375 /*static*/ 4376 DECLCALLBACK(int) Machine::deleteThread(RTTHREAD Thread, void *pvUser) 4377 { 4378 LogFlowFuncEnter(); 4379 4380 DeleteTask *pTask = (DeleteTask*)pvUser; 4381 Assert(pTask); 4382 Assert(pTask->pMachine); 4383 Assert(pTask->pProgress); 4384 4385 HRESULT rc = pTask->pMachine->deleteTaskWorker(*pTask); 4386 pTask->pProgress->notifyComplete(rc); 4387 4388 delete pTask; 4389 4390 LogFlowFuncLeave(); 4391 4392 NOREF(Thread); 4393 4394 return VINF_SUCCESS; 4395 } 4396 4397 /** 4398 * Task thread implementation for Machine::Delete(), called from Machine::deleteThread(). 4399 * @param task 4400 * @return 4401 */ 4402 HRESULT Machine::deleteTaskWorker(DeleteTask &task) 4403 { 4404 AutoCaller autoCaller(this); 4405 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4406 4407 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4408 4409 ULONG uLogHistoryCount = 3; 4410 ComPtr<ISystemProperties> systemProperties; 4411 mParent->COMGETTER(SystemProperties)(systemProperties.asOutParam()); 4412 if (!systemProperties.isNull()) 4413 systemProperties->COMGETTER(LogHistoryCount)(&uLogHistoryCount); 4414 4415 // delete the files pushed on the task list by Machine::Delete() 4416 // (this includes saved states of the machine and snapshots and 4417 // medium storage files from the IMedium list passed in, and the 4418 // machine XML file) 4419 std::list<Utf8Str>::const_iterator it = task.llFilesToDelete.begin(); 4420 while (it != task.llFilesToDelete.end()) 4421 { 4422 const Utf8Str &strFile = *it; 4423 LogFunc(("Deleting file %s\n", strFile.c_str())); 4424 RTFileDelete(strFile.c_str()); 4425 4426 ++it; 4427 if (it == task.llFilesToDelete.end()) 4428 { 4429 task.pProgress->SetNextOperation(Bstr(tr("Cleaning up machine directory")), 1); 4430 break; 4431 } 4432 4433 task.pProgress->SetNextOperation(BstrFmt(tr("Deleting '%s'"), it->c_str()), 1); 4434 } 4435 4436 /* delete the settings only when the file actually exists */ 4437 if (mData->pMachineConfigFile->fileExists()) 4438 { 4439 /* Delete any backup or uncommitted XML files. Ignore failures. 4440 See the fSafe parameter of xml::XmlFileWriter::write for details. */ 4441 /** @todo Find a way to avoid referring directly to iprt/xml.h here. */ 4442 Utf8Str otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszTmpSuff); 4443 RTFileDelete(otherXml.c_str()); 4444 otherXml = Utf8StrFmt("%s%s", mData->m_strConfigFileFull.c_str(), xml::XmlFileWriter::s_pszPrevSuff); 4445 RTFileDelete(otherXml.c_str()); 4446 4447 /* delete the Logs folder, nothing important should be left 4448 * there (we don't check for errors because the user might have 4449 * some private files there that we don't want to delete) */ 4450 Utf8Str logFolder; 4451 getLogFolder(logFolder); 4452 Assert(logFolder.length()); 4453 if (RTDirExists(logFolder.c_str())) 4454 { 4455 /* Delete all VBox.log[.N] files from the Logs folder 4456 * (this must be in sync with the rotation logic in 4457 * Console::powerUpThread()). Also, delete the VBox.png[.N] 4458 * files that may have been created by the GUI. */ 4459 Utf8Str log = Utf8StrFmt("%s%cVBox.log", 4460 logFolder.c_str(), RTPATH_DELIMITER); 4461 RTFileDelete(log.c_str()); 4462 log = Utf8StrFmt("%s%cVBox.png", 4463 logFolder.c_str(), RTPATH_DELIMITER); 4464 RTFileDelete(log.c_str()); 4465 for (int i = uLogHistoryCount; i > 0; i--) 4466 { 4467 log = Utf8StrFmt("%s%cVBox.log.%d", 4468 logFolder.c_str(), RTPATH_DELIMITER, i); 4469 RTFileDelete(log.c_str()); 4470 log = Utf8StrFmt("%s%cVBox.png.%d", 4471 logFolder.c_str(), RTPATH_DELIMITER, i); 4472 RTFileDelete(log.c_str()); 4473 } 4474 4475 RTDirRemove(logFolder.c_str()); 4476 } 4477 4478 /* delete the Snapshots folder, nothing important should be left 4479 * there (we don't check for errors because the user might have 4480 * some private files there that we don't want to delete) */ 4481 Assert(mUserData->m_strSnapshotFolderFull.length()); 4482 if (RTDirExists(mUserData->m_strSnapshotFolderFull.c_str())) 4483 RTDirRemove(mUserData->m_strSnapshotFolderFull.c_str()); 4484 4485 /* delete the directory that contains the settings file, but only 4486 * if it matches the VM name (i.e. a structure created by default in 4487 * prepareSaveSettings()) */ 4488 { 4489 Utf8Str settingsDir; 4490 if (isInOwnDir(&settingsDir)) 4491 RTDirRemove(settingsDir.c_str()); 4492 } 4493 } 4494 4495 alock.release(); 4496 4497 if (task.fNeedsGlobalSaveSettings) 4498 { 4499 AutoWriteLock vboxlock(mParent COMMA_LOCKVAL_SRC_POS); 4500 mParent->saveSettings(); 4501 } 4502 4503 return S_OK; 4504 } 4505 4506 STDMETHODIMP Machine::GetSnapshot(IN_BSTR aId, ISnapshot **aSnapshot) 4507 { 4508 CheckComArgOutPointerValid(aSnapshot); 4509 4510 AutoCaller autoCaller(this); 4511 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4512 4513 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4514 4515 Guid uuid(aId); 4516 /* Todo: fix this properly by perhaps introducing an isValid method for the Guid class */ 4517 if ( (aId) 4518 && (*aId != '\0') // an empty Bstr means "get root snapshot", so don't fail on that 4519 && (uuid.isEmpty())) 4520 { 4521 RTUUID uuidTemp; 4522 /* Either it's a null UUID or the conversion failed. (null uuid has a special meaning in findSnapshot) */ 4523 if (RT_FAILURE(RTUuidFromUtf16(&uuidTemp, aId))) 4524 return setError(E_FAIL, 4525 tr("Could not find a snapshot with UUID {%ls}"), 4526 aId); 4527 } 4528 4529 ComObjPtr<Snapshot> snapshot; 4530 4531 HRESULT rc = findSnapshot(uuid, snapshot, true /* aSetError */); 4532 snapshot.queryInterfaceTo(aSnapshot); 4533 4534 return rc; 4535 } 4536 4537 STDMETHODIMP Machine::FindSnapshot(IN_BSTR aName, ISnapshot **aSnapshot) 4538 { 4539 CheckComArgStrNotEmptyOrNull(aName); 4540 CheckComArgOutPointerValid(aSnapshot); 4541 4542 AutoCaller autoCaller(this); 4543 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4544 4545 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4546 4547 ComObjPtr<Snapshot> snapshot; 4548 4549 HRESULT rc = findSnapshot(aName, snapshot, true /* aSetError */); 4550 snapshot.queryInterfaceTo(aSnapshot); 4551 4552 return rc; 4553 } 4554 4555 STDMETHODIMP Machine::SetCurrentSnapshot(IN_BSTR /* aId */) 4556 { 4557 /// @todo (dmik) don't forget to set 4558 // mData->mCurrentStateModified to FALSE 4559 4560 return setError(E_NOTIMPL, "Not implemented"); 4561 } 4562 4563 STDMETHODIMP Machine::CreateSharedFolder(IN_BSTR aName, IN_BSTR aHostPath, BOOL aWritable, BOOL aAutoMount) 4564 { 4565 CheckComArgStrNotEmptyOrNull(aName); 4566 CheckComArgStrNotEmptyOrNull(aHostPath); 4567 4568 AutoCaller autoCaller(this); 4569 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4570 4571 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4572 4573 HRESULT rc = checkStateDependency(MutableStateDep); 4574 if (FAILED(rc)) return rc; 4575 4576 ComObjPtr<SharedFolder> sharedFolder; 4577 rc = findSharedFolder(aName, sharedFolder, false /* aSetError */); 4578 if (SUCCEEDED(rc)) 4579 return setError(VBOX_E_OBJECT_IN_USE, 4580 tr("Shared folder named '%ls' already exists"), 4581 aName); 4582 4583 sharedFolder.createObject(); 4584 rc = sharedFolder->init(getMachine(), aName, aHostPath, aWritable, aAutoMount); 4585 if (FAILED(rc)) return rc; 4586 4587 setModified(IsModified_SharedFolders); 4588 mHWData.backup(); 4589 mHWData->mSharedFolders.push_back(sharedFolder); 4590 4591 /* inform the direct session if any */ 4592 alock.leave(); 4593 onSharedFolderChange(); 4594 4595 return S_OK; 4596 } 4597 4598 STDMETHODIMP Machine::RemoveSharedFolder(IN_BSTR aName) 4599 { 4600 CheckComArgStrNotEmptyOrNull(aName); 4601 4602 AutoCaller autoCaller(this); 4603 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4604 4605 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4606 4607 HRESULT rc = checkStateDependency(MutableStateDep); 4608 if (FAILED(rc)) return rc; 4609 4610 ComObjPtr<SharedFolder> sharedFolder; 4611 rc = findSharedFolder(aName, sharedFolder, true /* aSetError */); 4612 if (FAILED(rc)) return rc; 4613 4614 setModified(IsModified_SharedFolders); 4615 mHWData.backup(); 4616 mHWData->mSharedFolders.remove(sharedFolder); 4617 4618 /* inform the direct session if any */ 4619 alock.leave(); 4620 onSharedFolderChange(); 4621 4622 return S_OK; 4623 } 4624 4625 STDMETHODIMP Machine::CanShowConsoleWindow(BOOL *aCanShow) 4626 { 4627 CheckComArgOutPointerValid(aCanShow); 4628 4629 /* start with No */ 4630 *aCanShow = FALSE; 4631 4632 AutoCaller autoCaller(this); 4633 AssertComRCReturnRC(autoCaller.rc()); 4634 4635 ComPtr<IInternalSessionControl> directControl; 4636 { 4637 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4638 4639 if (mData->mSession.mState != SessionState_Locked) 4640 return setError(VBOX_E_INVALID_VM_STATE, 4641 tr("Machine is not locked for session (session state: %s)"), 4642 Global::stringifySessionState(mData->mSession.mState)); 4643 4644 directControl = mData->mSession.mDirectControl; 4645 } 4646 4647 /* ignore calls made after #OnSessionEnd() is called */ 4648 if (!directControl) 4649 return S_OK; 4650 4651 LONG64 dummy; 4652 return directControl->OnShowWindow(TRUE /* aCheck */, aCanShow, &dummy); 4653 } 4654 4655 STDMETHODIMP Machine::ShowConsoleWindow(LONG64 *aWinId) 4656 { 4657 CheckComArgOutPointerValid(aWinId); 4658 4659 AutoCaller autoCaller(this); 4660 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 4661 4662 ComPtr<IInternalSessionControl> directControl; 4663 { 4664 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4665 4666 if (mData->mSession.mState != SessionState_Locked) 4667 return setError(E_FAIL, 4668 tr("Machine is not locked for session (session state: %s)"), 4669 Global::stringifySessionState(mData->mSession.mState)); 4670 4671 directControl = mData->mSession.mDirectControl; 4672 } 4673 4674 /* ignore calls made after #OnSessionEnd() is called */ 4675 if (!directControl) 4676 return S_OK; 4677 4678 BOOL dummy; 4679 return directControl->OnShowWindow(FALSE /* aCheck */, &dummy, aWinId); 4680 } 4681 4682 #ifdef VBOX_WITH_GUEST_PROPS 4683 /** 4684 * Look up a guest property in VBoxSVC's internal structures. 4685 */ 4686 HRESULT Machine::getGuestPropertyFromService(IN_BSTR aName, 4687 BSTR *aValue, 4688 LONG64 *aTimestamp, 4689 BSTR *aFlags) const 4690 { 4691 using namespace guestProp; 4692 4693 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4694 Utf8Str strName(aName); 4695 HWData::GuestPropertyList::const_iterator it; 4696 4697 for (it = mHWData->mGuestProperties.begin(); 4698 it != mHWData->mGuestProperties.end(); ++it) 4699 { 4700 if (it->strName == strName) 4701 { 4702 char szFlags[MAX_FLAGS_LEN + 1]; 4703 it->strValue.cloneTo(aValue); 4704 *aTimestamp = it->mTimestamp; 4705 writeFlags(it->mFlags, szFlags); 4706 Bstr(szFlags).cloneTo(aFlags); 4707 break; 4708 } 4709 } 4710 return S_OK; 4711 } 4712 4713 /** 4714 * Query the VM that a guest property belongs to for the property. 4715 * @returns E_ACCESSDENIED if the VM process is not available or not 4716 * currently handling queries and the lookup should then be done in 4717 * VBoxSVC. 4718 */ 4719 HRESULT Machine::getGuestPropertyFromVM(IN_BSTR aName, 4720 BSTR *aValue, 4721 LONG64 *aTimestamp, 4722 BSTR *aFlags) const 4723 { 4724 HRESULT rc; 4725 ComPtr<IInternalSessionControl> directControl; 4726 directControl = mData->mSession.mDirectControl; 4727 4728 /* fail if we were called after #OnSessionEnd() is called. This is a 4729 * silly race condition. */ 4730 4731 if (!directControl) 4732 rc = E_ACCESSDENIED; 4733 else 4734 rc = directControl->AccessGuestProperty(aName, NULL, NULL, 4735 false /* isSetter */, 4736 aValue, aTimestamp, aFlags); 4737 return rc; 4738 } 4739 #endif // VBOX_WITH_GUEST_PROPS 4740 4741 STDMETHODIMP Machine::GetGuestProperty(IN_BSTR aName, 4742 BSTR *aValue, 4743 LONG64 *aTimestamp, 4744 BSTR *aFlags) 4745 { 4746 #ifndef VBOX_WITH_GUEST_PROPS 4747 ReturnComNotImplemented(); 4748 #else // VBOX_WITH_GUEST_PROPS 4749 CheckComArgStrNotEmptyOrNull(aName); 4750 CheckComArgOutPointerValid(aValue); 4751 CheckComArgOutPointerValid(aTimestamp); 4752 CheckComArgOutPointerValid(aFlags); 4753 4754 AutoCaller autoCaller(this); 4755 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4756 4757 HRESULT rc = getGuestPropertyFromVM(aName, aValue, aTimestamp, aFlags); 4758 if (rc == E_ACCESSDENIED) 4759 /* The VM is not running or the service is not (yet) accessible */ 4760 rc = getGuestPropertyFromService(aName, aValue, aTimestamp, aFlags); 4761 return rc; 4762 #endif // VBOX_WITH_GUEST_PROPS 4763 } 4764 4765 STDMETHODIMP Machine::GetGuestPropertyValue(IN_BSTR aName, BSTR *aValue) 4766 { 4767 LONG64 dummyTimestamp; 4768 Bstr dummyFlags; 4769 return GetGuestProperty(aName, aValue, &dummyTimestamp, dummyFlags.asOutParam()); 4770 } 4771 4772 STDMETHODIMP Machine::GetGuestPropertyTimestamp(IN_BSTR aName, LONG64 *aTimestamp) 4773 { 4774 Bstr dummyValue; 4775 Bstr dummyFlags; 4776 return GetGuestProperty(aName, dummyValue.asOutParam(), aTimestamp, dummyFlags.asOutParam()); 4777 } 4778 4779 #ifdef VBOX_WITH_GUEST_PROPS 4780 /** 4781 * Set a guest property in VBoxSVC's internal structures. 4782 */ 4783 HRESULT Machine::setGuestPropertyToService(IN_BSTR aName, IN_BSTR aValue, 4784 IN_BSTR aFlags) 4785 { 4786 using namespace guestProp; 4787 4788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 4789 HRESULT rc = S_OK; 4790 HWData::GuestProperty property; 4791 property.mFlags = NILFLAG; 4792 bool found = false; 4793 4794 rc = checkStateDependency(MutableStateDep); 4795 if (FAILED(rc)) return rc; 4796 4797 try 4798 { 4799 Utf8Str utf8Name(aName); 4800 Utf8Str utf8Flags(aFlags); 4801 uint32_t fFlags = NILFLAG; 4802 if ( (aFlags != NULL) 4803 && RT_FAILURE(validateFlags(utf8Flags.c_str(), &fFlags)) 4804 ) 4805 return setError(E_INVALIDARG, 4806 tr("Invalid flag values: '%ls'"), 4807 aFlags); 4808 4809 /** @todo r=bird: see efficiency rant in PushGuestProperty. (Yeah, I 4810 * know, this is simple and do an OK job atm.) */ 4811 HWData::GuestPropertyList::iterator it; 4812 for (it = mHWData->mGuestProperties.begin(); 4813 it != mHWData->mGuestProperties.end(); ++it) 4814 if (it->strName == utf8Name) 4815 { 4816 property = *it; 4817 if (it->mFlags & (RDONLYHOST)) 4818 rc = setError(E_ACCESSDENIED, 4819 tr("The property '%ls' cannot be changed by the host"), 4820 aName); 4821 else 4822 { 4823 setModified(IsModified_MachineData); 4824 mHWData.backup(); // @todo r=dj backup in a loop?!? 4825 4826 /* The backup() operation invalidates our iterator, so 4827 * get a new one. */ 4828 for (it = mHWData->mGuestProperties.begin(); 4829 it->strName != utf8Name; 4830 ++it) 4831 ; 4832 mHWData->mGuestProperties.erase(it); 4833 } 4834 found = true; 4835 break; 4836 } 4837 if (found && SUCCEEDED(rc)) 4838 { 4839 if (*aValue) 4840 { 4841 RTTIMESPEC time; 4842 property.strValue = aValue; 4843 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); 4844 if (aFlags != NULL) 4845 property.mFlags = fFlags; 4846 mHWData->mGuestProperties.push_back(property); 4847 } 4848 } 4849 else if (SUCCEEDED(rc) && *aValue) 4850 { 4851 RTTIMESPEC time; 4852 setModified(IsModified_MachineData); 4853 mHWData.backup(); 4854 property.strName = aName; 4855 property.strValue = aValue; 4856 property.mTimestamp = RTTimeSpecGetNano(RTTimeNow(&time)); 4857 property.mFlags = fFlags; 4858 mHWData->mGuestProperties.push_back(property); 4859 } 4860 if ( SUCCEEDED(rc) 4861 && ( mHWData->mGuestPropertyNotificationPatterns.isEmpty() 4862 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(), 4863 RTSTR_MAX, 4864 utf8Name.c_str(), 4865 RTSTR_MAX, 4866 NULL) 4867 ) 4868 ) 4869 { 4870 /** @todo r=bird: Why aren't we leaving the lock here? The 4871 * same code in PushGuestProperty does... */ 4872 mParent->onGuestPropertyChange(mData->mUuid, aName, aValue, aFlags); 4873 } 4874 } 4875 catch (std::bad_alloc &) 4876 { 4877 rc = E_OUTOFMEMORY; 4878 } 4879 4880 return rc; 4881 } 4882 4883 /** 4884 * Set a property on the VM that that property belongs to. 4885 * @returns E_ACCESSDENIED if the VM process is not available or not 4886 * currently handling queries and the setting should then be done in 4887 * VBoxSVC. 4888 */ 4889 HRESULT Machine::setGuestPropertyToVM(IN_BSTR aName, IN_BSTR aValue, 4890 IN_BSTR aFlags) 4891 { 4892 HRESULT rc; 4893 4894 try { 4895 ComPtr<IInternalSessionControl> directControl = 4896 mData->mSession.mDirectControl; 4897 4898 BSTR dummy = NULL; /* will not be changed (setter) */ 4899 LONG64 dummy64; 4900 if (!directControl) 4901 rc = E_ACCESSDENIED; 4902 else 4903 rc = directControl->AccessGuestProperty 4904 (aName, 4905 /** @todo Fix when adding DeleteGuestProperty(), 4906 see defect. */ 4907 *aValue ? aValue : NULL, aFlags, true /* isSetter */, 4908 &dummy, &dummy64, &dummy); 4909 } 4910 catch (std::bad_alloc &) 4911 { 4912 rc = E_OUTOFMEMORY; 4913 } 4914 4915 return rc; 4916 } 4917 #endif // VBOX_WITH_GUEST_PROPS 4918 4919 STDMETHODIMP Machine::SetGuestProperty(IN_BSTR aName, IN_BSTR aValue, 4920 IN_BSTR aFlags) 4921 { 4922 #ifndef VBOX_WITH_GUEST_PROPS 4923 ReturnComNotImplemented(); 4924 #else // VBOX_WITH_GUEST_PROPS 4925 CheckComArgStrNotEmptyOrNull(aName); 4926 if ((aFlags != NULL) && !VALID_PTR(aFlags)) 4927 return E_INVALIDARG; 4928 AutoCaller autoCaller(this); 4929 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 4930 4931 HRESULT rc = setGuestPropertyToVM(aName, aValue, aFlags); 4932 if (rc == E_ACCESSDENIED) 4933 /* The VM is not running or the service is not (yet) accessible */ 4934 rc = setGuestPropertyToService(aName, aValue, aFlags); 4935 return rc; 4936 #endif // VBOX_WITH_GUEST_PROPS 4937 } 4938 4939 STDMETHODIMP Machine::SetGuestPropertyValue(IN_BSTR aName, IN_BSTR aValue) 4940 { 4941 return SetGuestProperty(aName, aValue, NULL); 4942 } 4943 4944 #ifdef VBOX_WITH_GUEST_PROPS 4945 /** 4946 * Enumerate the guest properties in VBoxSVC's internal structures. 4947 */ 4948 HRESULT Machine::enumerateGuestPropertiesInService 4949 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames), 4950 ComSafeArrayOut(BSTR, aValues), 4951 ComSafeArrayOut(LONG64, aTimestamps), 4952 ComSafeArrayOut(BSTR, aFlags)) 4953 { 4954 using namespace guestProp; 4955 4956 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 4957 Utf8Str strPatterns(aPatterns); 4958 4959 /* 4960 * Look for matching patterns and build up a list. 4961 */ 4962 HWData::GuestPropertyList propList; 4963 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin(); 4964 it != mHWData->mGuestProperties.end(); 4965 ++it) 4966 if ( strPatterns.isEmpty() 4967 || RTStrSimplePatternMultiMatch(strPatterns.c_str(), 4968 RTSTR_MAX, 4969 it->strName.c_str(), 4970 RTSTR_MAX, 4971 NULL) 4972 ) 4973 propList.push_back(*it); 4974 4975 /* 4976 * And build up the arrays for returning the property information. 4977 */ 4978 size_t cEntries = propList.size(); 4979 SafeArray<BSTR> names(cEntries); 4980 SafeArray<BSTR> values(cEntries); 4981 SafeArray<LONG64> timestamps(cEntries); 4982 SafeArray<BSTR> flags(cEntries); 4983 size_t iProp = 0; 4984 for (HWData::GuestPropertyList::iterator it = propList.begin(); 4985 it != propList.end(); 4986 ++it) 4987 { 4988 char szFlags[MAX_FLAGS_LEN + 1]; 4989 it->strName.cloneTo(&names[iProp]); 4990 it->strValue.cloneTo(&values[iProp]); 4991 timestamps[iProp] = it->mTimestamp; 4992 writeFlags(it->mFlags, szFlags); 4993 Bstr(szFlags).cloneTo(&flags[iProp]); 4994 ++iProp; 4995 } 4996 names.detachTo(ComSafeArrayOutArg(aNames)); 4997 values.detachTo(ComSafeArrayOutArg(aValues)); 4998 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps)); 4999 flags.detachTo(ComSafeArrayOutArg(aFlags)); 5000 return S_OK; 5001 } 5002 5003 /** 5004 * Enumerate the properties managed by a VM. 5005 * @returns E_ACCESSDENIED if the VM process is not available or not 5006 * currently handling queries and the setting should then be done in 5007 * VBoxSVC. 5008 */ 5009 HRESULT Machine::enumerateGuestPropertiesOnVM 5010 (IN_BSTR aPatterns, ComSafeArrayOut(BSTR, aNames), 5011 ComSafeArrayOut(BSTR, aValues), 5012 ComSafeArrayOut(LONG64, aTimestamps), 5013 ComSafeArrayOut(BSTR, aFlags)) 5014 { 5015 HRESULT rc; 5016 ComPtr<IInternalSessionControl> directControl; 5017 directControl = mData->mSession.mDirectControl; 5018 5019 if (!directControl) 5020 rc = E_ACCESSDENIED; 5021 else 5022 rc = directControl->EnumerateGuestProperties 5023 (aPatterns, ComSafeArrayOutArg(aNames), 5024 ComSafeArrayOutArg(aValues), 5025 ComSafeArrayOutArg(aTimestamps), 5026 ComSafeArrayOutArg(aFlags)); 5027 return rc; 5028 } 5029 #endif // VBOX_WITH_GUEST_PROPS 5030 5031 STDMETHODIMP Machine::EnumerateGuestProperties(IN_BSTR aPatterns, 5032 ComSafeArrayOut(BSTR, aNames), 5033 ComSafeArrayOut(BSTR, aValues), 5034 ComSafeArrayOut(LONG64, aTimestamps), 5035 ComSafeArrayOut(BSTR, aFlags)) 5036 { 5037 #ifndef VBOX_WITH_GUEST_PROPS 5038 ReturnComNotImplemented(); 5039 #else // VBOX_WITH_GUEST_PROPS 5040 if (!VALID_PTR(aPatterns) && (aPatterns != NULL)) 5041 return E_POINTER; 5042 5043 CheckComArgOutSafeArrayPointerValid(aNames); 5044 CheckComArgOutSafeArrayPointerValid(aValues); 5045 CheckComArgOutSafeArrayPointerValid(aTimestamps); 5046 CheckComArgOutSafeArrayPointerValid(aFlags); 5047 5048 AutoCaller autoCaller(this); 5049 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5050 5051 HRESULT rc = enumerateGuestPropertiesOnVM 5052 (aPatterns, ComSafeArrayOutArg(aNames), 5053 ComSafeArrayOutArg(aValues), 5054 ComSafeArrayOutArg(aTimestamps), 5055 ComSafeArrayOutArg(aFlags)); 5056 if (rc == E_ACCESSDENIED) 5057 /* The VM is not running or the service is not (yet) accessible */ 5058 rc = enumerateGuestPropertiesInService 5059 (aPatterns, ComSafeArrayOutArg(aNames), 5060 ComSafeArrayOutArg(aValues), 5061 ComSafeArrayOutArg(aTimestamps), 5062 ComSafeArrayOutArg(aFlags)); 5063 return rc; 5064 #endif // VBOX_WITH_GUEST_PROPS 5065 } 5066 5067 STDMETHODIMP Machine::GetMediumAttachmentsOfController(IN_BSTR aName, 5068 ComSafeArrayOut(IMediumAttachment*, aAttachments)) 5069 { 5070 MediaData::AttachmentList atts; 5071 5072 HRESULT rc = getMediumAttachmentsOfController(aName, atts); 5073 if (FAILED(rc)) return rc; 5074 5075 SafeIfaceArray<IMediumAttachment> attachments(atts); 5076 attachments.detachTo(ComSafeArrayOutArg(aAttachments)); 5077 5078 return S_OK; 5079 } 5080 5081 STDMETHODIMP Machine::GetMediumAttachment(IN_BSTR aControllerName, 5082 LONG aControllerPort, 5083 LONG aDevice, 5084 IMediumAttachment **aAttachment) 5085 { 5086 LogFlowThisFunc(("aControllerName=\"%ls\" aControllerPort=%d aDevice=%d\n", 5087 aControllerName, aControllerPort, aDevice)); 5088 5089 CheckComArgStrNotEmptyOrNull(aControllerName); 5090 CheckComArgOutPointerValid(aAttachment); 5091 5092 AutoCaller autoCaller(this); 5093 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5094 5095 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5096 5097 *aAttachment = NULL; 5098 5099 ComObjPtr<MediumAttachment> pAttach = findAttachment(mMediaData->mAttachments, 5100 aControllerName, 5101 aControllerPort, 5102 aDevice); 5103 if (pAttach.isNull()) 5104 return setError(VBOX_E_OBJECT_NOT_FOUND, 5105 tr("No storage device attached to device slot %d on port %d of controller '%ls'"), 5106 aDevice, aControllerPort, aControllerName); 5107 5108 pAttach.queryInterfaceTo(aAttachment); 5109 5110 return S_OK; 5111 } 5112 5113 STDMETHODIMP Machine::AddStorageController(IN_BSTR aName, 5114 StorageBus_T aConnectionType, 5115 IStorageController **controller) 5116 { 5117 CheckComArgStrNotEmptyOrNull(aName); 5118 5119 if ( (aConnectionType <= StorageBus_Null) 5120 || (aConnectionType > StorageBus_SAS)) 5121 return setError(E_INVALIDARG, 5122 tr("Invalid connection type: %d"), 5123 aConnectionType); 5124 5125 AutoCaller autoCaller(this); 5126 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5127 5128 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5129 5130 HRESULT rc = checkStateDependency(MutableStateDep); 5131 if (FAILED(rc)) return rc; 5132 5133 /* try to find one with the name first. */ 5134 ComObjPtr<StorageController> ctrl; 5135 5136 rc = getStorageControllerByName(aName, ctrl, false /* aSetError */); 5137 if (SUCCEEDED(rc)) 5138 return setError(VBOX_E_OBJECT_IN_USE, 5139 tr("Storage controller named '%ls' already exists"), 5140 aName); 5141 5142 ctrl.createObject(); 5143 5144 /* get a new instance number for the storage controller */ 5145 ULONG ulInstance = 0; 5146 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 5147 it != mStorageControllers->end(); 5148 ++it) 5149 { 5150 if ((*it)->getStorageBus() == aConnectionType) 5151 { 5152 ULONG ulCurInst = (*it)->getInstance(); 5153 5154 if (ulCurInst >= ulInstance) 5155 ulInstance = ulCurInst + 1; 5156 } 5157 } 5158 5159 rc = ctrl->init(this, aName, aConnectionType, ulInstance); 5160 if (FAILED(rc)) return rc; 5161 5162 setModified(IsModified_Storage); 5163 mStorageControllers.backup(); 5164 mStorageControllers->push_back(ctrl); 5165 5166 ctrl.queryInterfaceTo(controller); 5167 5168 /* inform the direct session if any */ 5169 alock.leave(); 5170 onStorageControllerChange(); 5171 5172 return S_OK; 5173 } 5174 5175 STDMETHODIMP Machine::GetStorageControllerByName(IN_BSTR aName, 5176 IStorageController **aStorageController) 5177 { 5178 CheckComArgStrNotEmptyOrNull(aName); 5179 5180 AutoCaller autoCaller(this); 5181 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5182 5183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5184 5185 ComObjPtr<StorageController> ctrl; 5186 5187 HRESULT rc = getStorageControllerByName(aName, ctrl, true /* aSetError */); 5188 if (SUCCEEDED(rc)) 5189 ctrl.queryInterfaceTo(aStorageController); 5190 5191 return rc; 5192 } 5193 5194 STDMETHODIMP Machine::GetStorageControllerByInstance(ULONG aInstance, 5195 IStorageController **aStorageController) 5196 { 5197 AutoCaller autoCaller(this); 5198 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5199 5200 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5201 5202 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 5203 it != mStorageControllers->end(); 5204 ++it) 5205 { 5206 if ((*it)->getInstance() == aInstance) 5207 { 5208 (*it).queryInterfaceTo(aStorageController); 5209 return S_OK; 5210 } 5211 } 5212 5213 return setError(VBOX_E_OBJECT_NOT_FOUND, 5214 tr("Could not find a storage controller with instance number '%lu'"), 5215 aInstance); 5216 } 5217 5218 STDMETHODIMP Machine::RemoveStorageController(IN_BSTR aName) 5219 { 5220 CheckComArgStrNotEmptyOrNull(aName); 5221 5222 AutoCaller autoCaller(this); 5223 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5224 5225 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5226 5227 HRESULT rc = checkStateDependency(MutableStateDep); 5228 if (FAILED(rc)) return rc; 5229 5230 ComObjPtr<StorageController> ctrl; 5231 rc = getStorageControllerByName(aName, ctrl, true /* aSetError */); 5232 if (FAILED(rc)) return rc; 5233 5234 /* We can remove the controller only if there is no device attached. */ 5235 /* check if the device slot is already busy */ 5236 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 5237 it != mMediaData->mAttachments.end(); 5238 ++it) 5239 { 5240 if ((*it)->getControllerName() == aName) 5241 return setError(VBOX_E_OBJECT_IN_USE, 5242 tr("Storage controller named '%ls' has still devices attached"), 5243 aName); 5244 } 5245 5246 /* We can remove it now. */ 5247 setModified(IsModified_Storage); 5248 mStorageControllers.backup(); 5249 5250 ctrl->unshare(); 5251 5252 mStorageControllers->remove(ctrl); 5253 5254 /* inform the direct session if any */ 5255 alock.leave(); 5256 onStorageControllerChange(); 5257 5258 return S_OK; 5259 } 5260 5261 /* @todo where is the right place for this? */ 5262 #define sSSMDisplayScreenshotVer 0x00010001 5263 5264 static int readSavedDisplayScreenshot(const Utf8Str &strStateFilePath, uint32_t u32Type, uint8_t **ppu8Data, uint32_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height) 24 int readSavedDisplayScreenshot(const Utf8Str &strStateFilePath, uint32_t u32Type, uint8_t **ppu8Data, uint32_t *pcbData, uint32_t *pu32Width, uint32_t *pu32Height) 5265 25 { 5266 26 LogFlowFunc(("u32Type = %d [%s]\n", u32Type, strStateFilePath.c_str())); … … 5375 135 } 5376 136 5377 staticvoid freeSavedDisplayScreenshot(uint8_t *pu8Data)137 void freeSavedDisplayScreenshot(uint8_t *pu8Data) 5378 138 { 5379 139 /* @todo not necessary when caching is implemented. */ … … 5381 141 } 5382 142 5383 STDMETHODIMP Machine::QuerySavedThumbnailSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight)143 int readSavedGuestSize(const Utf8Str &strStateFilePath, uint32_t u32ScreenId, uint32_t *pu32Width, uint32_t *pu32Height) 5384 144 { 5385 LogFlow ThisFunc(("\n"));145 LogFlowFunc(("u32ScreenId = %d [%s]\n", u32ScreenId, strStateFilePath.c_str())); 5386 146 5387 CheckComArgNotNull(aSize); 5388 CheckComArgNotNull(aWidth); 5389 CheckComArgNotNull(aHeight); 147 /* @todo cache read data */ 148 if (strStateFilePath.isEmpty()) 149 { 150 /* No saved state data. */ 151 return VERR_NOT_SUPPORTED; 152 } 5390 153 5391 if (aScreenId != 0)5392 return E_NOTIMPL;5393 5394 AutoCaller autoCaller(this);5395 if (FAILED(autoCaller.rc())) return autoCaller.rc();5396 5397 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);5398 5399 uint8_t *pu8Data = NULL;5400 uint32_t cbData = 0;5401 154 uint32_t u32Width = 0; 5402 155 uint32_t u32Height = 0; 5403 156 5404 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5405 5406 if (RT_FAILURE(vrc)) 5407 return setError(VBOX_E_IPRT_ERROR, 5408 tr("Saved screenshot data is not available (%Rrc)"), 5409 vrc); 5410 5411 *aSize = cbData; 5412 *aWidth = u32Width; 5413 *aHeight = u32Height; 5414 5415 freeSavedDisplayScreenshot(pu8Data); 5416 5417 return S_OK; 5418 } 5419 5420 STDMETHODIMP Machine::ReadSavedThumbnailToArray(ULONG aScreenId, BOOL aBGR, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 5421 { 5422 LogFlowThisFunc(("\n")); 5423 5424 CheckComArgNotNull(aWidth); 5425 CheckComArgNotNull(aHeight); 5426 CheckComArgOutSafeArrayPointerValid(aData); 5427 5428 if (aScreenId != 0) 5429 return E_NOTIMPL; 5430 5431 AutoCaller autoCaller(this); 5432 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5433 5434 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5435 5436 uint8_t *pu8Data = NULL; 5437 uint32_t cbData = 0; 5438 uint32_t u32Width = 0; 5439 uint32_t u32Height = 0; 5440 5441 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5442 5443 if (RT_FAILURE(vrc)) 5444 return setError(VBOX_E_IPRT_ERROR, 5445 tr("Saved screenshot data is not available (%Rrc)"), 5446 vrc); 5447 5448 *aWidth = u32Width; 5449 *aHeight = u32Height; 5450 5451 com::SafeArray<BYTE> bitmap(cbData); 5452 /* Convert pixels to format expected by the API caller. */ 5453 if (aBGR) 5454 { 5455 /* [0] B, [1] G, [2] R, [3] A. */ 5456 for (unsigned i = 0; i < cbData; i += 4) 5457 { 5458 bitmap[i] = pu8Data[i]; 5459 bitmap[i + 1] = pu8Data[i + 1]; 5460 bitmap[i + 2] = pu8Data[i + 2]; 5461 bitmap[i + 3] = 0xff; 5462 } 5463 } 5464 else 5465 { 5466 /* [0] R, [1] G, [2] B, [3] A. */ 5467 for (unsigned i = 0; i < cbData; i += 4) 5468 { 5469 bitmap[i] = pu8Data[i + 2]; 5470 bitmap[i + 1] = pu8Data[i + 1]; 5471 bitmap[i + 2] = pu8Data[i]; 5472 bitmap[i + 3] = 0xff; 5473 } 5474 } 5475 bitmap.detachTo(ComSafeArrayOutArg(aData)); 5476 5477 freeSavedDisplayScreenshot(pu8Data); 5478 5479 return S_OK; 5480 } 5481 5482 5483 STDMETHODIMP Machine::ReadSavedThumbnailPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 5484 { 5485 LogFlowThisFunc(("\n")); 5486 5487 CheckComArgNotNull(aWidth); 5488 CheckComArgNotNull(aHeight); 5489 CheckComArgOutSafeArrayPointerValid(aData); 5490 5491 if (aScreenId != 0) 5492 return E_NOTIMPL; 5493 5494 AutoCaller autoCaller(this); 5495 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5496 5497 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5498 5499 uint8_t *pu8Data = NULL; 5500 uint32_t cbData = 0; 5501 uint32_t u32Width = 0; 5502 uint32_t u32Height = 0; 5503 5504 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 0 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5505 5506 if (RT_FAILURE(vrc)) 5507 return setError(VBOX_E_IPRT_ERROR, 5508 tr("Saved screenshot data is not available (%Rrc)"), 5509 vrc); 5510 5511 *aWidth = u32Width; 5512 *aHeight = u32Height; 5513 5514 uint8_t *pu8PNG = NULL; 5515 uint32_t cbPNG = 0; 5516 uint32_t cxPNG = 0; 5517 uint32_t cyPNG = 0; 5518 5519 DisplayMakePNG(pu8Data, u32Width, u32Height, &pu8PNG, &cbPNG, &cxPNG, &cyPNG, 0); 5520 5521 com::SafeArray<BYTE> screenData(cbPNG); 5522 screenData.initFrom(pu8PNG, cbPNG); 5523 RTMemFree(pu8PNG); 5524 5525 screenData.detachTo(ComSafeArrayOutArg(aData)); 5526 5527 freeSavedDisplayScreenshot(pu8Data); 5528 5529 return S_OK; 5530 } 5531 5532 STDMETHODIMP Machine::QuerySavedScreenshotPNGSize(ULONG aScreenId, ULONG *aSize, ULONG *aWidth, ULONG *aHeight) 5533 { 5534 LogFlowThisFunc(("\n")); 5535 5536 CheckComArgNotNull(aSize); 5537 CheckComArgNotNull(aWidth); 5538 CheckComArgNotNull(aHeight); 5539 5540 if (aScreenId != 0) 5541 return E_NOTIMPL; 5542 5543 AutoCaller autoCaller(this); 5544 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5545 5546 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5547 5548 uint8_t *pu8Data = NULL; 5549 uint32_t cbData = 0; 5550 uint32_t u32Width = 0; 5551 uint32_t u32Height = 0; 5552 5553 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5554 5555 if (RT_FAILURE(vrc)) 5556 return setError(VBOX_E_IPRT_ERROR, 5557 tr("Saved screenshot data is not available (%Rrc)"), 5558 vrc); 5559 5560 *aSize = cbData; 5561 *aWidth = u32Width; 5562 *aHeight = u32Height; 5563 5564 freeSavedDisplayScreenshot(pu8Data); 5565 5566 return S_OK; 5567 } 5568 5569 STDMETHODIMP Machine::ReadSavedScreenshotPNGToArray(ULONG aScreenId, ULONG *aWidth, ULONG *aHeight, ComSafeArrayOut(BYTE, aData)) 5570 { 5571 LogFlowThisFunc(("\n")); 5572 5573 CheckComArgNotNull(aWidth); 5574 CheckComArgNotNull(aHeight); 5575 CheckComArgOutSafeArrayPointerValid(aData); 5576 5577 if (aScreenId != 0) 5578 return E_NOTIMPL; 5579 5580 AutoCaller autoCaller(this); 5581 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5582 5583 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5584 5585 uint8_t *pu8Data = NULL; 5586 uint32_t cbData = 0; 5587 uint32_t u32Width = 0; 5588 uint32_t u32Height = 0; 5589 5590 int vrc = readSavedDisplayScreenshot(mSSData->mStateFilePath, 1 /* u32Type */, &pu8Data, &cbData, &u32Width, &u32Height); 5591 5592 if (RT_FAILURE(vrc)) 5593 return setError(VBOX_E_IPRT_ERROR, 5594 tr("Saved screenshot thumbnail data is not available (%Rrc)"), 5595 vrc); 5596 5597 *aWidth = u32Width; 5598 *aHeight = u32Height; 5599 5600 com::SafeArray<BYTE> png(cbData); 5601 png.initFrom(pu8Data, cbData); 5602 png.detachTo(ComSafeArrayOutArg(aData)); 5603 5604 freeSavedDisplayScreenshot(pu8Data); 5605 5606 return S_OK; 5607 } 5608 5609 STDMETHODIMP Machine::HotPlugCPU(ULONG aCpu) 5610 { 5611 HRESULT rc = S_OK; 5612 LogFlowThisFunc(("\n")); 5613 5614 AutoCaller autoCaller(this); 5615 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5616 5617 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5618 5619 if (!mHWData->mCPUHotPlugEnabled) 5620 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled")); 5621 5622 if (aCpu >= mHWData->mCPUCount) 5623 return setError(E_INVALIDARG, tr("CPU id exceeds number of possible CPUs [0:%lu]"), mHWData->mCPUCount-1); 5624 5625 if (mHWData->mCPUAttached[aCpu]) 5626 return setError(VBOX_E_OBJECT_IN_USE, tr("CPU %lu is already attached"), aCpu); 5627 5628 alock.release(); 5629 rc = onCPUChange(aCpu, false); 5630 alock.acquire(); 5631 if (FAILED(rc)) return rc; 5632 5633 setModified(IsModified_MachineData); 5634 mHWData.backup(); 5635 mHWData->mCPUAttached[aCpu] = true; 5636 5637 /* Save settings if online */ 5638 if (Global::IsOnline(mData->mMachineState)) 5639 saveSettings(NULL); 5640 5641 return S_OK; 5642 } 5643 5644 STDMETHODIMP Machine::HotUnplugCPU(ULONG aCpu) 5645 { 5646 HRESULT rc = S_OK; 5647 LogFlowThisFunc(("\n")); 5648 5649 AutoCaller autoCaller(this); 5650 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5651 5652 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 5653 5654 if (!mHWData->mCPUHotPlugEnabled) 5655 return setError(E_INVALIDARG, tr("CPU hotplug is not enabled")); 5656 5657 if (aCpu >= SchemaDefs::MaxCPUCount) 5658 return setError(E_INVALIDARG, 5659 tr("CPU index exceeds maximum CPU count (must be in range [0:%lu])"), 5660 SchemaDefs::MaxCPUCount); 5661 5662 if (!mHWData->mCPUAttached[aCpu]) 5663 return setError(VBOX_E_OBJECT_NOT_FOUND, tr("CPU %lu is not attached"), aCpu); 5664 5665 /* CPU 0 can't be detached */ 5666 if (aCpu == 0) 5667 return setError(E_INVALIDARG, tr("It is not possible to detach CPU 0")); 5668 5669 alock.release(); 5670 rc = onCPUChange(aCpu, true); 5671 alock.acquire(); 5672 if (FAILED(rc)) return rc; 5673 5674 setModified(IsModified_MachineData); 5675 mHWData.backup(); 5676 mHWData->mCPUAttached[aCpu] = false; 5677 5678 /* Save settings if online */ 5679 if (Global::IsOnline(mData->mMachineState)) 5680 saveSettings(NULL); 5681 5682 return S_OK; 5683 } 5684 5685 STDMETHODIMP Machine::GetCPUStatus(ULONG aCpu, BOOL *aCpuAttached) 5686 { 5687 LogFlowThisFunc(("\n")); 5688 5689 CheckComArgNotNull(aCpuAttached); 5690 5691 *aCpuAttached = false; 5692 5693 AutoCaller autoCaller(this); 5694 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5695 5696 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5697 5698 /* If hotplug is enabled the CPU is always enabled. */ 5699 if (!mHWData->mCPUHotPlugEnabled) 5700 { 5701 if (aCpu < mHWData->mCPUCount) 5702 *aCpuAttached = true; 5703 } 5704 else 5705 { 5706 if (aCpu < SchemaDefs::MaxCPUCount) 5707 *aCpuAttached = mHWData->mCPUAttached[aCpu]; 5708 } 5709 5710 return S_OK; 5711 } 5712 5713 STDMETHODIMP Machine::QueryLogFilename(ULONG aIdx, BSTR *aName) 5714 { 5715 CheckComArgOutPointerValid(aName); 5716 5717 AutoCaller autoCaller(this); 5718 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5719 5720 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5721 5722 Utf8Str log = queryLogFilename(aIdx); 5723 if (!RTFileExists(log.c_str())) 5724 log.setNull(); 5725 log.cloneTo(aName); 5726 5727 return S_OK; 5728 } 5729 5730 STDMETHODIMP Machine::ReadLog(ULONG aIdx, LONG64 aOffset, LONG64 aSize, ComSafeArrayOut(BYTE, aData)) 5731 { 5732 LogFlowThisFunc(("\n")); 5733 CheckComArgOutSafeArrayPointerValid(aData); 5734 if (aSize < 0) 5735 return setError(E_INVALIDARG, tr("The size argument (%lld) is negative"), aSize); 5736 5737 AutoCaller autoCaller(this); 5738 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 5739 5740 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 5741 5742 HRESULT rc = S_OK; 5743 Utf8Str log = queryLogFilename(aIdx); 5744 5745 /* do not unnecessarily hold the lock while doing something which does 5746 * not need the lock and potentially takes a long time. */ 5747 alock.release(); 5748 5749 /* Limit the chunk size to 32K for now, as that gives better performance 5750 * over (XP)COM, and keeps the SOAP reply size under 1M for the webservice. 5751 * One byte expands to approx. 25 bytes of breathtaking XML. */ 5752 size_t cbData = (size_t)RT_MIN(aSize, 32768); 5753 com::SafeArray<BYTE> logData(cbData); 5754 5755 RTFILE LogFile; 5756 int vrc = RTFileOpen(&LogFile, log.c_str(), 5757 RTFILE_O_OPEN | RTFILE_O_READ | RTFILE_O_DENY_NONE); 157 PSSMHANDLE pSSM; 158 int vrc = SSMR3Open(strStateFilePath.c_str(), 0 /*fFlags*/, &pSSM); 5758 159 if (RT_SUCCESS(vrc)) 5759 160 { 5760 vrc = RTFileReadAt(LogFile, aOffset, logData.raw(), cbData, &cbData); 161 uint32_t uVersion; 162 vrc = SSMR3Seek(pSSM, "DisplayData", 0 /*iInstance*/, &uVersion); 5761 163 if (RT_SUCCESS(vrc)) 5762 logData.resize(cbData);5763 else5764 rc = setError(VBOX_E_IPRT_ERROR,5765 tr("Could not read log file '%s' (%Rrc)"),5766 log.c_str(), vrc);5767 RTFileClose(LogFile);5768 }5769 else5770 rc = setError(VBOX_E_IPRT_ERROR,5771 tr("Could not open log file '%s' (%Rrc)"),5772 log.c_str(), vrc);5773 5774 if (FAILED(rc))5775 logData.resize(0);5776 logData.detachTo(ComSafeArrayOutArg(aData));5777 5778 return rc;5779 }5780 5781 5782 // public methods for internal purposes5783 /////////////////////////////////////////////////////////////////////////////5784 5785 /**5786 * Adds the given IsModified_* flag to the dirty flags of the machine.5787 * This must be called either during loadSettings or under the machine write lock.5788 * @param fl5789 */5790 void Machine::setModified(uint32_t fl)5791 {5792 mData->flModifications |= fl;5793 }5794 5795 /**5796 * Saves the registry entry of this machine to the given configuration node.5797 *5798 * @param aEntryNode Node to save the registry entry to.5799 *5800 * @note locks this object for reading.5801 */5802 HRESULT Machine::saveRegistryEntry(settings::MachineRegistryEntry &data)5803 {5804 AutoLimitedCaller autoCaller(this);5805 AssertComRCReturnRC(autoCaller.rc());5806 5807 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);5808 5809 data.uuid = mData->mUuid;5810 data.strSettingsFile = mData->m_strConfigFile;5811 5812 return S_OK;5813 }5814 5815 /**5816 * Calculates the absolute path of the given path taking the directory of the5817 * machine settings file as the current directory.5818 *5819 * @param aPath Path to calculate the absolute path for.5820 * @param aResult Where to put the result (used only on success, can be the5821 * same Utf8Str instance as passed in @a aPath).5822 * @return IPRT result.5823 *5824 * @note Locks this object for reading.5825 */5826 int Machine::calculateFullPath(const Utf8Str &strPath, Utf8Str &aResult)5827 {5828 AutoCaller autoCaller(this);5829 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());5830 5831 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);5832 5833 AssertReturn(!mData->m_strConfigFileFull.isEmpty(), VERR_GENERAL_FAILURE);5834 5835 Utf8Str strSettingsDir = mData->m_strConfigFileFull;5836 5837 strSettingsDir.stripFilename();5838 char folder[RTPATH_MAX];5839 int vrc = RTPathAbsEx(strSettingsDir.c_str(), strPath.c_str(), folder, sizeof(folder));5840 if (RT_SUCCESS(vrc))5841 aResult = folder;5842 5843 return vrc;5844 }5845 5846 /**5847 * Copies strSource to strTarget, making it relative to the machine folder5848 * if it is a subdirectory thereof, or simply copying it otherwise.5849 *5850 * @param strSource Path to evalue and copy.5851 * @param strTarget Buffer to receive target path.5852 *5853 * @note Locks this object for reading.5854 */5855 void Machine::copyPathRelativeToMachine(const Utf8Str &strSource,5856 Utf8Str &strTarget)5857 {5858 AutoCaller autoCaller(this);5859 AssertComRCReturn(autoCaller.rc(), (void)0);5860 5861 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);5862 5863 AssertReturnVoid(!mData->m_strConfigFileFull.isEmpty());5864 // use strTarget as a temporary buffer to hold the machine settings dir5865 strTarget = mData->m_strConfigFileFull;5866 strTarget.stripFilename();5867 if (RTPathStartsWith(strSource.c_str(), strTarget.c_str()))5868 // is relative: then append what's left5869 strTarget.append(strSource.c_str() + strTarget.length()); // include '/'5870 else5871 // is not relative: then overwrite5872 strTarget = strSource;5873 }5874 5875 /**5876 * Returns the full path to the machine's log folder in the5877 * \a aLogFolder argument.5878 */5879 void Machine::getLogFolder(Utf8Str &aLogFolder)5880 {5881 AutoCaller autoCaller(this);5882 AssertComRCReturnVoid(autoCaller.rc());5883 5884 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);5885 5886 Utf8Str settingsDir;5887 if (isInOwnDir(&settingsDir))5888 /* Log folder is <Machines>/<VM_Name>/Logs */5889 aLogFolder = settingsDir;5890 else5891 {5892 /* Log folder is <Machines>/<VM_SnapshotFolder>/Logs */5893 Assert(!mUserData->m_strSnapshotFolderFull.isEmpty());5894 aLogFolder = mUserData->m_strSnapshotFolderFull;5895 }5896 5897 aLogFolder.append(RTPATH_DELIMITER);5898 aLogFolder.append("Logs");5899 }5900 5901 /**5902 * Returns the full path to the machine's log file for an given index.5903 */5904 Utf8Str Machine::queryLogFilename(ULONG idx)5905 {5906 Utf8Str logFolder;5907 getLogFolder(logFolder);5908 Assert(logFolder.length());5909 Utf8Str log;5910 if (idx == 0)5911 log = Utf8StrFmt("%s%cVBox.log",5912 logFolder.c_str(), RTPATH_DELIMITER);5913 else5914 log = Utf8StrFmt("%s%cVBox.log.%d",5915 logFolder.c_str(), RTPATH_DELIMITER, idx);5916 return log;5917 }5918 5919 /**5920 * @note Locks this object for writing, calls the client process5921 * (inside the lock).5922 */5923 HRESULT Machine::openRemoteSession(IInternalSessionControl *aControl,5924 IN_BSTR aType,5925 IN_BSTR aEnvironment,5926 ProgressProxy *aProgress)5927 {5928 LogFlowThisFuncEnter();5929 5930 AssertReturn(aControl, E_FAIL);5931 AssertReturn(aProgress, E_FAIL);5932 5933 AutoCaller autoCaller(this);5934 if (FAILED(autoCaller.rc())) return autoCaller.rc();5935 5936 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);5937 5938 if (!mData->mRegistered)5939 return setError(E_UNEXPECTED,5940 tr("The machine '%s' is not registered"),5941 mUserData->s.strName.c_str());5942 5943 LogFlowThisFunc(("mSession.mState=%s\n", Global::stringifySessionState(mData->mSession.mState)));5944 5945 if ( mData->mSession.mState == SessionState_Locked5946 || mData->mSession.mState == SessionState_Spawning5947 || mData->mSession.mState == SessionState_Unlocking)5948 return setError(VBOX_E_INVALID_OBJECT_STATE,5949 tr("The machine '%s' is already locked by a session (or being locked or unlocked)"),5950 mUserData->s.strName.c_str());5951 5952 /* may not be busy */5953 AssertReturn(!Global::IsOnlineOrTransient(mData->mMachineState), E_FAIL);5954 5955 /* get the path to the executable */5956 char szPath[RTPATH_MAX];5957 RTPathAppPrivateArch(szPath, RTPATH_MAX);5958 size_t sz = strlen(szPath);5959 szPath[sz++] = RTPATH_DELIMITER;5960 szPath[sz] = 0;5961 char *cmd = szPath + sz;5962 sz = RTPATH_MAX - sz;5963 5964 int vrc = VINF_SUCCESS;5965 RTPROCESS pid = NIL_RTPROCESS;5966 5967 RTENV env = RTENV_DEFAULT;5968 5969 if (aEnvironment != NULL && *aEnvironment)5970 {5971 char *newEnvStr = NULL;5972 5973 do5974 164 { 5975 /* clone the current environment */ 5976 int vrc2 = RTEnvClone(&env, RTENV_DEFAULT); 5977 AssertRCBreakStmt(vrc2, vrc = vrc2); 5978 5979 newEnvStr = RTStrDup(Utf8Str(aEnvironment).c_str()); 5980 AssertPtrBreakStmt(newEnvStr, vrc = vrc2); 5981 5982 /* put new variables to the environment 5983 * (ignore empty variable names here since RTEnv API 5984 * intentionally doesn't do that) */ 5985 char *var = newEnvStr; 5986 for (char *p = newEnvStr; *p; ++p) 165 /* Only the second version is supported. */ 166 if (uVersion == sSSMDisplayVer2) 5987 167 { 5988 if (*p == '\n' && (p == newEnvStr || *(p - 1) != '\\')) 168 uint32_t cMonitors; 169 SSMR3GetU32(pSSM, &cMonitors); 170 if (u32ScreenId > cMonitors) 171 vrc = -2; 172 else 5989 173 { 5990 *p = '\0'; 5991 if (*var) 5992 { 5993 char *val = strchr(var, '='); 5994 if (val) 5995 { 5996 *val++ = '\0'; 5997 vrc2 = RTEnvSetEx(env, var, val); 5998 } 5999 else 6000 vrc2 = RTEnvUnsetEx(env, var); 6001 if (RT_FAILURE(vrc2)) 6002 break; 6003 } 6004 var = p + 1; 6005 } 6006 } 6007 if (RT_SUCCESS(vrc2) && *var) 6008 vrc2 = RTEnvPutEx(env, var); 6009 6010 AssertRCBreakStmt(vrc2, vrc = vrc2); 6011 } 6012 while (0); 6013 6014 if (newEnvStr != NULL) 6015 RTStrFree(newEnvStr); 6016 } 6017 6018 Utf8Str strType(aType); 6019 6020 /* Qt is default */ 6021 #ifdef VBOX_WITH_QTGUI 6022 if (strType == "gui" || strType == "GUI/Qt") 6023 { 6024 # ifdef RT_OS_DARWIN /* Avoid Launch Services confusing this with the selector by using a helper app. */ 6025 const char VirtualBox_exe[] = "../Resources/VirtualBoxVM.app/Contents/MacOS/VirtualBoxVM"; 6026 # else 6027 const char VirtualBox_exe[] = "VirtualBox" HOSTSUFF_EXE; 6028 # endif 6029 Assert(sz >= sizeof(VirtualBox_exe)); 6030 strcpy(cmd, VirtualBox_exe); 6031 6032 Utf8Str idStr = mData->mUuid.toString(); 6033 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), "--no-startvm-errormsgbox", 0 }; 6034 vrc = RTProcCreate(szPath, args, env, 0, &pid); 6035 } 6036 #else /* !VBOX_WITH_QTGUI */ 6037 if (0) 6038 ; 6039 #endif /* VBOX_WITH_QTGUI */ 6040 6041 else 6042 6043 #ifdef VBOX_WITH_VBOXSDL 6044 if (strType == "sdl" || strType == "GUI/SDL") 6045 { 6046 const char VBoxSDL_exe[] = "VBoxSDL" HOSTSUFF_EXE; 6047 Assert(sz >= sizeof(VBoxSDL_exe)); 6048 strcpy(cmd, VBoxSDL_exe); 6049 6050 Utf8Str idStr = mData->mUuid.toString(); 6051 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0 }; 6052 vrc = RTProcCreate(szPath, args, env, 0, &pid); 6053 } 6054 #else /* !VBOX_WITH_VBOXSDL */ 6055 if (0) 6056 ; 6057 #endif /* !VBOX_WITH_VBOXSDL */ 6058 6059 else 6060 6061 #ifdef VBOX_WITH_HEADLESS 6062 if ( strType == "headless" 6063 || strType == "capture" 6064 #ifdef VBOX_WITH_VRDP 6065 || strType == "vrdp" 6066 #endif 6067 ) 6068 { 6069 const char VBoxHeadless_exe[] = "VBoxHeadless" HOSTSUFF_EXE; 6070 Assert(sz >= sizeof(VBoxHeadless_exe)); 6071 strcpy(cmd, VBoxHeadless_exe); 6072 6073 Utf8Str idStr = mData->mUuid.toString(); 6074 /* Leave space for 2 args, as "headless" needs --vrdp off on non-OSE. */ 6075 const char * args[] = {szPath, "--comment", mUserData->s.strName.c_str(), "--startvm", idStr.c_str(), 0, 0, 0 }; 6076 #ifdef VBOX_WITH_VRDP 6077 if (strType == "headless") 6078 { 6079 unsigned pos = RT_ELEMENTS(args) - 3; 6080 args[pos++] = "--vrdp"; 6081 args[pos] = "off"; 6082 } 6083 #endif 6084 if (strType == "capture") 6085 { 6086 unsigned pos = RT_ELEMENTS(args) - 3; 6087 args[pos] = "--capture"; 6088 } 6089 vrc = RTProcCreate(szPath, args, env, 0, &pid); 6090 } 6091 #else /* !VBOX_WITH_HEADLESS */ 6092 if (0) 6093 ; 6094 #endif /* !VBOX_WITH_HEADLESS */ 6095 else 6096 { 6097 RTEnvDestroy(env); 6098 return setError(E_INVALIDARG, 6099 tr("Invalid session type: '%s'"), 6100 strType.c_str()); 6101 } 6102 6103 RTEnvDestroy(env); 6104 6105 if (RT_FAILURE(vrc)) 6106 return setError(VBOX_E_IPRT_ERROR, 6107 tr("Could not launch a process for the machine '%s' (%Rrc)"), 6108 mUserData->s.strName.c_str(), vrc); 6109 6110 LogFlowThisFunc(("launched.pid=%d(0x%x)\n", pid, pid)); 6111 6112 /* 6113 * Note that we don't leave the lock here before calling the client, 6114 * because it doesn't need to call us back if called with a NULL argument. 6115 * Leaving the lock herer is dangerous because we didn't prepare the 6116 * launch data yet, but the client we've just started may happen to be 6117 * too fast and call openSession() that will fail (because of PID, etc.), 6118 * so that the Machine will never get out of the Spawning session state. 6119 */ 6120 6121 /* inform the session that it will be a remote one */ 6122 LogFlowThisFunc(("Calling AssignMachine (NULL)...\n")); 6123 HRESULT rc = aControl->AssignMachine(NULL); 6124 LogFlowThisFunc(("AssignMachine (NULL) returned %08X\n", rc)); 6125 6126 if (FAILED(rc)) 6127 { 6128 /* restore the session state */ 6129 mData->mSession.mState = SessionState_Unlocked; 6130 /* The failure may occur w/o any error info (from RPC), so provide one */ 6131 return setError(VBOX_E_VM_ERROR, 6132 tr("Failed to assign the machine to the session (%Rrc)"), rc); 6133 } 6134 6135 /* attach launch data to the machine */ 6136 Assert(mData->mSession.mPid == NIL_RTPROCESS); 6137 mData->mSession.mRemoteControls.push_back (aControl); 6138 mData->mSession.mProgress = aProgress; 6139 mData->mSession.mPid = pid; 6140 mData->mSession.mState = SessionState_Spawning; 6141 mData->mSession.mType = strType; 6142 6143 LogFlowThisFuncLeave(); 6144 return S_OK; 6145 } 6146 6147 /** 6148 * Returns @c true if the given machine has an open direct session and returns 6149 * the session machine instance and additional session data (on some platforms) 6150 * if so. 6151 * 6152 * Note that when the method returns @c false, the arguments remain unchanged. 6153 * 6154 * @param aMachine Session machine object. 6155 * @param aControl Direct session control object (optional). 6156 * @param aIPCSem Mutex IPC semaphore handle for this machine (optional). 6157 * 6158 * @note locks this object for reading. 6159 */ 6160 #if defined(RT_OS_WINDOWS) 6161 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 6162 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 6163 HANDLE *aIPCSem /*= NULL*/, 6164 bool aAllowClosing /*= false*/) 6165 #elif defined(RT_OS_OS2) 6166 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 6167 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 6168 HMTX *aIPCSem /*= NULL*/, 6169 bool aAllowClosing /*= false*/) 6170 #else 6171 bool Machine::isSessionOpen(ComObjPtr<SessionMachine> &aMachine, 6172 ComPtr<IInternalSessionControl> *aControl /*= NULL*/, 6173 bool aAllowClosing /*= false*/) 6174 #endif 6175 { 6176 AutoLimitedCaller autoCaller(this); 6177 AssertComRCReturn(autoCaller.rc(), false); 6178 6179 /* just return false for inaccessible machines */ 6180 if (autoCaller.state() != Ready) 6181 return false; 6182 6183 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6184 6185 if ( mData->mSession.mState == SessionState_Locked 6186 || (aAllowClosing && mData->mSession.mState == SessionState_Unlocking) 6187 ) 6188 { 6189 AssertReturn(!mData->mSession.mMachine.isNull(), false); 6190 6191 aMachine = mData->mSession.mMachine; 6192 6193 if (aControl != NULL) 6194 *aControl = mData->mSession.mDirectControl; 6195 6196 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 6197 /* Additional session data */ 6198 if (aIPCSem != NULL) 6199 *aIPCSem = aMachine->mIPCSem; 6200 #endif 6201 return true; 6202 } 6203 6204 return false; 6205 } 6206 6207 /** 6208 * Returns @c true if the given machine has an spawning direct session and 6209 * returns and additional session data (on some platforms) if so. 6210 * 6211 * Note that when the method returns @c false, the arguments remain unchanged. 6212 * 6213 * @param aPID PID of the spawned direct session process. 6214 * 6215 * @note locks this object for reading. 6216 */ 6217 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 6218 bool Machine::isSessionSpawning(RTPROCESS *aPID /*= NULL*/) 6219 #else 6220 bool Machine::isSessionSpawning() 6221 #endif 6222 { 6223 AutoLimitedCaller autoCaller(this); 6224 AssertComRCReturn(autoCaller.rc(), false); 6225 6226 /* just return false for inaccessible machines */ 6227 if (autoCaller.state() != Ready) 6228 return false; 6229 6230 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 6231 6232 if (mData->mSession.mState == SessionState_Spawning) 6233 { 6234 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 6235 /* Additional session data */ 6236 if (aPID != NULL) 6237 { 6238 AssertReturn(mData->mSession.mPid != NIL_RTPROCESS, false); 6239 *aPID = mData->mSession.mPid; 6240 } 6241 #endif 6242 return true; 6243 } 6244 6245 return false; 6246 } 6247 6248 /** 6249 * Called from the client watcher thread to check for unexpected client process 6250 * death during Session_Spawning state (e.g. before it successfully opened a 6251 * direct session). 6252 * 6253 * On Win32 and on OS/2, this method is called only when we've got the 6254 * direct client's process termination notification, so it always returns @c 6255 * true. 6256 * 6257 * On other platforms, this method returns @c true if the client process is 6258 * terminated and @c false if it's still alive. 6259 * 6260 * @note Locks this object for writing. 6261 */ 6262 bool Machine::checkForSpawnFailure() 6263 { 6264 AutoCaller autoCaller(this); 6265 if (!autoCaller.isOk()) 6266 { 6267 /* nothing to do */ 6268 LogFlowThisFunc(("Already uninitialized!\n")); 6269 return true; 6270 } 6271 6272 /* VirtualBox::addProcessToReap() needs a write lock */ 6273 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS); 6274 6275 if (mData->mSession.mState != SessionState_Spawning) 6276 { 6277 /* nothing to do */ 6278 LogFlowThisFunc(("Not spawning any more!\n")); 6279 return true; 6280 } 6281 6282 HRESULT rc = S_OK; 6283 6284 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2) 6285 6286 /* the process was already unexpectedly terminated, we just need to set an 6287 * error and finalize session spawning */ 6288 rc = setError(E_FAIL, 6289 tr("The virtual machine '%ls' has terminated unexpectedly during startup"), 6290 getName().c_str()); 6291 #else 6292 6293 /* PID not yet initialized, skip check. */ 6294 if (mData->mSession.mPid == NIL_RTPROCESS) 6295 return false; 6296 6297 RTPROCSTATUS status; 6298 int vrc = ::RTProcWait(mData->mSession.mPid, RTPROCWAIT_FLAGS_NOBLOCK, 6299 &status); 6300 6301 if (vrc != VERR_PROCESS_RUNNING) 6302 { 6303 if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_NORMAL) 6304 rc = setError(E_FAIL, 6305 tr("The virtual machine '%s' has terminated unexpectedly during startup with exit code %d"), 6306 getName().c_str(), status.iStatus); 6307 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_SIGNAL) 6308 rc = setError(E_FAIL, 6309 tr("The virtual machine '%s' has terminated unexpectedly during startup because of signal %d"), 6310 getName().c_str(), status.iStatus); 6311 else if (RT_SUCCESS(vrc) && status.enmReason == RTPROCEXITREASON_ABEND) 6312 rc = setError(E_FAIL, 6313 tr("The virtual machine '%s' has terminated abnormally"), 6314 getName().c_str(), status.iStatus); 6315 else 6316 rc = setError(E_FAIL, 6317 tr("The virtual machine '%s' has terminated unexpectedly during startup (%Rrc)"), 6318 getName().c_str(), rc); 6319 } 6320 6321 #endif 6322 6323 if (FAILED(rc)) 6324 { 6325 /* Close the remote session, remove the remote control from the list 6326 * and reset session state to Closed (@note keep the code in sync with 6327 * the relevant part in checkForSpawnFailure()). */ 6328 6329 Assert(mData->mSession.mRemoteControls.size() == 1); 6330 if (mData->mSession.mRemoteControls.size() == 1) 6331 { 6332 ErrorInfoKeeper eik; 6333 mData->mSession.mRemoteControls.front()->Uninitialize(); 6334 } 6335 6336 mData->mSession.mRemoteControls.clear(); 6337 mData->mSession.mState = SessionState_Unlocked; 6338 6339 /* finalize the progress after setting the state */ 6340 if (!mData->mSession.mProgress.isNull()) 6341 { 6342 mData->mSession.mProgress->notifyComplete(rc); 6343 mData->mSession.mProgress.setNull(); 6344 } 6345 6346 mParent->addProcessToReap(mData->mSession.mPid); 6347 mData->mSession.mPid = NIL_RTPROCESS; 6348 6349 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked); 6350 return true; 6351 } 6352 6353 return false; 6354 } 6355 6356 /** 6357 * Checks whether the machine can be registered. If so, commits and saves 6358 * all settings. 6359 * 6360 * @note Must be called from mParent's write lock. Locks this object and 6361 * children for writing. 6362 */ 6363 HRESULT Machine::prepareRegister() 6364 { 6365 AssertReturn(mParent->isWriteLockOnCurrentThread(), E_FAIL); 6366 6367 AutoLimitedCaller autoCaller(this); 6368 AssertComRCReturnRC(autoCaller.rc()); 6369 6370 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6371 6372 /* wait for state dependants to drop to zero */ 6373 ensureNoStateDependencies(); 6374 6375 if (!mData->mAccessible) 6376 return setError(VBOX_E_INVALID_OBJECT_STATE, 6377 tr("The machine '%s' with UUID {%s} is inaccessible and cannot be registered"), 6378 mUserData->s.strName.c_str(), 6379 mData->mUuid.toString().c_str()); 6380 6381 AssertReturn(autoCaller.state() == Ready, E_FAIL); 6382 6383 if (mData->mRegistered) 6384 return setError(VBOX_E_INVALID_OBJECT_STATE, 6385 tr("The machine '%s' with UUID {%s} is already registered"), 6386 mUserData->s.strName.c_str(), 6387 mData->mUuid.toString().c_str()); 6388 6389 HRESULT rc = S_OK; 6390 6391 // Ensure the settings are saved. If we are going to be registered and 6392 // no config file exists yet, create it by calling saveSettings() too. 6393 if ( (mData->flModifications) 6394 || (!mData->pMachineConfigFile->fileExists()) 6395 ) 6396 { 6397 rc = saveSettings(NULL); 6398 // no need to check whether VirtualBox.xml needs saving too since 6399 // we can't have a machine XML file rename pending 6400 if (FAILED(rc)) return rc; 6401 } 6402 6403 /* more config checking goes here */ 6404 6405 if (SUCCEEDED(rc)) 6406 { 6407 /* we may have had implicit modifications we want to fix on success */ 6408 commit(); 6409 6410 mData->mRegistered = true; 6411 } 6412 else 6413 { 6414 /* we may have had implicit modifications we want to cancel on failure*/ 6415 rollback(false /* aNotify */); 6416 } 6417 6418 return rc; 6419 } 6420 6421 /** 6422 * Increases the number of objects dependent on the machine state or on the 6423 * registered state. Guarantees that these two states will not change at least 6424 * until #releaseStateDependency() is called. 6425 * 6426 * Depending on the @a aDepType value, additional state checks may be made. 6427 * These checks will set extended error info on failure. See 6428 * #checkStateDependency() for more info. 6429 * 6430 * If this method returns a failure, the dependency is not added and the caller 6431 * is not allowed to rely on any particular machine state or registration state 6432 * value and may return the failed result code to the upper level. 6433 * 6434 * @param aDepType Dependency type to add. 6435 * @param aState Current machine state (NULL if not interested). 6436 * @param aRegistered Current registered state (NULL if not interested). 6437 * 6438 * @note Locks this object for writing. 6439 */ 6440 HRESULT Machine::addStateDependency(StateDependency aDepType /* = AnyStateDep */, 6441 MachineState_T *aState /* = NULL */, 6442 BOOL *aRegistered /* = NULL */) 6443 { 6444 AutoCaller autoCaller(this); 6445 AssertComRCReturnRC(autoCaller.rc()); 6446 6447 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6448 6449 HRESULT rc = checkStateDependency(aDepType); 6450 if (FAILED(rc)) return rc; 6451 6452 { 6453 if (mData->mMachineStateChangePending != 0) 6454 { 6455 /* ensureNoStateDependencies() is waiting for state dependencies to 6456 * drop to zero so don't add more. It may make sense to wait a bit 6457 * and retry before reporting an error (since the pending state 6458 * transition should be really quick) but let's just assert for 6459 * now to see if it ever happens on practice. */ 6460 6461 AssertFailed(); 6462 6463 return setError(E_ACCESSDENIED, 6464 tr("Machine state change is in progress. Please retry the operation later.")); 6465 } 6466 6467 ++mData->mMachineStateDeps; 6468 Assert(mData->mMachineStateDeps != 0 /* overflow */); 6469 } 6470 6471 if (aState) 6472 *aState = mData->mMachineState; 6473 if (aRegistered) 6474 *aRegistered = mData->mRegistered; 6475 6476 return S_OK; 6477 } 6478 6479 /** 6480 * Decreases the number of objects dependent on the machine state. 6481 * Must always complete the #addStateDependency() call after the state 6482 * dependency is no more necessary. 6483 */ 6484 void Machine::releaseStateDependency() 6485 { 6486 AutoCaller autoCaller(this); 6487 AssertComRCReturnVoid(autoCaller.rc()); 6488 6489 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6490 6491 /* releaseStateDependency() w/o addStateDependency()? */ 6492 AssertReturnVoid(mData->mMachineStateDeps != 0); 6493 -- mData->mMachineStateDeps; 6494 6495 if (mData->mMachineStateDeps == 0) 6496 { 6497 /* inform ensureNoStateDependencies() that there are no more deps */ 6498 if (mData->mMachineStateChangePending != 0) 6499 { 6500 Assert(mData->mMachineStateDepsSem != NIL_RTSEMEVENTMULTI); 6501 RTSemEventMultiSignal (mData->mMachineStateDepsSem); 6502 } 6503 } 6504 } 6505 6506 // protected methods 6507 ///////////////////////////////////////////////////////////////////////////// 6508 6509 /** 6510 * Performs machine state checks based on the @a aDepType value. If a check 6511 * fails, this method will set extended error info, otherwise it will return 6512 * S_OK. It is supposed, that on failure, the caller will immedieately return 6513 * the return value of this method to the upper level. 6514 * 6515 * When @a aDepType is AnyStateDep, this method always returns S_OK. 6516 * 6517 * When @a aDepType is MutableStateDep, this method returns S_OK only if the 6518 * current state of this machine object allows to change settings of the 6519 * machine (i.e. the machine is not registered, or registered but not running 6520 * and not saved). It is useful to call this method from Machine setters 6521 * before performing any change. 6522 * 6523 * When @a aDepType is MutableOrSavedStateDep, this method behaves the same 6524 * as for MutableStateDep except that if the machine is saved, S_OK is also 6525 * returned. This is useful in setters which allow changing machine 6526 * properties when it is in the saved state. 6527 * 6528 * @param aDepType Dependency type to check. 6529 * 6530 * @note Non Machine based classes should use #addStateDependency() and 6531 * #releaseStateDependency() methods or the smart AutoStateDependency 6532 * template. 6533 * 6534 * @note This method must be called from under this object's read or write 6535 * lock. 6536 */ 6537 HRESULT Machine::checkStateDependency(StateDependency aDepType) 6538 { 6539 switch (aDepType) 6540 { 6541 case AnyStateDep: 6542 { 6543 break; 6544 } 6545 case MutableStateDep: 6546 { 6547 if ( mData->mRegistered 6548 && ( !isSessionMachine() /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */ 6549 || ( mData->mMachineState != MachineState_Paused 6550 && mData->mMachineState != MachineState_Running 6551 && mData->mMachineState != MachineState_Aborted 6552 && mData->mMachineState != MachineState_Teleported 6553 && mData->mMachineState != MachineState_PoweredOff 6554 ) 6555 ) 6556 ) 6557 return setError(VBOX_E_INVALID_VM_STATE, 6558 tr("The machine is not mutable (state is %s)"), 6559 Global::stringifyMachineState(mData->mMachineState)); 6560 break; 6561 } 6562 case MutableOrSavedStateDep: 6563 { 6564 if ( mData->mRegistered 6565 && ( !isSessionMachine() /** @todo This was just convered raw; Check if Running and Paused should actually be included here... (Live Migration) */ 6566 || ( mData->mMachineState != MachineState_Paused 6567 && mData->mMachineState != MachineState_Running 6568 && mData->mMachineState != MachineState_Aborted 6569 && mData->mMachineState != MachineState_Teleported 6570 && mData->mMachineState != MachineState_Saved 6571 && mData->mMachineState != MachineState_PoweredOff 6572 ) 6573 ) 6574 ) 6575 return setError(VBOX_E_INVALID_VM_STATE, 6576 tr("The machine is not mutable (state is %s)"), 6577 Global::stringifyMachineState(mData->mMachineState)); 6578 break; 6579 } 6580 } 6581 6582 return S_OK; 6583 } 6584 6585 /** 6586 * Helper to initialize all associated child objects and allocate data 6587 * structures. 6588 * 6589 * This method must be called as a part of the object's initialization procedure 6590 * (usually done in the #init() method). 6591 * 6592 * @note Must be called only from #init() or from #registeredInit(). 6593 */ 6594 HRESULT Machine::initDataAndChildObjects() 6595 { 6596 AutoCaller autoCaller(this); 6597 AssertComRCReturnRC(autoCaller.rc()); 6598 AssertComRCReturn(autoCaller.state() == InInit || 6599 autoCaller.state() == Limited, E_FAIL); 6600 6601 AssertReturn(!mData->mAccessible, E_FAIL); 6602 6603 /* allocate data structures */ 6604 mSSData.allocate(); 6605 mUserData.allocate(); 6606 mHWData.allocate(); 6607 mMediaData.allocate(); 6608 mStorageControllers.allocate(); 6609 6610 /* initialize mOSTypeId */ 6611 mUserData->s.strOsType = mParent->getUnknownOSType()->id(); 6612 6613 /* create associated BIOS settings object */ 6614 unconst(mBIOSSettings).createObject(); 6615 mBIOSSettings->init(this); 6616 6617 #ifdef VBOX_WITH_VRDP 6618 /* create an associated VRDPServer object (default is disabled) */ 6619 unconst(mVRDPServer).createObject(); 6620 mVRDPServer->init(this); 6621 #endif 6622 6623 /* create associated serial port objects */ 6624 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 6625 { 6626 unconst(mSerialPorts[slot]).createObject(); 6627 mSerialPorts[slot]->init(this, slot); 6628 } 6629 6630 /* create associated parallel port objects */ 6631 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 6632 { 6633 unconst(mParallelPorts[slot]).createObject(); 6634 mParallelPorts[slot]->init(this, slot); 6635 } 6636 6637 /* create the audio adapter object (always present, default is disabled) */ 6638 unconst(mAudioAdapter).createObject(); 6639 mAudioAdapter->init(this); 6640 6641 /* create the USB controller object (always present, default is disabled) */ 6642 unconst(mUSBController).createObject(); 6643 mUSBController->init(this); 6644 6645 /* create associated network adapter objects */ 6646 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot ++) 6647 { 6648 unconst(mNetworkAdapters[slot]).createObject(); 6649 mNetworkAdapters[slot]->init(this, slot); 6650 } 6651 6652 return S_OK; 6653 } 6654 6655 /** 6656 * Helper to uninitialize all associated child objects and to free all data 6657 * structures. 6658 * 6659 * This method must be called as a part of the object's uninitialization 6660 * procedure (usually done in the #uninit() method). 6661 * 6662 * @note Must be called only from #uninit() or from #registeredInit(). 6663 */ 6664 void Machine::uninitDataAndChildObjects() 6665 { 6666 AutoCaller autoCaller(this); 6667 AssertComRCReturnVoid(autoCaller.rc()); 6668 AssertComRCReturnVoid( autoCaller.state() == InUninit 6669 || autoCaller.state() == Limited); 6670 6671 /* uninit all children using addDependentChild()/removeDependentChild() 6672 * in their init()/uninit() methods */ 6673 uninitDependentChildren(); 6674 6675 /* tell all our other child objects we've been uninitialized */ 6676 6677 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++) 6678 { 6679 if (mNetworkAdapters[slot]) 6680 { 6681 mNetworkAdapters[slot]->uninit(); 6682 unconst(mNetworkAdapters[slot]).setNull(); 6683 } 6684 } 6685 6686 if (mUSBController) 6687 { 6688 mUSBController->uninit(); 6689 unconst(mUSBController).setNull(); 6690 } 6691 6692 if (mAudioAdapter) 6693 { 6694 mAudioAdapter->uninit(); 6695 unconst(mAudioAdapter).setNull(); 6696 } 6697 6698 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++) 6699 { 6700 if (mParallelPorts[slot]) 6701 { 6702 mParallelPorts[slot]->uninit(); 6703 unconst(mParallelPorts[slot]).setNull(); 6704 } 6705 } 6706 6707 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++) 6708 { 6709 if (mSerialPorts[slot]) 6710 { 6711 mSerialPorts[slot]->uninit(); 6712 unconst(mSerialPorts[slot]).setNull(); 6713 } 6714 } 6715 6716 #ifdef VBOX_WITH_VRDP 6717 if (mVRDPServer) 6718 { 6719 mVRDPServer->uninit(); 6720 unconst(mVRDPServer).setNull(); 6721 } 6722 #endif 6723 6724 if (mBIOSSettings) 6725 { 6726 mBIOSSettings->uninit(); 6727 unconst(mBIOSSettings).setNull(); 6728 } 6729 6730 /* Deassociate hard disks (only when a real Machine or a SnapshotMachine 6731 * instance is uninitialized; SessionMachine instances refer to real 6732 * Machine hard disks). This is necessary for a clean re-initialization of 6733 * the VM after successfully re-checking the accessibility state. Note 6734 * that in case of normal Machine or SnapshotMachine uninitialization (as 6735 * a result of unregistering or deleting the snapshot), outdated hard 6736 * disk attachments will already be uninitialized and deleted, so this 6737 * code will not affect them. */ 6738 if ( !!mMediaData 6739 && (!isSessionMachine()) 6740 ) 6741 { 6742 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin(); 6743 it != mMediaData->mAttachments.end(); 6744 ++it) 6745 { 6746 ComObjPtr<Medium> hd = (*it)->getMedium(); 6747 if (hd.isNull()) 6748 continue; 6749 HRESULT rc = hd->removeBackReference(mData->mUuid, getSnapshotId()); 6750 AssertComRC(rc); 6751 } 6752 } 6753 6754 if (!isSessionMachine() && !isSnapshotMachine()) 6755 { 6756 // clean up the snapshots list (Snapshot::uninit() will handle the snapshot's children recursively) 6757 if (mData->mFirstSnapshot) 6758 { 6759 // snapshots tree is protected by media write lock; strictly 6760 // this isn't necessary here since we're deleting the entire 6761 // machine, but otherwise we assert in Snapshot::uninit() 6762 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6763 mData->mFirstSnapshot->uninit(); 6764 mData->mFirstSnapshot.setNull(); 6765 } 6766 6767 mData->mCurrentSnapshot.setNull(); 6768 } 6769 6770 /* free data structures (the essential mData structure is not freed here 6771 * since it may be still in use) */ 6772 mMediaData.free(); 6773 mStorageControllers.free(); 6774 mHWData.free(); 6775 mUserData.free(); 6776 mSSData.free(); 6777 } 6778 6779 /** 6780 * Returns a pointer to the Machine object for this machine that acts like a 6781 * parent for complex machine data objects such as shared folders, etc. 6782 * 6783 * For primary Machine objects and for SnapshotMachine objects, returns this 6784 * object's pointer itself. For SessoinMachine objects, returns the peer 6785 * (primary) machine pointer. 6786 */ 6787 Machine* Machine::getMachine() 6788 { 6789 if (isSessionMachine()) 6790 return (Machine*)mPeer; 6791 return this; 6792 } 6793 6794 /** 6795 * Makes sure that there are no machine state dependants. If necessary, waits 6796 * for the number of dependants to drop to zero. 6797 * 6798 * Make sure this method is called from under this object's write lock to 6799 * guarantee that no new dependants may be added when this method returns 6800 * control to the caller. 6801 * 6802 * @note Locks this object for writing. The lock will be released while waiting 6803 * (if necessary). 6804 * 6805 * @warning To be used only in methods that change the machine state! 6806 */ 6807 void Machine::ensureNoStateDependencies() 6808 { 6809 AssertReturnVoid(isWriteLockOnCurrentThread()); 6810 6811 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6812 6813 /* Wait for all state dependants if necessary */ 6814 if (mData->mMachineStateDeps != 0) 6815 { 6816 /* lazy semaphore creation */ 6817 if (mData->mMachineStateDepsSem == NIL_RTSEMEVENTMULTI) 6818 RTSemEventMultiCreate(&mData->mMachineStateDepsSem); 6819 6820 LogFlowThisFunc(("Waiting for state deps (%d) to drop to zero...\n", 6821 mData->mMachineStateDeps)); 6822 6823 ++mData->mMachineStateChangePending; 6824 6825 /* reset the semaphore before waiting, the last dependant will signal 6826 * it */ 6827 RTSemEventMultiReset(mData->mMachineStateDepsSem); 6828 6829 alock.leave(); 6830 6831 RTSemEventMultiWait(mData->mMachineStateDepsSem, RT_INDEFINITE_WAIT); 6832 6833 alock.enter(); 6834 6835 -- mData->mMachineStateChangePending; 6836 } 6837 } 6838 6839 /** 6840 * Changes the machine state and informs callbacks. 6841 * 6842 * This method is not intended to fail so it either returns S_OK or asserts (and 6843 * returns a failure). 6844 * 6845 * @note Locks this object for writing. 6846 */ 6847 HRESULT Machine::setMachineState(MachineState_T aMachineState) 6848 { 6849 LogFlowThisFuncEnter(); 6850 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) )); 6851 6852 AutoCaller autoCaller(this); 6853 AssertComRCReturn(autoCaller.rc(), autoCaller.rc()); 6854 6855 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS); 6856 6857 /* wait for state dependants to drop to zero */ 6858 ensureNoStateDependencies(); 6859 6860 if (mData->mMachineState != aMachineState) 6861 { 6862 mData->mMachineState = aMachineState; 6863 6864 RTTimeNow(&mData->mLastStateChange); 6865 6866 mParent->onMachineStateChange(mData->mUuid, aMachineState); 6867 } 6868 6869 LogFlowThisFuncLeave(); 6870 return S_OK; 6871 } 6872 6873 /** 6874 * Searches for a shared folder with the given logical name 6875 * in the collection of shared folders. 6876 * 6877 * @param aName logical name of the shared folder 6878 * @param aSharedFolder where to return the found object 6879 * @param aSetError whether to set the error info if the folder is 6880 * not found 6881 * @return 6882 * S_OK when found or VBOX_E_OBJECT_NOT_FOUND when not found 6883 * 6884 * @note 6885 * must be called from under the object's lock! 6886 */ 6887 HRESULT Machine::findSharedFolder(CBSTR aName, 6888 ComObjPtr<SharedFolder> &aSharedFolder, 6889 bool aSetError /* = false */) 6890 { 6891 bool found = false; 6892 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin(); 6893 !found && it != mHWData->mSharedFolders.end(); 6894 ++it) 6895 { 6896 AutoWriteLock alock(*it COMMA_LOCKVAL_SRC_POS); 6897 found = (*it)->getName() == aName; 6898 if (found) 6899 aSharedFolder = *it; 6900 } 6901 6902 HRESULT rc = found ? S_OK : VBOX_E_OBJECT_NOT_FOUND; 6903 6904 if (aSetError && !found) 6905 setError(rc, tr("Could not find a shared folder named '%ls'"), aName); 6906 6907 return rc; 6908 } 6909 6910 /** 6911 * Initializes all machine instance data from the given settings structures 6912 * from XML. The exception is the machine UUID which needs special handling 6913 * depending on the caller's use case, so the caller needs to set that herself. 6914 * 6915 * @param config 6916 * @param fAllowStorage 6917 */ 6918 HRESULT Machine::loadMachineDataFromSettings(const settings::MachineConfigFile &config) 6919 { 6920 // copy name, description, OS type, teleporter, UTC etc. 6921 mUserData->s = config.machineUserData; 6922 6923 // look up the object by Id to check it is valid 6924 ComPtr<IGuestOSType> guestOSType; 6925 HRESULT rc = mParent->GetGuestOSType(Bstr(mUserData->s.strOsType), 6926 guestOSType.asOutParam()); 6927 if (FAILED(rc)) return rc; 6928 6929 // stateFile (optional) 6930 if (config.strStateFile.isEmpty()) 6931 mSSData->mStateFilePath.setNull(); 6932 else 6933 { 6934 Utf8Str stateFilePathFull(config.strStateFile); 6935 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull); 6936 if (RT_FAILURE(vrc)) 6937 return setError(E_FAIL, 6938 tr("Invalid saved state file path '%s' (%Rrc)"), 6939 config.strStateFile.c_str(), 6940 vrc); 6941 mSSData->mStateFilePath = stateFilePathFull; 6942 } 6943 6944 // snapshot folder needs special processing so set it again 6945 rc = COMSETTER(SnapshotFolder)(Bstr(config.machineUserData.strSnapshotFolder)); 6946 if (FAILED(rc)) return rc; 6947 6948 /* currentStateModified (optional, default is true) */ 6949 mData->mCurrentStateModified = config.fCurrentStateModified; 6950 6951 mData->mLastStateChange = config.timeLastStateChange; 6952 6953 /* 6954 * note: all mUserData members must be assigned prior this point because 6955 * we need to commit changes in order to let mUserData be shared by all 6956 * snapshot machine instances. 6957 */ 6958 mUserData.commitCopy(); 6959 6960 // machine registry, if present (must be loaded before snapshots) 6961 if (config.canHaveOwnMediaRegistry()) 6962 { 6963 rc = mParent->initMedia(getId(), // media registry ID == machine UUID 6964 config.mediaRegistry); 6965 if (FAILED(rc)) return rc; 6966 } 6967 6968 /* Snapshot node (optional) */ 6969 size_t cRootSnapshots; 6970 if ((cRootSnapshots = config.llFirstSnapshot.size())) 6971 { 6972 // there must be only one root snapshot 6973 Assert(cRootSnapshots == 1); 6974 6975 const settings::Snapshot &snap = config.llFirstSnapshot.front(); 6976 6977 rc = loadSnapshot(snap, 6978 config.uuidCurrentSnapshot, 6979 NULL); // no parent == first snapshot 6980 if (FAILED(rc)) return rc; 6981 } 6982 6983 // hardware data 6984 rc = loadHardware(config.hardwareMachine); 6985 if (FAILED(rc)) return rc; 6986 6987 // load storage controllers 6988 rc = loadStorageControllers(config.storageMachine); 6989 if (FAILED(rc)) return rc; 6990 6991 /* 6992 * NOTE: the assignment below must be the last thing to do, 6993 * otherwise it will be not possible to change the settings 6994 * somewehere in the code above because all setters will be 6995 * blocked by checkStateDependency(MutableStateDep). 6996 */ 6997 6998 /* set the machine state to Aborted or Saved when appropriate */ 6999 if (config.fAborted) 7000 { 7001 Assert(!mSSData->mStateFilePath.isEmpty()); 7002 mSSData->mStateFilePath.setNull(); 7003 7004 /* no need to use setMachineState() during init() */ 7005 mData->mMachineState = MachineState_Aborted; 7006 } 7007 else if (!mSSData->mStateFilePath.isEmpty()) 7008 { 7009 /* no need to use setMachineState() during init() */ 7010 mData->mMachineState = MachineState_Saved; 7011 } 7012 7013 // after loading settings, we are no longer different from the XML on disk 7014 mData->flModifications = 0; 7015 7016 return S_OK; 7017 } 7018 7019 /** 7020 * Recursively loads all snapshots starting from the given. 7021 * 7022 * @param aNode <Snapshot> node. 7023 * @param aCurSnapshotId Current snapshot ID from the settings file. 7024 * @param aParentSnapshot Parent snapshot. 7025 */ 7026 HRESULT Machine::loadSnapshot(const settings::Snapshot &data, 7027 const Guid &aCurSnapshotId, 7028 Snapshot *aParentSnapshot) 7029 { 7030 AssertReturn(!isSnapshotMachine(), E_FAIL); 7031 AssertReturn(!isSessionMachine(), E_FAIL); 7032 7033 HRESULT rc = S_OK; 7034 7035 Utf8Str strStateFile; 7036 if (!data.strStateFile.isEmpty()) 7037 { 7038 /* optional */ 7039 strStateFile = data.strStateFile; 7040 int vrc = calculateFullPath(strStateFile, strStateFile); 7041 if (RT_FAILURE(vrc)) 7042 return setError(E_FAIL, 7043 tr("Invalid saved state file path '%s' (%Rrc)"), 7044 strStateFile.c_str(), 7045 vrc); 7046 } 7047 7048 /* create a snapshot machine object */ 7049 ComObjPtr<SnapshotMachine> pSnapshotMachine; 7050 pSnapshotMachine.createObject(); 7051 rc = pSnapshotMachine->init(this, 7052 data.hardware, 7053 data.storage, 7054 data.uuid, 7055 strStateFile); 7056 if (FAILED(rc)) return rc; 7057 7058 /* create a snapshot object */ 7059 ComObjPtr<Snapshot> pSnapshot; 7060 pSnapshot.createObject(); 7061 /* initialize the snapshot */ 7062 rc = pSnapshot->init(mParent, // VirtualBox object 7063 data.uuid, 7064 data.strName, 7065 data.strDescription, 7066 data.timestamp, 7067 pSnapshotMachine, 7068 aParentSnapshot); 7069 if (FAILED(rc)) return rc; 7070 7071 /* memorize the first snapshot if necessary */ 7072 if (!mData->mFirstSnapshot) 7073 mData->mFirstSnapshot = pSnapshot; 7074 7075 /* memorize the current snapshot when appropriate */ 7076 if ( !mData->mCurrentSnapshot 7077 && pSnapshot->getId() == aCurSnapshotId 7078 ) 7079 mData->mCurrentSnapshot = pSnapshot; 7080 7081 // now create the children 7082 for (settings::SnapshotsList::const_iterator it = data.llChildSnapshots.begin(); 7083 it != data.llChildSnapshots.end(); 7084 ++it) 7085 { 7086 const settings::Snapshot &childData = *it; 7087 // recurse 7088 rc = loadSnapshot(childData, 7089 aCurSnapshotId, 7090 pSnapshot); // parent = the one we created above 7091 if (FAILED(rc)) return rc; 7092 } 7093 7094 return rc; 7095 } 7096 7097 /** 7098 * @param aNode <Hardware> node. 7099 */ 7100 HRESULT Machine::loadHardware(const settings::Hardware &data) 7101 { 7102 AssertReturn(!isSessionMachine(), E_FAIL); 7103 7104 HRESULT rc = S_OK; 7105 7106 try 7107 { 7108 /* The hardware version attribute (optional). */ 7109 mHWData->mHWVersion = data.strVersion; 7110 mHWData->mHardwareUUID = data.uuid; 7111 7112 mHWData->mHWVirtExEnabled = data.fHardwareVirt; 7113 mHWData->mHWVirtExExclusive = data.fHardwareVirtExclusive; 7114 mHWData->mHWVirtExNestedPagingEnabled = data.fNestedPaging; 7115 mHWData->mHWVirtExLargePagesEnabled = data.fLargePages; 7116 mHWData->mHWVirtExVPIDEnabled = data.fVPID; 7117 mHWData->mHWVirtExForceEnabled = data.fHardwareVirtForce; 7118 mHWData->mPAEEnabled = data.fPAE; 7119 mHWData->mSyntheticCpu = data.fSyntheticCpu; 7120 7121 mHWData->mCPUCount = data.cCPUs; 7122 mHWData->mCPUHotPlugEnabled = data.fCpuHotPlug; 7123 mHWData->mCpuPriority = data.ulCpuPriority; 7124 7125 // cpu 7126 if (mHWData->mCPUHotPlugEnabled) 7127 { 7128 for (settings::CpuList::const_iterator it = data.llCpus.begin(); 7129 it != data.llCpus.end(); 7130 ++it) 7131 { 7132 const settings::Cpu &cpu = *it; 7133 7134 mHWData->mCPUAttached[cpu.ulId] = true; 7135 } 7136 } 7137 7138 // cpuid leafs 7139 for (settings::CpuIdLeafsList::const_iterator it = data.llCpuIdLeafs.begin(); 7140 it != data.llCpuIdLeafs.end(); 7141 ++it) 7142 { 7143 const settings::CpuIdLeaf &leaf = *it; 7144 7145 switch (leaf.ulId) 7146 { 7147 case 0x0: 7148 case 0x1: 7149 case 0x2: 7150 case 0x3: 7151 case 0x4: 7152 case 0x5: 7153 case 0x6: 7154 case 0x7: 7155 case 0x8: 7156 case 0x9: 7157 case 0xA: 7158 mHWData->mCpuIdStdLeafs[leaf.ulId] = leaf; 7159 break; 7160 7161 case 0x80000000: 7162 case 0x80000001: 7163 case 0x80000002: 7164 case 0x80000003: 7165 case 0x80000004: 7166 case 0x80000005: 7167 case 0x80000006: 7168 case 0x80000007: 7169 case 0x80000008: 7170 case 0x80000009: 7171 case 0x8000000A: 7172 mHWData->mCpuIdExtLeafs[leaf.ulId - 0x80000000] = leaf; 7173 break; 7174 7175 default: 7176 /* just ignore */ 7177 break; 7178 } 7179 } 7180 7181 mHWData->mMemorySize = data.ulMemorySizeMB; 7182 mHWData->mPageFusionEnabled = data.fPageFusionEnabled; 7183 7184 // boot order 7185 for (size_t i = 0; 7186 i < RT_ELEMENTS(mHWData->mBootOrder); 7187 i++) 7188 { 7189 settings::BootOrderMap::const_iterator it = data.mapBootOrder.find(i); 7190 if (it == data.mapBootOrder.end()) 7191 mHWData->mBootOrder[i] = DeviceType_Null; 7192 else 7193 mHWData->mBootOrder[i] = it->second; 7194 } 7195 7196 mHWData->mVRAMSize = data.ulVRAMSizeMB; 7197 mHWData->mMonitorCount = data.cMonitors; 7198 mHWData->mAccelerate3DEnabled = data.fAccelerate3D; 7199 mHWData->mAccelerate2DVideoEnabled = data.fAccelerate2DVideo; 7200 mHWData->mFirmwareType = data.firmwareType; 7201 mHWData->mPointingHidType = data.pointingHidType; 7202 mHWData->mKeyboardHidType = data.keyboardHidType; 7203 mHWData->mChipsetType = data.chipsetType; 7204 mHWData->mHpetEnabled = data.fHpetEnabled; 7205 7206 #ifdef VBOX_WITH_VRDP 7207 /* RemoteDisplay */ 7208 rc = mVRDPServer->loadSettings(data.vrdpSettings); 7209 if (FAILED(rc)) return rc; 7210 #endif 7211 7212 /* BIOS */ 7213 rc = mBIOSSettings->loadSettings(data.biosSettings); 7214 if (FAILED(rc)) return rc; 7215 7216 /* USB Controller */ 7217 rc = mUSBController->loadSettings(data.usbController); 7218 if (FAILED(rc)) return rc; 7219 7220 // network adapters 7221 for (settings::NetworkAdaptersList::const_iterator it = data.llNetworkAdapters.begin(); 7222 it != data.llNetworkAdapters.end(); 7223 ++it) 7224 { 7225 const settings::NetworkAdapter &nic = *it; 7226 7227 /* slot unicity is guaranteed by XML Schema */ 7228 AssertBreak(nic.ulSlot < RT_ELEMENTS(mNetworkAdapters)); 7229 rc = mNetworkAdapters[nic.ulSlot]->loadSettings(nic); 7230 if (FAILED(rc)) return rc; 7231 } 7232 7233 // serial ports 7234 for (settings::SerialPortsList::const_iterator it = data.llSerialPorts.begin(); 7235 it != data.llSerialPorts.end(); 7236 ++it) 7237 { 7238 const settings::SerialPort &s = *it; 7239 7240 AssertBreak(s.ulSlot < RT_ELEMENTS(mSerialPorts)); 7241 rc = mSerialPorts[s.ulSlot]->loadSettings(s); 7242 if (FAILED(rc)) return rc; 7243 } 7244 7245 // parallel ports (optional) 7246 for (settings::ParallelPortsList::const_iterator it = data.llParallelPorts.begin(); 7247 it != data.llParallelPorts.end(); 7248 ++it) 7249 { 7250 const settings::ParallelPort &p = *it; 7251 7252 AssertBreak(p.ulSlot < RT_ELEMENTS(mParallelPorts)); 7253 rc = mParallelPorts[p.ulSlot]->loadSettings(p); 7254 if (FAILED(rc)) return rc; 7255 } 7256 7257 /* AudioAdapter */ 7258 rc = mAudioAdapter->loadSettings(data.audioAdapter); 7259 if (FAILED(rc)) return rc; 7260 7261 for (settings::SharedFoldersList::const_iterator it = data.llSharedFolders.begin(); 7262 it != data.llSharedFolders.end(); 7263 ++it) 7264 { 7265 const settings::SharedFolder &sf = *it; 7266 rc = CreateSharedFolder(Bstr(sf.strName), Bstr(sf.strHostPath), sf.fWritable, sf.fAutoMount); 7267 if (FAILED(rc)) return rc; 7268 } 7269 7270 // Clipboard 7271 mHWData->mClipboardMode = data.clipboardMode; 7272 7273 // guest settings 7274 mHWData->mMemoryBalloonSize = data.ulMemoryBalloonSize; 7275 7276 // IO settings 7277 mHWData->mIoCacheEnabled = data.ioSettings.fIoCacheEnabled; 7278 mHWData->mIoCacheSize = data.ioSettings.ulIoCacheSize; 7279 7280 #ifdef VBOX_WITH_GUEST_PROPS 7281 /* Guest properties (optional) */ 7282 for (settings::GuestPropertiesList::const_iterator it = data.llGuestProperties.begin(); 7283 it != data.llGuestProperties.end(); 7284 ++it) 7285 { 7286 const settings::GuestProperty &prop = *it; 7287 uint32_t fFlags = guestProp::NILFLAG; 7288 guestProp::validateFlags(prop.strFlags.c_str(), &fFlags); 7289 HWData::GuestProperty property = { prop.strName, prop.strValue, prop.timestamp, fFlags }; 7290 mHWData->mGuestProperties.push_back(property); 7291 } 7292 7293 mHWData->mGuestPropertyNotificationPatterns = data.strNotificationPatterns; 7294 #endif /* VBOX_WITH_GUEST_PROPS defined */ 7295 } 7296 catch(std::bad_alloc &) 7297 { 7298 return E_OUTOFMEMORY; 7299 } 7300 7301 AssertComRC(rc); 7302 return rc; 7303 } 7304 7305 /** 7306 * @param aNode <StorageControllers> node. 7307 */ 7308 HRESULT Machine::loadStorageControllers(const settings::Storage &data, 7309 const Guid *aSnapshotId /* = NULL */) 7310 { 7311 AssertReturn(!isSessionMachine(), E_FAIL); 7312 7313 HRESULT rc = S_OK; 7314 7315 for (settings::StorageControllersList::const_iterator it = data.llStorageControllers.begin(); 7316 it != data.llStorageControllers.end(); 7317 ++it) 7318 { 7319 const settings::StorageController &ctlData = *it; 7320 7321 ComObjPtr<StorageController> pCtl; 7322 /* Try to find one with the name first. */ 7323 rc = getStorageControllerByName(ctlData.strName, pCtl, false /* aSetError */); 7324 if (SUCCEEDED(rc)) 7325 return setError(VBOX_E_OBJECT_IN_USE, 7326 tr("Storage controller named '%s' already exists"), 7327 ctlData.strName.c_str()); 7328 7329 pCtl.createObject(); 7330 rc = pCtl->init(this, 7331 ctlData.strName, 7332 ctlData.storageBus, 7333 ctlData.ulInstance); 7334 if (FAILED(rc)) return rc; 7335 7336 mStorageControllers->push_back(pCtl); 7337 7338 rc = pCtl->COMSETTER(ControllerType)(ctlData.controllerType); 7339 if (FAILED(rc)) return rc; 7340 7341 rc = pCtl->COMSETTER(PortCount)(ctlData.ulPortCount); 7342 if (FAILED(rc)) return rc; 7343 7344 rc = pCtl->COMSETTER(UseHostIOCache)(ctlData.fUseHostIOCache); 7345 if (FAILED(rc)) return rc; 7346 7347 /* Set IDE emulation settings (only for AHCI controller). */ 7348 if (ctlData.controllerType == StorageControllerType_IntelAhci) 7349 { 7350 if ( (FAILED(rc = pCtl->SetIDEEmulationPort(0, ctlData.lIDE0MasterEmulationPort))) 7351 || (FAILED(rc = pCtl->SetIDEEmulationPort(1, ctlData.lIDE0SlaveEmulationPort))) 7352 || (FAILED(rc = pCtl->SetIDEEmulationPort(2, ctlData.lIDE1MasterEmulationPort))) 7353 || (FAILED(rc = pCtl->SetIDEEmulationPort(3, ctlData.lIDE1SlaveEmulationPort))) 7354 ) 7355 return rc; 7356 } 7357 7358 /* Load the attached devices now. */ 7359 rc = loadStorageDevices(pCtl, 7360 ctlData, 7361 aSnapshotId); 7362 if (FAILED(rc)) return rc; 7363 } 7364 7365 return S_OK; 7366 } 7367 7368 /** 7369 * @param aNode <HardDiskAttachments> node. 7370 * @param fAllowStorage if false, we produce an error if the config requests media attachments 7371 * (used with importing unregistered machines which cannot have media attachments) 7372 * @param aSnapshotId pointer to the snapshot ID if this is a snapshot machine 7373 * 7374 * @note Lock mParent for reading and hard disks for writing before calling. 7375 */ 7376 HRESULT Machine::loadStorageDevices(StorageController *aStorageController, 7377 const settings::StorageController &data, 7378 const Guid *aSnapshotId /*= NULL*/) 7379 { 7380 HRESULT rc = S_OK; 7381 7382 /* paranoia: detect duplicate attachments */ 7383 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin(); 7384 it != data.llAttachedDevices.end(); 7385 ++it) 7386 { 7387 const settings::AttachedDevice &ad = *it; 7388 7389 for (settings::AttachedDevicesList::const_iterator it2 = it; 7390 it2 != data.llAttachedDevices.end(); 7391 ++it2) 7392 { 7393 if (it == it2) 7394 continue; 7395 7396 const settings::AttachedDevice &ad2 = *it2; 7397 7398 if ( ad.lPort == ad2.lPort 7399 && ad.lDevice == ad2.lDevice) 7400 { 7401 return setError(E_FAIL, 7402 tr("Duplicate attachments for storage controller '%s', port %d, device %d of the virtual machine '%s'"), 7403 aStorageController->getName().c_str(), 7404 ad.lPort, 7405 ad.lDevice, 7406 mUserData->s.strName.c_str()); 7407 } 7408 } 7409 } 7410 7411 for (settings::AttachedDevicesList::const_iterator it = data.llAttachedDevices.begin(); 7412 it != data.llAttachedDevices.end(); 7413 ++it) 7414 { 7415 const settings::AttachedDevice &dev = *it; 7416 ComObjPtr<Medium> medium; 7417 7418 switch (dev.deviceType) 7419 { 7420 case DeviceType_Floppy: 7421 case DeviceType_DVD: 7422 rc = mParent->findRemoveableMedium(dev.deviceType, dev.uuid, false /* fRefresh */, medium); 7423 if (FAILED(rc)) 7424 return rc; 7425 break; 7426 7427 case DeviceType_HardDisk: 7428 { 7429 /* find a hard disk by UUID */ 7430 rc = mParent->findHardDisk(&dev.uuid, Utf8Str::Empty, true /* aDoSetError */, &medium); 7431 if (FAILED(rc)) 7432 { 7433 if (isSnapshotMachine()) 7434 { 7435 // wrap another error message around the "cannot find hard disk" set by findHardDisk 7436 // so the user knows that the bad disk is in a snapshot somewhere 7437 com::ErrorInfo info; 7438 return setError(E_FAIL, 7439 tr("A differencing image of snapshot {%RTuuid} could not be found. %ls"), 7440 aSnapshotId->raw(), 7441 info.getText().raw()); 7442 } 7443 else 7444 return rc; 7445 } 7446 7447 AutoWriteLock hdLock(medium COMMA_LOCKVAL_SRC_POS); 7448 7449 if (medium->getType() == MediumType_Immutable) 7450 { 7451 if (isSnapshotMachine()) 7452 return setError(E_FAIL, 7453 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to snapshot with UUID {%RTuuid} " 7454 "of the virtual machine '%s' ('%s')"), 7455 medium->getLocationFull().c_str(), 7456 dev.uuid.raw(), 7457 aSnapshotId->raw(), 7458 mUserData->s.strName.c_str(), 7459 mData->m_strConfigFileFull.c_str()); 7460 7461 return setError(E_FAIL, 7462 tr("Immutable hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s')"), 7463 medium->getLocationFull().c_str(), 7464 dev.uuid.raw(), 7465 mUserData->s.strName.c_str(), 7466 mData->m_strConfigFileFull.c_str()); 7467 } 7468 7469 if ( !isSnapshotMachine() 7470 && medium->getChildren().size() != 0 7471 ) 7472 return setError(E_FAIL, 7473 tr("Hard disk '%s' with UUID {%RTuuid} cannot be directly attached to the virtual machine '%s' ('%s') " 7474 "because it has %d differencing child hard disks"), 7475 medium->getLocationFull().c_str(), 7476 dev.uuid.raw(), 7477 mUserData->s.strName.c_str(), 7478 mData->m_strConfigFileFull.c_str(), 7479 medium->getChildren().size()); 7480 7481 if (findAttachment(mMediaData->mAttachments, 7482 medium)) 7483 return setError(E_FAIL, 7484 tr("Hard disk '%s' with UUID {%RTuuid} is already attached to the virtual machine '%s' ('%s')"), 7485 medium->getLocationFull().c_str(), 7486 dev.uuid.raw(), 7487 mUserData->s.strName.c_str(), 7488 mData->m_strConfigFileFull.c_str()); 7489 7490 break; 7491 } 7492 7493 default: 7494 return setError(E_FAIL, 7495 tr("Device '%s' with unknown type is attached to the virtual machine '%s' ('%s')"), 7496 medium->getLocationFull().c_str(), 7497 mUserData->s.strName.c_str(), 7498 mData->m_strConfigFileFull.c_str()); 7499 } 7500 7501 if (FAILED(rc)) 7502 break; 7503 7504 const Bstr controllerName = aStorageController->getName(); 7505 ComObjPtr<MediumAttachment> pAttachment; 7506 pAttachment.createObject(); 7507 rc = pAttachment->init(this, 7508 medium, 7509 controllerName, 7510 dev.lPort, 7511 dev.lDevice, 7512 dev.deviceType, 7513 dev.fPassThrough, 7514 dev.ulBandwidthLimit); 7515 if (FAILED(rc)) break; 7516 7517 /* associate the medium with this machine and snapshot */ 7518 if (!medium.isNull()) 7519 { 7520 if (isSnapshotMachine()) 7521 rc = medium->addBackReference(mData->mUuid, *aSnapshotId); 7522 else 7523 rc = medium->addBackReference(mData->mUuid); 7524 } 7525 7526 if (FAILED(rc)) 7527 break; 7528 7529 /* back up mMediaData to let registeredInit() properly rollback on failure 7530 * (= limited accessibility) */ 7531 setModified(IsModified_Storage); 7532 mMediaData.backup(); 7533 mMediaData->mAttachments.push_back(pAttachment); 7534 } 7535 7536 return rc; 7537 } 7538 7539 /** 7540 * Returns the snapshot with the given UUID or fails of no such snapshot exists. 7541 * 7542 * @param aId snapshot UUID to find (empty UUID refers the first snapshot) 7543 * @param aSnapshot where to return the found snapshot 7544 * @param aSetError true to set extended error info on failure 7545 */ 7546 HRESULT Machine::findSnapshot(const Guid &aId, 7547 ComObjPtr<Snapshot> &aSnapshot, 7548 bool aSetError /* = false */) 7549 { 7550 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS); 7551 7552 if (!mData->mFirstSnapshot) 7553 { 7554 if (aSetError) 7555 return setError(E_FAIL, 7556 tr("This machine does not have any snapshots")); 7557 return E_FAIL; 7558 } 7559 7560 if (aId.isEmpty()) 7561 aSnapshot = mData->mFirstSnapshot; 7562 else 7563 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aId); 7564 7565 if (!aSnapshot) 7566 { 7567 if (aSetError) 7568 return setError(E_FAIL, 7569 tr("Could not find a snapshot with UUID {%s}"), 7570 aId.toString().c_str()); 7571 return E_FAIL; 7572 } 7573 7574 return S_OK; 7575 } 7576 7577 /** 7578 * Returns the snapshot with the given name or fails of no such snapshot. 7579 * 7580 * @param aName snapshot name to find 7581 * @param aSnapshot where to return the found snapshot 7582 * @param aSetError true to set extended error info on failure 7583 */ 7584 HRESULT Machine::findSnapshot(IN_BSTR aName, 7585 ComObjPtr<Snapshot> &aSnapshot, 7586 bool aSetError /* = false */) 7587 { 7588 AssertReturn(aName, E_INVALIDARG); 7589 7590 AutoReadLock chlock(this COMMA_LOCKVAL_SRC_POS); 7591 7592 if (!mData->mFirstSnapshot) 7593 { 7594 if (aSetError) 7595 return setError(VBOX_E_OBJECT_NOT_FOUND, 7596 tr("This machine does not have any snapshots")); 7597 return VBOX_E_OBJECT_NOT_FOUND; 7598 } 7599 7600 aSnapshot = mData->mFirstSnapshot->findChildOrSelf(aName); 7601 7602 if (!aSnapshot) 7603 { 7604 if (aSetError) 7605 return setError(VBOX_E_OBJECT_NOT_FOUND, 7606 tr("Could not find a snapshot named '%ls'"), aName); 7607 return VBOX_E_OBJECT_NOT_FOUND; 7608 } 7609 7610 return S_OK; 7611 } 7612 7613 /** 7614 * Returns a storage controller object with the given name. 7615 * 7616 * @param aName storage controller name to find 7617 * @param aStorageController where to return the found storage controller 7618 * @param aSetError true to set extended error info on failure 7619 */ 7620 HRESULT Machine::getStorageControllerByName(const Utf8Str &aName, 7621 ComObjPtr<StorageController> &aStorageController, 7622 bool aSetError /* = false */) 7623 { 7624 AssertReturn(!aName.isEmpty(), E_INVALIDARG); 7625 7626 for (StorageControllerList::const_iterator it = mStorageControllers->begin(); 7627 it != mStorageControllers->end(); 7628 ++it) 7629 { 7630 if ((*it)->getName() == aName) 7631 { 7632 aStorageController = (*it); 7633 return S_OK; 7634 } 7635 } 7636 7637 if (aSetError) 7638 return setError(VBOX_E_OBJECT_NOT_FOUND, 7639 tr("Could not find a storage controller named '%s'"), 7640 aName.c_str()); 7641 return VBOX_E_OBJECT_NOT_FOUND; 7642 } 7643 7644 HRESULT Machine::getMediumAttachmentsOfController(CBSTR aName, 7645 MediaData::AttachmentList &atts) 7646 { 7647 AutoCaller autoCaller(this); 7648 if (FAILED(autoCaller.rc())) return autoCaller.rc(); 7649 7650 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS); 7651 7652 for (MediaData::AttachmentList::iterator it = mMediaData->mAttachments.begin(); 7653 it != mMediaData->mAttachments.end(); 7654 ++it) 7655 { 7656 const ComObjPtr<MediumAttachment> &pAtt = *it; 7657 7658 // should never happen, but deal with NULL pointers in the list. 7659 AssertStmt(!pAtt.isNull(), continue); 7660 7661 // getControllerName() needs caller+read lock 7662 AutoCaller autoAttCaller(pAtt); 7663 if (FAILED(autoAttCaller.rc())) 7664 { 7665 atts.clear(); 7666 return autoAttCaller.rc(); 7667 } 7668 AutoReadLock attLock(pAtt COMMA_LOCKVAL_SRC_POS); 7669 7670 if (pAtt->getControllerName() == aName) 7671 atts.push_back(pAtt); 7672 } 7673 7674 return S_OK; 7675 } 7676 7677 /** 7678 * Helper for #saveSettings. Cares about renaming the settings directory and 7679 * file if the machine name was changed and about creating a new settings file 7680 * if this is a new machine. 7681 * 7682 * @note Must be never called directly but only from #saveSettings(). 7683 */ 7684 HRESULT Machine::prepareSaveSettings(bool *pfNeedsGlobalSaveSettings) 7685 { 7686 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 7687 7688 HRESULT rc = S_OK; 7689 7690 bool fSettingsFileIsNew = !mData->pMachineConfigFile->fileExists(); 7691 7692 /* attempt to rename the settings file if machine name is changed */ 7693 if ( mUserData->s.fNameSync 7694 && mUserData.isBackedUp() 7695 && mUserData.backedUpData()->s.strName != mUserData->s.strName 7696 ) 7697 { 7698 bool dirRenamed = false; 7699 bool fileRenamed = false; 7700 7701 Utf8Str configFile, newConfigFile; 7702 Utf8Str configDir, newConfigDir; 7703 7704 do 7705 { 7706 int vrc = VINF_SUCCESS; 7707 7708 Utf8Str name = mUserData.backedUpData()->s.strName; 7709 Utf8Str newName = mUserData->s.strName; 7710 7711 configFile = mData->m_strConfigFileFull; 7712 7713 /* first, rename the directory if it matches the machine name */ 7714 configDir = configFile; 7715 configDir.stripFilename(); 7716 newConfigDir = configDir; 7717 if (!strcmp(RTPathFilename(configDir.c_str()), name.c_str())) 7718 { 7719 newConfigDir.stripFilename(); 7720 newConfigDir.append(RTPATH_DELIMITER); 7721 newConfigDir.append(newName); 7722 /* new dir and old dir cannot be equal here because of 'if' 7723 * above and because name != newName */ 7724 Assert(configDir != newConfigDir); 7725 if (!fSettingsFileIsNew) 7726 { 7727 /* perform real rename only if the machine is not new */ 7728 vrc = RTPathRename(configDir.c_str(), newConfigDir.c_str(), 0); 7729 if (RT_FAILURE(vrc)) 7730 { 7731 rc = setError(E_FAIL, 7732 tr("Could not rename the directory '%s' to '%s' to save the settings file (%Rrc)"), 7733 configDir.c_str(), 7734 newConfigDir.c_str(), 7735 vrc); 7736 break; 7737 } 7738 dirRenamed = true; 7739 } 7740 } 7741 7742 newConfigFile = Utf8StrFmt("%s%c%s.xml", 7743 newConfigDir.c_str(), RTPATH_DELIMITER, newName.c_str()); 7744 7745 /* then try to rename the settings file itself */ 7746 if (newConfigFile != configFile) 7747 { 7748 /* get the path to old settings file in renamed directory */ 7749 configFile = Utf8StrFmt("%s%c%s", 7750 newConfigDir.c_str(), 7751 RTPATH_DELIMITER, 7752 RTPathFilename(configFile.c_str())); 7753 if (!fSettingsFileIsNew) 7754 { 7755 /* perform real rename only if the machine is not new */ 7756 vrc = RTFileRename(configFile.c_str(), newConfigFile.c_str(), 0); 7757 if (RT_FAILURE(vrc)) 7758 { 7759 rc = setError(E_FAIL, 7760 tr("Could not rename the settings file '%s' to '%s' (%Rrc)"), 7761 configFile.c_str(), 7762 newConfigFile.c_str(), 7763 vrc); 7764 break; 7765 } 7766 fileRenamed = true; 7767 } 7768 } 7769 7770 /* update m_strConfigFileFull amd mConfigFile */ 7771 mData->m_strConfigFileFull = newConfigFile; 7772 // compute the relative path too 7773 mParent->copyPathRelativeToConfig(newConfigFile, mData->m_strConfigFile); 7774 7775 // store the old and new so that VirtualBox::saveSettings() can update 7776 // the media registry 7777 if ( mData->mRegistered 7778 && configDir != newConfigDir) 7779 { 7780 mParent->rememberMachineNameChangeForMedia(configDir, newConfigDir); 7781 7782 if (pfNeedsGlobalSaveSettings) 7783 *pfNeedsGlobalSaveSettings = true; 7784 } 7785 7786 /* update the snapshot folder */ 7787 if (RTPathStartsWith(mUserData->m_strSnapshotFolderFull.c_str(), 7788 configDir.c_str())) 7789 { 7790 mUserData->m_strSnapshotFolderFull = Utf8StrFmt("%s%s", 7791 newConfigDir.c_str(), 7792 mUserData->m_strSnapshotFolderFull.c_str() + configDir.length()); 7793 copyPathRelativeToMachine(mUserData->m_strSnapshotFolderFull, 7794 mUserData->s.strSnapshotFolder); 7795 } 7796 7797 /* update the saved state file path */ 7798 Utf8Str path = mSSData->mStateFilePath; 7799 if (RTPathStartsWith(path.c_str(), configDir.c_str())) 7800 mSSData->mStateFilePath = Utf8StrFmt("%s%s", 7801 newConfigDir.c_str(), 7802 path.c_str() + configDir.length()); 7803 7804 /* Update saved state file paths of all online snapshots. 7805 * Note that saveSettings() will recognize name change 7806 * and will save all snapshots in this case. */ 7807 if (mData->mFirstSnapshot) 7808 mData->mFirstSnapshot->updateSavedStatePaths(configDir.c_str(), 7809 newConfigDir.c_str()); 7810 } 7811 while (0); 7812 7813 if (FAILED(rc)) 7814 { 7815 /* silently try to rename everything back */ 7816 if (fileRenamed) 7817 RTFileRename(newConfigFile.c_str(), configFile.c_str(), 0); 7818 if (dirRenamed) 7819 RTPathRename(newConfigDir.c_str(), configDir.c_str(), 0); 7820 } 7821 7822 if (FAILED(rc)) return rc; 7823 } 7824 7825 if (fSettingsFileIsNew) 7826 { 7827 /* create a virgin config file */ 7828 int vrc = VINF_SUCCESS; 7829 7830 /* ensure the settings directory exists */ 7831 Utf8Str path(mData->m_strConfigFileFull); 7832 path.stripFilename(); 7833 if (!RTDirExists(path.c_str())) 7834 { 7835 vrc = RTDirCreateFullPath(path.c_str(), 0777); 7836 if (RT_FAILURE(vrc)) 7837 { 7838 return setError(E_FAIL, 7839 tr("Could not create a directory '%s' to save the settings file (%Rrc)"), 7840 path.c_str(), 7841 vrc); 7842 } 7843 } 7844 7845 /* Note: open flags must correlate with RTFileOpen() in lockConfig() */ 7846 path = Utf8Str(mData->m_strConfigFileFull); 7847 RTFILE f = NIL_RTFILE; 7848 vrc = RTFileOpen(&f, path.c_str(), 7849 RTFILE_O_READWRITE | RTFILE_O_CREATE | RTFILE_O_DENY_WRITE); 7850 if (RT_FAILURE(vrc)) 7851 return setError(E_FAIL, 7852 tr("Could not create the settings file '%s' (%Rrc)"), 7853 path.c_str(), 7854 vrc); 7855 RTFileClose(f); 7856 } 7857 7858 return rc; 7859 } 7860 7861 /** 7862 * Saves and commits machine data, user data and hardware data. 7863 * 7864 * Note that on failure, the data remains uncommitted. 7865 * 7866 * @a aFlags may combine the following flags: 7867 * 7868 * - SaveS_ResetCurStateModified: Resets mData->mCurrentStateModified to FALSE. 7869 * Used when saving settings after an operation that makes them 100% 7870 * correspond to the settings from the current snapshot. 7871 * - SaveS_InformCallbacksAnyway: Callbacks will be informed even if 7872 * #isReallyModified() returns false. This is necessary for cases when we 7873 * change machine data directly, not through the backup()/commit() mechanism. 7874 * - SaveS_Force: settings will be saved without doing a deep compare of the 7875 * settings structures. This is used when this is called because snapshots 7876 * have changed to avoid the overhead of the deep compare. 7877 * 7878 * @note Must be called from under this object's write lock. Locks children for 7879 * writing. 7880 * 7881 * @param pfNeedsGlobalSaveSettings Optional pointer to a bool that must have been 7882 * initialized to false and that will be set to true by this function if 7883 * the caller must invoke VirtualBox::saveSettings() because the global 7884 * settings have changed. This will happen if a machine rename has been 7885 * saved and the global machine and media registries will therefore need 7886 * updating. 7887 */ 7888 HRESULT Machine::saveSettings(bool *pfNeedsGlobalSaveSettings, 7889 int aFlags /*= 0*/) 7890 { 7891 LogFlowThisFuncEnter(); 7892 7893 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 7894 7895 /* make sure child objects are unable to modify the settings while we are 7896 * saving them */ 7897 ensureNoStateDependencies(); 7898 7899 AssertReturn(!isSnapshotMachine(), 7900 E_FAIL); 7901 7902 HRESULT rc = S_OK; 7903 bool fNeedsWrite = false; 7904 7905 /* First, prepare to save settings. It will care about renaming the 7906 * settings directory and file if the machine name was changed and about 7907 * creating a new settings file if this is a new machine. */ 7908 rc = prepareSaveSettings(pfNeedsGlobalSaveSettings); 7909 if (FAILED(rc)) return rc; 7910 7911 // keep a pointer to the current settings structures 7912 settings::MachineConfigFile *pOldConfig = mData->pMachineConfigFile; 7913 settings::MachineConfigFile *pNewConfig = NULL; 7914 7915 try 7916 { 7917 // make a fresh one to have everyone write stuff into 7918 pNewConfig = new settings::MachineConfigFile(NULL); 7919 pNewConfig->copyBaseFrom(*mData->pMachineConfigFile); 7920 7921 // now go and copy all the settings data from COM to the settings structures 7922 // (this calles saveSettings() on all the COM objects in the machine) 7923 copyMachineDataToSettings(*pNewConfig); 7924 7925 if (aFlags & SaveS_ResetCurStateModified) 7926 { 7927 // this gets set by takeSnapshot() (if offline snapshot) and restoreSnapshot() 7928 mData->mCurrentStateModified = FALSE; 7929 fNeedsWrite = true; // always, no need to compare 7930 } 7931 else if (aFlags & SaveS_Force) 7932 { 7933 fNeedsWrite = true; // always, no need to compare 7934 } 7935 else 7936 { 7937 if (!mData->mCurrentStateModified) 7938 { 7939 // do a deep compare of the settings that we just saved with the settings 7940 // previously stored in the config file; this invokes MachineConfigFile::operator== 7941 // which does a deep compare of all the settings, which is expensive but less expensive 7942 // than writing out XML in vain 7943 bool fAnySettingsChanged = (*pNewConfig == *pOldConfig); 7944 7945 // could still be modified if any settings changed 7946 mData->mCurrentStateModified = fAnySettingsChanged; 7947 7948 fNeedsWrite = fAnySettingsChanged; 7949 } 7950 else 7951 fNeedsWrite = true; 7952 } 7953 7954 pNewConfig->fCurrentStateModified = !!mData->mCurrentStateModified; 7955 7956 if (fNeedsWrite) 7957 // now spit it all out! 7958 pNewConfig->write(mData->m_strConfigFileFull); 7959 7960 mData->pMachineConfigFile = pNewConfig; 7961 delete pOldConfig; 7962 commit(); 7963 7964 // after saving settings, we are no longer different from the XML on disk 7965 mData->flModifications = 0; 7966 } 7967 catch (HRESULT err) 7968 { 7969 // we assume that error info is set by the thrower 7970 rc = err; 7971 7972 // restore old config 7973 delete pNewConfig; 7974 mData->pMachineConfigFile = pOldConfig; 7975 } 7976 catch (...) 7977 { 7978 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 7979 } 7980 7981 if (fNeedsWrite || (aFlags & SaveS_InformCallbacksAnyway)) 7982 { 7983 /* Fire the data change event, even on failure (since we've already 7984 * committed all data). This is done only for SessionMachines because 7985 * mutable Machine instances are always not registered (i.e. private 7986 * to the client process that creates them) and thus don't need to 7987 * inform callbacks. */ 7988 if (isSessionMachine()) 7989 mParent->onMachineDataChange(mData->mUuid); 7990 } 7991 7992 LogFlowThisFunc(("rc=%08X\n", rc)); 7993 LogFlowThisFuncLeave(); 7994 return rc; 7995 } 7996 7997 /** 7998 * Implementation for saving the machine settings into the given 7999 * settings::MachineConfigFile instance. This copies machine extradata 8000 * from the previous machine config file in the instance data, if any. 8001 * 8002 * This gets called from two locations: 8003 * 8004 * -- Machine::saveSettings(), during the regular XML writing; 8005 * 8006 * -- Appliance::buildXMLForOneVirtualSystem(), when a machine gets 8007 * exported to OVF and we write the VirtualBox proprietary XML 8008 * into a <vbox:Machine> tag. 8009 * 8010 * This routine fills all the fields in there, including snapshots, *except* 8011 * for the following: 8012 * 8013 * -- fCurrentStateModified. There is some special logic associated with that. 8014 * 8015 * The caller can then call MachineConfigFile::write() or do something else 8016 * with it. 8017 * 8018 * Caller must hold the machine lock! 8019 * 8020 * This throws XML errors and HRESULT, so the caller must have a catch block! 8021 */ 8022 void Machine::copyMachineDataToSettings(settings::MachineConfigFile &config) 8023 { 8024 // deep copy extradata 8025 config.mapExtraDataItems = mData->pMachineConfigFile->mapExtraDataItems; 8026 8027 config.uuid = mData->mUuid; 8028 8029 // copy name, description, OS type, teleport, UTC etc. 8030 config.machineUserData = mUserData->s; 8031 8032 if ( mData->mMachineState == MachineState_Saved 8033 || mData->mMachineState == MachineState_Restoring 8034 // when deleting a snapshot we may or may not have a saved state in the current state, 8035 // so let's not assert here please 8036 || ( ( mData->mMachineState == MachineState_DeletingSnapshot 8037 || mData->mMachineState == MachineState_DeletingSnapshotOnline 8038 || mData->mMachineState == MachineState_DeletingSnapshotPaused) 8039 && (!mSSData->mStateFilePath.isEmpty()) 8040 ) 8041 ) 8042 { 8043 Assert(!mSSData->mStateFilePath.isEmpty()); 8044 /* try to make the file name relative to the settings file dir */ 8045 copyPathRelativeToMachine(mSSData->mStateFilePath, config.strStateFile); 8046 } 8047 else 8048 { 8049 Assert(mSSData->mStateFilePath.isEmpty()); 8050 config.strStateFile.setNull(); 8051 } 8052 8053 if (mData->mCurrentSnapshot) 8054 config.uuidCurrentSnapshot = mData->mCurrentSnapshot->getId(); 8055 else 8056 config.uuidCurrentSnapshot.clear(); 8057 8058 config.timeLastStateChange = mData->mLastStateChange; 8059 config.fAborted = (mData->mMachineState == MachineState_Aborted); 8060 /// @todo Live Migration: config.fTeleported = (mData->mMachineState == MachineState_Teleported); 8061 8062 HRESULT rc = saveHardware(config.hardwareMachine); 8063 if (FAILED(rc)) throw rc; 8064 8065 rc = saveStorageControllers(config.storageMachine); 8066 if (FAILED(rc)) throw rc; 8067 8068 // save machine's media registry if this is VirtualBox 4.0 or later 8069 if (config.canHaveOwnMediaRegistry()) 8070 mParent->saveMediaRegistry(config.mediaRegistry, 8071 getId()); // only media with registry ID == machine UUID 8072 // this throws HRESULT 8073 8074 // save snapshots 8075 rc = saveAllSnapshots(config); 8076 if (FAILED(rc)) throw rc; 8077 } 8078 8079 /** 8080 * Saves all snapshots of the machine into the given machine config file. Called 8081 * from Machine::buildMachineXML() and SessionMachine::deleteSnapshotHandler(). 8082 * @param config 8083 * @return 8084 */ 8085 HRESULT Machine::saveAllSnapshots(settings::MachineConfigFile &config) 8086 { 8087 AssertReturn(isWriteLockOnCurrentThread(), E_FAIL); 8088 8089 HRESULT rc = S_OK; 8090 8091 try 8092 { 8093 config.llFirstSnapshot.clear(); 8094 8095 if (mData->mFirstSnapshot) 8096 { 8097 settings::Snapshot snapNew; 8098 config.llFirstSnapshot.push_back(snapNew); 8099 8100 // get reference to the fresh copy of the snapshot on the list and 8101 // work on that copy directly to avoid excessive copying later 8102 settings::Snapshot &snap = config.llFirstSnapshot.front(); 8103 8104 rc = mData->mFirstSnapshot->saveSnapshot(snap, false /*aAttrsOnly*/); 8105 if (FAILED(rc)) throw rc; 8106 } 8107 8108 // if (mType == IsSessionMachine) 8109 // mParent->onMachineDataChange(mData->mUuid); @todo is this necessary? 8110 8111 } 8112 catch (HRESULT err) 8113 { 8114 /* we assume that error info is set by the thrower */ 8115 rc = err; 8116 } 8117 catch (...) 8118 { 8119 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS); 8120 } 8121 8122 return rc; 8123 } 8124 8125 /** 8126 * Saves the VM hardware configuration. It is assumed that the 8127 * given node is empty. 8128 * 8129 * @param aNode <Hardware> node to save the VM hardware confguration to. 8130 */ 8131 HRESULT Machine::saveHardware(settings::Hardware &data) 8132 { 8133 HRESULT rc = S_OK; 8134 8135 try 8136 { 8137 /* The hardware version attribute (optional). 8138 Automatically upgrade from 1 to 2 when there is no saved state. (ugly!) */ 8139 if ( mHWData->mHWVersion == "1" 8140 && mSSData->mStateFilePath.isEmpty() 8141 ) 8142 mHWData->mHWVersion = "2"; /** @todo Is this safe, to update mHWVersion here? If not some other point needs to be found where this can be done. */ 8143 8144 data.strVersion = mHWData->mHWVersion; 8145 data.uuid = mHWData->mHardwareUUID; 8146 8147 // CPU 8148 data.fHardwareVirt = !!mHWData->mHWVirtExEnabled; 8149 data.fHardwareVirtExclusive = !!mHWData->mHWVirtExExclusive; 8150 data.fNestedPaging = !!mHWData->mHWVirtExNestedPagingEnabled; 8151 data.fLargePages = !!mHWData->mHWVirtExLargePagesEnabled; 8152 data.fVPID = !!mHWData->mHWVirtExVPIDEnabled; 8153 data.fHardwareVirtForce = !!mHWData->mHWVirtExForceEnabled; 8154 data.fPAE = !!mHWData->mPAEEnabled; 8155 data.fSyntheticCpu = !!mHWData->mSyntheticCpu; 8156 8157 /* Standard and Extended CPUID leafs. */ 8158 data.llCpuIdLeafs.clear(); 8159 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdStdLeafs); idx++) 8160 { 8161 if (mHWData->mCpuIdStdLeafs[idx].ulId != UINT32_MAX) 8162 data.llCpuIdLeafs.push_back(mHWData->mCpuIdStdLeafs[idx]); 8163 } 8164 for (unsigned idx = 0; idx < RT_ELEMENTS(mHWData->mCpuIdExtLeafs); idx++) 8165 { 8166 if (mHWData->mCpuIdExtLeafs[idx].ulId != UINT32_MAX) 8167 data.llCpuIdLeafs.push_back(mHWData->mCpuIdExtLeafs[idx]); 8168 } 8169 8170 data.cCPUs = mHWData->mCPUCount; 8171 data.fCpuHotPlug = !!mHWData->mCPUHotPlugEnabled; 8172 data.ulCpuPriority = mHWData->mCpuPriority; 8173 8174 data.llCpus.clear(); 8175 if (data.fCpuHotPlug) 8176 { 8177 for (unsigned idx = 0; idx < data.cCPUs; idx++) 8178 { 8179 if (mHWData->mCPUAttached[idx]) 8180 { 8181 settings::Cpu cpu; 8182 cpu.ulId = idx; 8183 data.llCpus.push_back(cpu); 174 /* Skip all previous monitors and the first 3 entries. */ 175 SSMR3Skip(pSSM, u32ScreenId * 5 * sizeof(uint32_t) + 3 * sizeof(uint32_t)); 176 SSMR3GetU32(pSSM, &u32Width); 177 SSMR3GetU32(pSSM, &u32Height); 8184 178 } 8185 179 } 8186 180 } 8187 181 8188 // memory 8189 data.ulMemorySizeMB = mHWData->mMemorySize; 8190 data.fPageFusionEnabled = mHWData->mPageFusionEnabled; 8191 8192 // firmware 8193 data.firmwareType = mHWData->mFirmwareType; 8194 8195 // HID 8196 data.pointingHidType = mHWData->mPointingHidType; 8197 data.keyboardHidType = mHWData->mKeyboardHidType; 8198 8199 // chipset 8200 data.chipsetType = mHWData->mChipsetType; 8201 8202 // HPET 8203 data.fHpetEnabled = !!mHWData->mHpetEnabled; 8204 8205 // boot order 8206 data.mapBootOrder.clear(); 8207 for (size_t i = 0; 8208 i < RT_ELEMENTS(mHWData->mBootOrder); 8209 ++i) 8210 data.mapBootOrder[i] = mHWData->mBootOrder[i]; 8211 8212 // display 8213 data.ulVRAMSizeMB = mHWData->mVRAMSize; 8214 data.cMonitors = mHWData->mMonitorCount; 8215 data.fAccelerate3D = !!mHWData->mAccelerate3DEnabled; 8216 data.fAccelerate2DVideo = !!mHWData->mAccelerate2DVideoEnabled; 8217 8218 #ifdef VBOX_WITH_VRDP 8219 /* VRDP settings (optional) */ 8220 rc = mVRDPServer->saveSettings(data.vrdpSettings); 8221 if (FAILED(rc)) throw rc; 8222 #endif 8223 8224 /* BIOS (required) */ 8225 rc = mBIOSSettings->saveSettings(data.biosSettings); 8226 if (FAILED(rc)) throw rc; 8227 8228 /* USB Controller (required) */ 8229 rc = mUSBController->saveSettings(data.usbController); 8230 if (FAILED(rc)) throw rc; 8231 8232 /* Network adapters (required) */ 8233 data.llNetworkAdapters.clear(); 8234 for (ULONG slot = 0; 8235 slot < RT_ELEMENTS(mNetworkAdapters); 8236 ++slot) 8237 { 8238 settings::NetworkAdapter nic; 8239 nic.ulSlot = slot; 8240 rc = mNetworkAdapters[slot]->saveSettings(nic); 8241 if (FAILED(rc)) throw rc; 8242 8243 data.llNetworkAdapters.push_back(nic); 8244 } 8245 8246 /* Serial ports */ 8247 data.llSerialPorts.clear(); 8248 for (ULONG slot = 0; 8249 slot < RT_ELEMENTS(mSerialPorts); 8250 ++slot) 8251 { 8252 settings::SerialPort s; 8253 s.ulSlot = slot; 8254 rc = mSerialPorts[slot]->saveSettings(s); 8255 if (FAILED(rc)) return rc; 8256 8257 data.llSerialPorts.push_back(s); 8258 } 8259 8260 /* Parallel ports */ 8261 data.llParallelPorts.clear(); 8262 for (ULONG slot = 0; 8263 slot < RT_ELEMENTS(mParallelPorts); 8264 ++slot) 8265 { 8266 settings::ParallelPort p; 8267 p.ulSlot = slot; 8268 rc = mParallelPorts[slot]->saveSettings(p); 8269 if (FAILED(rc)) return rc; 8270 8271 data.llParallelPorts.push_back(p); 8272 } 8273 8274 /* Audio adapter */ 8275 rc = mAudioAdapter->saveSettings(data.audioAdapter); 8276 if (FAILED(rc)) return rc; 8277 8278 /* Shared folders */ 8279 data.llSharedFolders.clear(); 8280 for (HWData::SharedFolderList::const_iterator it = mHWData->mSharedFolders.begin(); 8281 it != mHWData->mSharedFolders.end(); 8282 ++it) 8283 { 8284 ComObjPtr<SharedFolder> pFolder = *it; 8285 settings::SharedFolder sf; 8286 sf.strName = pFolder->getName(); 8287 sf.strHostPath = pFolder->getHostPath(); 8288 sf.fWritable = !!pFolder->isWritable(); 8289 sf.fAutoMount = !!pFolder->isAutoMounted(); 8290 8291 data.llSharedFolders.push_back(sf); 8292 } 8293 8294 // clipboard 8295 data.clipboardMode = mHWData->mClipboardMode; 8296 8297 /* Guest */ 8298 data.ulMemoryBalloonSize = mHWData->mMemoryBalloonSize; 8299 8300 // IO settings 8301 data.ioSettings.fIoCacheEnabled = !!mHWData->mIoCacheEnabled; 8302 data.ioSettings.ulIoCacheSize = mHWData->mIoCacheSize; 8303 8304 // guest properties 8305 data.llGuestProperties.clear(); 8306 #ifdef VBOX_WITH_GUEST_PROPS 8307 for (HWData::GuestPropertyList::const_iterator it = mHWData->mGuestProperties.begin(); 8308 it != mHWData->mGuestProperties.end(); 8309 ++it) 8310 { 8311 HWData::GuestProperty property = *it; 8312 8313 /* Remove transient guest properties at shutdown unless we 8314 * are saving state */ 8315 if ( ( mData->mMachineState == MachineState_PoweredOff 8316 || mData->mMachineState == MachineState_Aborted 8317 || mData->mMachineState == MachineState_Teleported) 8318 && property.mFlags & guestProp::TRANSIENT) 8319 continue; 8320 settings::GuestProperty prop; 8321 prop.strName = property.strName; 8322 prop.strValue = property.strValue; 8323 prop.timestamp = property.mTimestamp; 8324 char szFlags[guestProp::MAX_FLAGS_LEN + 1]; 8325 guestProp::writeFlags(property.mFlags, szFlags); 8326 prop.strFlags = szFlags; 8327 8328 data.llGuestProperties.push_back(prop); 8329 } 8330 8331 data.strNotificationPatterns = mHWData->mGuestPropertyNotificationPatterns; 8332 /* I presume this doesn't require a backup(). */ 8333 mData->mGuestPropertiesModified = FALSE; 8334 #endif /* VBOX_WITH_GUEST_PROPS defined */ 8335 } 8336 catch(std::bad_alloc &) 8337 { 8338 return E_OUTOFMEMORY; 182 SSMR3Close(pSSM); 8339 183 } 8340 184 8341 AssertComRC(rc); 8342 return rc; 185 if (RT_SUCCESS(vrc)) 186 { 187 *pu32Width = u32Width; 188 *pu32Height = u32Height; 189 } 190 191 LogFlowFunc(("vrc %Rrc\n", vrc)); 192 return vrc; 8343 193 } 8344 194 8345 /**8346 * Saves the storage controller configuration.8347 *8348 * @param aNode <StorageControllers> node to save the VM hardware confguration to.8349 */8350 HRESULT Machine::saveStorageControllers(settings::Storage &data)8351 {8352 data.llStorageControllers.clear();8353 8354 for (StorageControllerList::const_iterator it = mStorageControllers->begin();8355 it != mStorageControllers->end();8356 ++it)8357 {8358 HRESULT rc;8359 ComObjPtr<StorageController> pCtl = *it;8360 8361 settings::StorageController ctl;8362 ctl.strName = pCtl->getName();8363 ctl.controllerType = pCtl->getControllerType();8364 ctl.storageBus = pCtl->getStorageBus();8365 ctl.ulInstance = pCtl->getInstance();8366 8367 /* Save the port count. */8368 ULONG portCount;8369 rc = pCtl->COMGETTER(PortCount)(&portCount);8370 ComAssertComRCRet(rc, rc);8371 ctl.ulPortCount = portCount;8372 8373 /* Save fUseHostIOCache */8374 BOOL fUseHostIOCache;8375 rc = pCtl->COMGETTER(UseHostIOCache)(&fUseHostIOCache);8376 ComAssertComRCRet(rc, rc);8377 ctl.fUseHostIOCache = !!fUseHostIOCache;8378 8379 /* Save IDE emulation settings. */8380 if (ctl.controllerType == StorageControllerType_IntelAhci)8381 {8382 if ( (FAILED(rc = pCtl->GetIDEEmulationPort(0, (LONG*)&ctl.lIDE0MasterEmulationPort)))8383 || (FAILED(rc = pCtl->GetIDEEmulationPort(1, (LONG*)&ctl.lIDE0SlaveEmulationPort)))8384 || (FAILED(rc = pCtl->GetIDEEmulationPort(2, (LONG*)&ctl.lIDE1MasterEmulationPort)))8385 || (FAILED(rc = pCtl->GetIDEEmulationPort(3, (LONG*)&ctl.lIDE1SlaveEmulationPort)))8386 )8387 ComAssertComRCRet(rc, rc);8388 }8389 8390 /* save the devices now. */8391 rc = saveStorageDevices(pCtl, ctl);8392 ComAssertComRCRet(rc, rc);8393 8394 data.llStorageControllers.push_back(ctl);8395 }8396 8397 return S_OK;8398 }8399 8400 /**8401 * Saves the hard disk confguration.8402 */8403 HRESULT Machine::saveStorageDevices(ComObjPtr<StorageController> aStorageController,8404 settings::StorageController &data)8405 {8406 MediaData::AttachmentList atts;8407 8408 HRESULT rc = getMediumAttachmentsOfController(Bstr(aStorageController->getName()), atts);8409 if (FAILED(rc)) return rc;8410 8411 data.llAttachedDevices.clear();8412 for (MediaData::AttachmentList::const_iterator it = atts.begin();8413 it != atts.end();8414 ++it)8415 {8416 settings::AttachedDevice dev;8417 8418 MediumAttachment *pAttach = *it;8419 Medium *pMedium = pAttach->getMedium();8420 8421 dev.deviceType = pAttach->getType();8422 dev.lPort = pAttach->getPort();8423 dev.lDevice = pAttach->getDevice();8424 if (pMedium)8425 {8426 if (pMedium->isHostDrive())8427 dev.strHostDriveSrc = pMedium->getLocation();8428 else8429 dev.uuid = pMedium->getId();8430 dev.fPassThrough = pAttach->getPassthrough();8431 }8432 8433 data.llAttachedDevices.push_back(dev);8434 }8435 8436 return S_OK;8437 }8438 8439 /**8440 * Saves machine state settings as defined by aFlags8441 * (SaveSTS_* values).8442 *8443 * @param aFlags Combination of SaveSTS_* flags.8444 *8445 * @note Locks objects for writing.8446 */8447 HRESULT Machine::saveStateSettings(int aFlags)8448 {8449 if (aFlags == 0)8450 return S_OK;8451 8452 AutoCaller autoCaller(this);8453 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());8454 8455 /* This object's write lock is also necessary to serialize file access8456 * (prevent concurrent reads and writes) */8457 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);8458 8459 HRESULT rc = S_OK;8460 8461 Assert(mData->pMachineConfigFile);8462 8463 try8464 {8465 if (aFlags & SaveSTS_CurStateModified)8466 mData->pMachineConfigFile->fCurrentStateModified = true;8467 8468 if (aFlags & SaveSTS_StateFilePath)8469 {8470 if (!mSSData->mStateFilePath.isEmpty())8471 /* try to make the file name relative to the settings file dir */8472 copyPathRelativeToMachine(mSSData->mStateFilePath, mData->pMachineConfigFile->strStateFile);8473 else8474 mData->pMachineConfigFile->strStateFile.setNull();8475 }8476 8477 if (aFlags & SaveSTS_StateTimeStamp)8478 {8479 Assert( mData->mMachineState != MachineState_Aborted8480 || mSSData->mStateFilePath.isEmpty());8481 8482 mData->pMachineConfigFile->timeLastStateChange = mData->mLastStateChange;8483 8484 mData->pMachineConfigFile->fAborted = (mData->mMachineState == MachineState_Aborted);8485 //@todo live migration mData->pMachineConfigFile->fTeleported = (mData->mMachineState == MachineState_Teleported);8486 }8487 8488 mData->pMachineConfigFile->write(mData->m_strConfigFileFull);8489 }8490 catch (...)8491 {8492 rc = VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);8493 }8494 8495 return rc;8496 }8497 8498 /**8499 * Creates differencing hard disks for all normal hard disks attached to this8500 * machine and a new set of attachments to refer to created disks.8501 *8502 * Used when taking a snapshot or when deleting the current state. Gets called8503 * from SessionMachine::BeginTakingSnapshot() and SessionMachine::restoreSnapshotHandler().8504 *8505 * This method assumes that mMediaData contains the original hard disk attachments8506 * it needs to create diffs for. On success, these attachments will be replaced8507 * with the created diffs. On failure, #deleteImplicitDiffs() is implicitly8508 * called to delete created diffs which will also rollback mMediaData and restore8509 * whatever was backed up before calling this method.8510 *8511 * Attachments with non-normal hard disks are left as is.8512 *8513 * If @a aOnline is @c false then the original hard disks that require implicit8514 * diffs will be locked for reading. Otherwise it is assumed that they are8515 * already locked for writing (when the VM was started). Note that in the latter8516 * case it is responsibility of the caller to lock the newly created diffs for8517 * writing if this method succeeds.8518 *8519 * @param aProgress Progress object to run (must contain at least as8520 * many operations left as the number of hard disks8521 * attached).8522 * @param aOnline Whether the VM was online prior to this operation.8523 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true8524 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.8525 *8526 * @note The progress object is not marked as completed, neither on success nor8527 * on failure. This is a responsibility of the caller.8528 *8529 * @note Locks this object for writing.8530 */8531 HRESULT Machine::createImplicitDiffs(IProgress *aProgress,8532 ULONG aWeight,8533 bool aOnline,8534 bool *pfNeedsSaveSettings)8535 {8536 LogFlowThisFunc(("aOnline=%d\n", aOnline));8537 8538 AutoCaller autoCaller(this);8539 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());8540 8541 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);8542 8543 /* must be in a protective state because we leave the lock below */8544 AssertReturn( mData->mMachineState == MachineState_Saving8545 || mData->mMachineState == MachineState_LiveSnapshotting8546 || mData->mMachineState == MachineState_RestoringSnapshot8547 || mData->mMachineState == MachineState_DeletingSnapshot8548 , E_FAIL);8549 8550 HRESULT rc = S_OK;8551 8552 MediumLockListMap lockedMediaOffline;8553 MediumLockListMap *lockedMediaMap;8554 if (aOnline)8555 lockedMediaMap = &mData->mSession.mLockedMedia;8556 else8557 lockedMediaMap = &lockedMediaOffline;8558 8559 try8560 {8561 if (!aOnline)8562 {8563 /* lock all attached hard disks early to detect "in use"8564 * situations before creating actual diffs */8565 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();8566 it != mMediaData->mAttachments.end();8567 ++it)8568 {8569 MediumAttachment* pAtt = *it;8570 if (pAtt->getType() == DeviceType_HardDisk)8571 {8572 Medium* pMedium = pAtt->getMedium();8573 Assert(pMedium);8574 8575 MediumLockList *pMediumLockList(new MediumLockList());8576 rc = pMedium->createMediumLockList(true /* fFailIfInaccessible */,8577 false /* fMediumLockWrite */,8578 NULL,8579 *pMediumLockList);8580 if (FAILED(rc))8581 {8582 delete pMediumLockList;8583 throw rc;8584 }8585 rc = lockedMediaMap->Insert(pAtt, pMediumLockList);8586 if (FAILED(rc))8587 {8588 throw setError(rc,8589 tr("Collecting locking information for all attached media failed"));8590 }8591 }8592 }8593 8594 /* Now lock all media. If this fails, nothing is locked. */8595 rc = lockedMediaMap->Lock();8596 if (FAILED(rc))8597 {8598 throw setError(rc,8599 tr("Locking of attached media failed"));8600 }8601 }8602 8603 /* remember the current list (note that we don't use backup() since8604 * mMediaData may be already backed up) */8605 MediaData::AttachmentList atts = mMediaData->mAttachments;8606 8607 /* start from scratch */8608 mMediaData->mAttachments.clear();8609 8610 /* go through remembered attachments and create diffs for normal hard8611 * disks and attach them */8612 for (MediaData::AttachmentList::const_iterator it = atts.begin();8613 it != atts.end();8614 ++it)8615 {8616 MediumAttachment* pAtt = *it;8617 8618 DeviceType_T devType = pAtt->getType();8619 Medium* pMedium = pAtt->getMedium();8620 8621 if ( devType != DeviceType_HardDisk8622 || pMedium == NULL8623 || pMedium->getType() != MediumType_Normal)8624 {8625 /* copy the attachment as is */8626 8627 /** @todo the progress object created in Console::TakeSnaphot8628 * only expects operations for hard disks. Later other8629 * device types need to show up in the progress as well. */8630 if (devType == DeviceType_HardDisk)8631 {8632 if (pMedium == NULL)8633 aProgress->SetNextOperation(Bstr(tr("Skipping attachment without medium")),8634 aWeight); // weight8635 else8636 aProgress->SetNextOperation(BstrFmt(tr("Skipping medium '%s'"),8637 pMedium->getBase()->getName().c_str()),8638 aWeight); // weight8639 }8640 8641 mMediaData->mAttachments.push_back(pAtt);8642 continue;8643 }8644 8645 /* need a diff */8646 aProgress->SetNextOperation(BstrFmt(tr("Creating differencing hard disk for '%s'"),8647 pMedium->getBase()->getName().c_str()),8648 aWeight); // weight8649 8650 ComObjPtr<Medium> diff;8651 diff.createObject();8652 rc = diff->init(mParent,8653 pMedium->getPreferredDiffFormat(),8654 Utf8Str(mUserData->m_strSnapshotFolderFull).append(RTPATH_SLASH_STR),8655 pMedium->getRegistryMachineId(), // store the diff in the same registry as the parent8656 pfNeedsSaveSettings);8657 if (FAILED(rc)) throw rc;8658 8659 /** @todo r=bird: How is the locking and diff image cleaned up if we fail before8660 * the push_back? Looks like we're going to leave medium with the8661 * wrong kind of lock (general issue with if we fail anywhere at all)8662 * and an orphaned VDI in the snapshots folder. */8663 8664 /* update the appropriate lock list */8665 MediumLockList *pMediumLockList;8666 rc = lockedMediaMap->Get(pAtt, pMediumLockList);8667 AssertComRCThrowRC(rc);8668 if (aOnline)8669 {8670 rc = pMediumLockList->Update(pMedium, false);8671 AssertComRCThrowRC(rc);8672 }8673 8674 /* leave the lock before the potentially lengthy operation */8675 alock.leave();8676 rc = pMedium->createDiffStorage(diff, MediumVariant_Standard,8677 pMediumLockList,8678 NULL /* aProgress */,8679 true /* aWait */,8680 pfNeedsSaveSettings);8681 alock.enter();8682 if (FAILED(rc)) throw rc;8683 8684 rc = lockedMediaMap->Unlock();8685 AssertComRCThrowRC(rc);8686 rc = pMediumLockList->Append(diff, true);8687 AssertComRCThrowRC(rc);8688 rc = lockedMediaMap->Lock();8689 AssertComRCThrowRC(rc);8690 8691 rc = diff->addBackReference(mData->mUuid);8692 AssertComRCThrowRC(rc);8693 8694 /* add a new attachment */8695 ComObjPtr<MediumAttachment> attachment;8696 attachment.createObject();8697 rc = attachment->init(this,8698 diff,8699 pAtt->getControllerName(),8700 pAtt->getPort(),8701 pAtt->getDevice(),8702 DeviceType_HardDisk,8703 true /* aImplicit */,8704 0 /* No bandwidth limit */);8705 if (FAILED(rc)) throw rc;8706 8707 rc = lockedMediaMap->ReplaceKey(pAtt, attachment);8708 AssertComRCThrowRC(rc);8709 mMediaData->mAttachments.push_back(attachment);8710 }8711 }8712 catch (HRESULT aRC) { rc = aRC; }8713 8714 /* unlock all hard disks we locked */8715 if (!aOnline)8716 {8717 ErrorInfoKeeper eik;8718 8719 rc = lockedMediaMap->Clear();8720 AssertComRC(rc);8721 }8722 8723 if (FAILED(rc))8724 {8725 MultiResult mrc = rc;8726 8727 mrc = deleteImplicitDiffs(pfNeedsSaveSettings);8728 }8729 8730 return rc;8731 }8732 8733 /**8734 * Deletes implicit differencing hard disks created either by8735 * #createImplicitDiffs() or by #AttachMedium() and rolls back mMediaData.8736 *8737 * Note that to delete hard disks created by #AttachMedium() this method is8738 * called from #fixupMedia() when the changes are rolled back.8739 *8740 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true8741 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.8742 *8743 * @note Locks this object for writing.8744 */8745 HRESULT Machine::deleteImplicitDiffs(bool *pfNeedsSaveSettings)8746 {8747 AutoCaller autoCaller(this);8748 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());8749 8750 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);8751 LogFlowThisFuncEnter();8752 8753 AssertReturn(mMediaData.isBackedUp(), E_FAIL);8754 8755 HRESULT rc = S_OK;8756 8757 MediaData::AttachmentList implicitAtts;8758 8759 const MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;8760 8761 /* enumerate new attachments */8762 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();8763 it != mMediaData->mAttachments.end();8764 ++it)8765 {8766 ComObjPtr<Medium> hd = (*it)->getMedium();8767 if (hd.isNull())8768 continue;8769 8770 if ((*it)->isImplicit())8771 {8772 /* deassociate and mark for deletion */8773 LogFlowThisFunc(("Detaching '%s', pending deletion\n", (*it)->getLogName()));8774 rc = hd->removeBackReference(mData->mUuid);8775 AssertComRC(rc);8776 implicitAtts.push_back(*it);8777 continue;8778 }8779 8780 /* was this hard disk attached before? */8781 if (!findAttachment(oldAtts, hd))8782 {8783 /* no: de-associate */8784 LogFlowThisFunc(("Detaching '%s', no deletion\n", (*it)->getLogName()));8785 rc = hd->removeBackReference(mData->mUuid);8786 AssertComRC(rc);8787 continue;8788 }8789 LogFlowThisFunc(("Not detaching '%s'\n", (*it)->getLogName()));8790 }8791 8792 /* rollback hard disk changes */8793 mMediaData.rollback();8794 8795 MultiResult mrc(S_OK);8796 8797 /* delete unused implicit diffs */8798 if (implicitAtts.size() != 0)8799 {8800 /* will leave the lock before the potentially lengthy8801 * operation, so protect with the special state (unless already8802 * protected) */8803 MachineState_T oldState = mData->mMachineState;8804 if ( oldState != MachineState_Saving8805 && oldState != MachineState_LiveSnapshotting8806 && oldState != MachineState_RestoringSnapshot8807 && oldState != MachineState_DeletingSnapshot8808 && oldState != MachineState_DeletingSnapshotOnline8809 && oldState != MachineState_DeletingSnapshotPaused8810 )8811 setMachineState(MachineState_SettingUp);8812 8813 alock.leave();8814 8815 for (MediaData::AttachmentList::const_iterator it = implicitAtts.begin();8816 it != implicitAtts.end();8817 ++it)8818 {8819 LogFlowThisFunc(("Deleting '%s'\n", (*it)->getLogName()));8820 ComObjPtr<Medium> hd = (*it)->getMedium();8821 8822 rc = hd->deleteStorage(NULL /*aProgress*/, true /*aWait*/,8823 pfNeedsSaveSettings);8824 AssertMsg(SUCCEEDED(rc), ("rc=%Rhrc it=%s hd=%s\n", rc, (*it)->getLogName(), hd->getLocationFull().c_str() ));8825 mrc = rc;8826 }8827 8828 alock.enter();8829 8830 if (mData->mMachineState == MachineState_SettingUp)8831 {8832 setMachineState(oldState);8833 }8834 }8835 8836 return mrc;8837 }8838 8839 /**8840 * Looks through the given list of media attachments for one with the given parameters8841 * and returns it, or NULL if not found. The list is a parameter so that backup lists8842 * can be searched as well if needed.8843 *8844 * @param list8845 * @param aControllerName8846 * @param aControllerPort8847 * @param aDevice8848 * @return8849 */8850 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,8851 IN_BSTR aControllerName,8852 LONG aControllerPort,8853 LONG aDevice)8854 {8855 for (MediaData::AttachmentList::const_iterator it = ll.begin();8856 it != ll.end();8857 ++it)8858 {8859 MediumAttachment *pAttach = *it;8860 if (pAttach->matches(aControllerName, aControllerPort, aDevice))8861 return pAttach;8862 }8863 8864 return NULL;8865 }8866 8867 /**8868 * Looks through the given list of media attachments for one with the given parameters8869 * and returns it, or NULL if not found. The list is a parameter so that backup lists8870 * can be searched as well if needed.8871 *8872 * @param list8873 * @param aControllerName8874 * @param aControllerPort8875 * @param aDevice8876 * @return8877 */8878 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,8879 ComObjPtr<Medium> pMedium)8880 {8881 for (MediaData::AttachmentList::const_iterator it = ll.begin();8882 it != ll.end();8883 ++it)8884 {8885 MediumAttachment *pAttach = *it;8886 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();8887 if (pMediumThis == pMedium)8888 return pAttach;8889 }8890 8891 return NULL;8892 }8893 8894 /**8895 * Looks through the given list of media attachments for one with the given parameters8896 * and returns it, or NULL if not found. The list is a parameter so that backup lists8897 * can be searched as well if needed.8898 *8899 * @param list8900 * @param aControllerName8901 * @param aControllerPort8902 * @param aDevice8903 * @return8904 */8905 MediumAttachment* Machine::findAttachment(const MediaData::AttachmentList &ll,8906 Guid &id)8907 {8908 for (MediaData::AttachmentList::const_iterator it = ll.begin();8909 it != ll.end();8910 ++it)8911 {8912 MediumAttachment *pAttach = *it;8913 ComObjPtr<Medium> pMediumThis = pAttach->getMedium();8914 if (pMediumThis->getId() == id)8915 return pAttach;8916 }8917 8918 return NULL;8919 }8920 8921 /**8922 * Main implementation for Machine::DetachDevice. This also gets called8923 * from Machine::prepareUnregister() so it has been taken out for simplicity.8924 *8925 * @param pAttach Medium attachment to detach.8926 * @param writeLock Machine write lock which the caller must have locked once. This may be released temporarily in here.8927 * @param pSnapshot If NULL, then the detachment is for the current machine. Otherwise this is for a SnapshotMachine, and this must be its snapshot.8928 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true8929 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.8930 * @return8931 */8932 HRESULT Machine::detachDevice(MediumAttachment *pAttach,8933 AutoWriteLock &writeLock,8934 Snapshot *pSnapshot,8935 bool *pfNeedsSaveSettings)8936 {8937 ComObjPtr<Medium> oldmedium = pAttach->getMedium();8938 DeviceType_T mediumType = pAttach->getType();8939 8940 LogFlowThisFunc(("Entering, medium of attachment is %s\n", oldmedium ? oldmedium->getLocationFull().c_str() : "NULL"));8941 8942 if (pAttach->isImplicit())8943 {8944 /* attempt to implicitly delete the implicitly created diff */8945 8946 /// @todo move the implicit flag from MediumAttachment to Medium8947 /// and forbid any hard disk operation when it is implicit. Or maybe8948 /// a special media state for it to make it even more simple.8949 8950 Assert(mMediaData.isBackedUp());8951 8952 /* will leave the lock before the potentially lengthy operation, so8953 * protect with the special state */8954 MachineState_T oldState = mData->mMachineState;8955 setMachineState(MachineState_SettingUp);8956 8957 writeLock.release();8958 8959 HRESULT rc = oldmedium->deleteStorage(NULL /*aProgress*/, true /*aWait*/,8960 pfNeedsSaveSettings);8961 8962 writeLock.acquire();8963 8964 setMachineState(oldState);8965 8966 if (FAILED(rc)) return rc;8967 }8968 8969 setModified(IsModified_Storage);8970 mMediaData.backup();8971 8972 // we cannot use erase (it) below because backup() above will create8973 // a copy of the list and make this copy active, but the iterator8974 // still refers to the original and is not valid for the copy8975 mMediaData->mAttachments.remove(pAttach);8976 8977 if (!oldmedium.isNull())8978 {8979 // if this is from a snapshot, do not defer detachment to commitMedia()8980 if (pSnapshot)8981 oldmedium->removeBackReference(mData->mUuid, pSnapshot->getId());8982 // else if non-hard disk media, do not defer detachment to commitMedia() either8983 else if (mediumType != DeviceType_HardDisk)8984 oldmedium->removeBackReference(mData->mUuid);8985 }8986 8987 return S_OK;8988 }8989 8990 /**8991 * Goes thru all medium attachments of the list and calls detachDevice() on each8992 * of them and attaches all Medium objects found in the process to the given list,8993 * depending on cleanupMode.8994 *8995 * This gets called from Machine::Unregister, both for the actual Machine and8996 * the SnapshotMachine objects that might be found in the snapshots.8997 *8998 * Requires caller and locking.8999 *9000 * @param writeLock Machine lock from top-level caller; this gets passed to detachDevice.9001 * @param pSnapshot Must be NULL when called for a "real" Machine or a snapshot object if called for a SnapshotMachine.9002 * @param cleanupMode If DetachAllReturnHardDisksOnly, only hard disk media get added to llMedia; if Full, then all media get added;9003 * otherwise no media get added.9004 * @param llMedia Caller's list to receive Medium objects which got detached so caller can close() them, depending on cleanupMode.9005 * @return9006 */9007 HRESULT Machine::detachAllMedia(AutoWriteLock &writeLock,9008 Snapshot *pSnapshot,9009 CleanupMode_T cleanupMode,9010 MediaList &llMedia)9011 {9012 Assert(isWriteLockOnCurrentThread());9013 9014 HRESULT rc;9015 9016 // make a temporary list because detachDevice invalidates iterators into9017 // mMediaData->mAttachments9018 MediaData::AttachmentList llAttachments2 = mMediaData->mAttachments;9019 9020 for (MediaData::AttachmentList::iterator it = llAttachments2.begin();9021 it != llAttachments2.end();9022 ++it)9023 {9024 ComObjPtr<MediumAttachment> pAttach = *it;9025 ComObjPtr<Medium> pMedium = pAttach->getMedium();9026 9027 if (!pMedium.isNull())9028 {9029 DeviceType_T devType = pMedium->getDeviceType();9030 if ( ( cleanupMode == CleanupMode_DetachAllReturnHardDisksOnly9031 && devType == DeviceType_HardDisk)9032 || (cleanupMode == CleanupMode_Full)9033 )9034 llMedia.push_back(pMedium);9035 }9036 9037 // real machine: then we need to use the proper method9038 rc = detachDevice(pAttach,9039 writeLock,9040 pSnapshot,9041 NULL /* pfNeedsSaveSettings */);9042 9043 if (FAILED(rc))9044 return rc;9045 }9046 9047 return S_OK;9048 }9049 9050 /**9051 * Perform deferred hard disk detachments.9052 *9053 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not9054 * backed up).9055 *9056 * If @a aOnline is @c true then this method will also unlock the old hard disks9057 * for which the new implicit diffs were created and will lock these new diffs for9058 * writing.9059 *9060 * @param aOnline Whether the VM was online prior to this operation.9061 *9062 * @note Locks this object for writing!9063 */9064 void Machine::commitMedia(bool aOnline /*= false*/)9065 {9066 AutoCaller autoCaller(this);9067 AssertComRCReturnVoid(autoCaller.rc());9068 9069 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);9070 9071 LogFlowThisFunc(("Entering, aOnline=%d\n", aOnline));9072 9073 HRESULT rc = S_OK;9074 9075 /* no attach/detach operations -- nothing to do */9076 if (!mMediaData.isBackedUp())9077 return;9078 9079 MediaData::AttachmentList &oldAtts = mMediaData.backedUpData()->mAttachments;9080 bool fMediaNeedsLocking = false;9081 9082 /* enumerate new attachments */9083 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();9084 it != mMediaData->mAttachments.end();9085 ++it)9086 {9087 MediumAttachment *pAttach = *it;9088 9089 pAttach->commit();9090 9091 Medium* pMedium = pAttach->getMedium();9092 bool fImplicit = pAttach->isImplicit();9093 9094 LogFlowThisFunc(("Examining current medium '%s' (implicit: %d)\n",9095 (pMedium) ? pMedium->getName().c_str() : "NULL",9096 fImplicit));9097 9098 /** @todo convert all this Machine-based voodoo to MediumAttachment9099 * based commit logic. */9100 if (fImplicit)9101 {9102 /* convert implicit attachment to normal */9103 pAttach->setImplicit(false);9104 9105 if ( aOnline9106 && pMedium9107 && pAttach->getType() == DeviceType_HardDisk9108 )9109 {9110 ComObjPtr<Medium> parent = pMedium->getParent();9111 AutoWriteLock parentLock(parent COMMA_LOCKVAL_SRC_POS);9112 9113 /* update the appropriate lock list */9114 MediumLockList *pMediumLockList;9115 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);9116 AssertComRC(rc);9117 if (pMediumLockList)9118 {9119 /* unlock if there's a need to change the locking */9120 if (!fMediaNeedsLocking)9121 {9122 rc = mData->mSession.mLockedMedia.Unlock();9123 AssertComRC(rc);9124 fMediaNeedsLocking = true;9125 }9126 rc = pMediumLockList->Update(parent, false);9127 AssertComRC(rc);9128 rc = pMediumLockList->Append(pMedium, true);9129 AssertComRC(rc);9130 }9131 }9132 9133 continue;9134 }9135 9136 if (pMedium)9137 {9138 /* was this medium attached before? */9139 for (MediaData::AttachmentList::iterator oldIt = oldAtts.begin();9140 oldIt != oldAtts.end();9141 ++oldIt)9142 {9143 MediumAttachment *pOldAttach = *oldIt;9144 if (pOldAttach->getMedium() == pMedium)9145 {9146 LogFlowThisFunc(("--> medium '%s' was attached before, will not remove\n", pMedium->getName().c_str()));9147 9148 /* yes: remove from old to avoid de-association */9149 oldAtts.erase(oldIt);9150 break;9151 }9152 }9153 }9154 }9155 9156 /* enumerate remaining old attachments and de-associate from the9157 * current machine state */9158 for (MediaData::AttachmentList::const_iterator it = oldAtts.begin();9159 it != oldAtts.end();9160 ++it)9161 {9162 MediumAttachment *pAttach = *it;9163 Medium* pMedium = pAttach->getMedium();9164 9165 /* Detach only hard disks, since DVD/floppy media is detached9166 * instantly in MountMedium. */9167 if (pAttach->getType() == DeviceType_HardDisk && pMedium)9168 {9169 LogFlowThisFunc(("detaching medium '%s' from machine\n", pMedium->getName().c_str()));9170 9171 /* now de-associate from the current machine state */9172 rc = pMedium->removeBackReference(mData->mUuid);9173 AssertComRC(rc);9174 9175 if (aOnline)9176 {9177 /* unlock since medium is not used anymore */9178 MediumLockList *pMediumLockList;9179 rc = mData->mSession.mLockedMedia.Get(pAttach, pMediumLockList);9180 AssertComRC(rc);9181 if (pMediumLockList)9182 {9183 rc = mData->mSession.mLockedMedia.Remove(pAttach);9184 AssertComRC(rc);9185 }9186 }9187 }9188 }9189 9190 /* take media locks again so that the locking state is consistent */9191 if (fMediaNeedsLocking)9192 {9193 Assert(aOnline);9194 rc = mData->mSession.mLockedMedia.Lock();9195 AssertComRC(rc);9196 }9197 9198 /* commit the hard disk changes */9199 mMediaData.commit();9200 9201 if (isSessionMachine())9202 {9203 /* attach new data to the primary machine and reshare it */9204 mPeer->mMediaData.attach(mMediaData);9205 }9206 9207 return;9208 }9209 9210 /**9211 * Perform deferred deletion of implicitly created diffs.9212 *9213 * Does nothing if the hard disk attachment data (mMediaData) is not changed (not9214 * backed up).9215 *9216 * @param pfNeedsSaveSettings Optional pointer to a bool that must have been initialized to false and that will be set to true9217 * by this function if the caller should invoke VirtualBox::saveSettings() because the global settings have changed.9218 *9219 * @note Locks this object for writing!9220 */9221 void Machine::rollbackMedia()9222 {9223 AutoCaller autoCaller(this);9224 AssertComRCReturnVoid (autoCaller.rc());9225 9226 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);9227 9228 LogFlowThisFunc(("Entering\n"));9229 9230 HRESULT rc = S_OK;9231 9232 /* no attach/detach operations -- nothing to do */9233 if (!mMediaData.isBackedUp())9234 return;9235 9236 /* enumerate new attachments */9237 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();9238 it != mMediaData->mAttachments.end();9239 ++it)9240 {9241 MediumAttachment *pAttach = *it;9242 /* Fix up the backrefs for DVD/floppy media. */9243 if (pAttach->getType() != DeviceType_HardDisk)9244 {9245 Medium* pMedium = pAttach->getMedium();9246 if (pMedium)9247 {9248 rc = pMedium->removeBackReference(mData->mUuid);9249 AssertComRC(rc);9250 }9251 }9252 9253 (*it)->rollback();9254 9255 pAttach = *it;9256 /* Fix up the backrefs for DVD/floppy media. */9257 if (pAttach->getType() != DeviceType_HardDisk)9258 {9259 Medium* pMedium = pAttach->getMedium();9260 if (pMedium)9261 {9262 rc = pMedium->addBackReference(mData->mUuid);9263 AssertComRC(rc);9264 }9265 }9266 }9267 9268 /** @todo convert all this Machine-based voodoo to MediumAttachment9269 * based rollback logic. */9270 // @todo r=dj the below totally fails if this gets called from Machine::rollback(),9271 // which gets called if Machine::registeredInit() fails...9272 deleteImplicitDiffs(NULL /*pfNeedsSaveSettings*/);9273 9274 return;9275 }9276 9277 /**9278 * Returns true if the settings file is located in the directory named exactly9279 * as the machine. This will be true if the machine settings structure was9280 * created by default in #openConfigLoader().9281 *9282 * @param aSettingsDir if not NULL, the full machine settings file directory9283 * name will be assigned there.9284 *9285 * @note Doesn't lock anything.9286 * @note Not thread safe (must be called from this object's lock).9287 */9288 bool Machine::isInOwnDir(Utf8Str *aSettingsDir /* = NULL */) const9289 {9290 Utf8Str settingsDir = mData->m_strConfigFileFull;9291 settingsDir.stripFilename();9292 Utf8Str strDirName = RTPathFilename(settingsDir.c_str());9293 9294 AssertReturn(!strDirName.isEmpty(), false);9295 9296 /* if we don't rename anything on name change, return false shorlty */9297 if (!mUserData->s.fNameSync)9298 return false;9299 9300 if (aSettingsDir)9301 *aSettingsDir = settingsDir;9302 9303 return strDirName == mUserData->s.strName;9304 }9305 9306 /**9307 * Discards all changes to machine settings.9308 *9309 * @param aNotify Whether to notify the direct session about changes or not.9310 *9311 * @note Locks objects for writing!9312 */9313 void Machine::rollback(bool aNotify)9314 {9315 AutoCaller autoCaller(this);9316 AssertComRCReturn(autoCaller.rc(), (void)0);9317 9318 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);9319 9320 if (!mStorageControllers.isNull())9321 {9322 if (mStorageControllers.isBackedUp())9323 {9324 /* unitialize all new devices (absent in the backed up list). */9325 StorageControllerList::const_iterator it = mStorageControllers->begin();9326 StorageControllerList *backedList = mStorageControllers.backedUpData();9327 while (it != mStorageControllers->end())9328 {9329 if ( std::find(backedList->begin(), backedList->end(), *it)9330 == backedList->end()9331 )9332 {9333 (*it)->uninit();9334 }9335 ++it;9336 }9337 9338 /* restore the list */9339 mStorageControllers.rollback();9340 }9341 9342 /* rollback any changes to devices after restoring the list */9343 if (mData->flModifications & IsModified_Storage)9344 {9345 StorageControllerList::const_iterator it = mStorageControllers->begin();9346 while (it != mStorageControllers->end())9347 {9348 (*it)->rollback();9349 ++it;9350 }9351 }9352 }9353 9354 mUserData.rollback();9355 9356 mHWData.rollback();9357 9358 if (mData->flModifications & IsModified_Storage)9359 rollbackMedia();9360 9361 if (mBIOSSettings)9362 mBIOSSettings->rollback();9363 9364 #ifdef VBOX_WITH_VRDP9365 if (mVRDPServer && (mData->flModifications & IsModified_VRDPServer))9366 mVRDPServer->rollback();9367 #endif9368 9369 if (mAudioAdapter)9370 mAudioAdapter->rollback();9371 9372 if (mUSBController && (mData->flModifications & IsModified_USB))9373 mUSBController->rollback();9374 9375 ComPtr<INetworkAdapter> networkAdapters[RT_ELEMENTS(mNetworkAdapters)];9376 ComPtr<ISerialPort> serialPorts[RT_ELEMENTS(mSerialPorts)];9377 ComPtr<IParallelPort> parallelPorts[RT_ELEMENTS(mParallelPorts)];9378 9379 if (mData->flModifications & IsModified_NetworkAdapters)9380 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)9381 if ( mNetworkAdapters[slot]9382 && mNetworkAdapters[slot]->isModified())9383 {9384 mNetworkAdapters[slot]->rollback();9385 networkAdapters[slot] = mNetworkAdapters[slot];9386 }9387 9388 if (mData->flModifications & IsModified_SerialPorts)9389 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)9390 if ( mSerialPorts[slot]9391 && mSerialPorts[slot]->isModified())9392 {9393 mSerialPorts[slot]->rollback();9394 serialPorts[slot] = mSerialPorts[slot];9395 }9396 9397 if (mData->flModifications & IsModified_ParallelPorts)9398 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)9399 if ( mParallelPorts[slot]9400 && mParallelPorts[slot]->isModified())9401 {9402 mParallelPorts[slot]->rollback();9403 parallelPorts[slot] = mParallelPorts[slot];9404 }9405 9406 if (aNotify)9407 {9408 /* inform the direct session about changes */9409 9410 ComObjPtr<Machine> that = this;9411 uint32_t flModifications = mData->flModifications;9412 alock.leave();9413 9414 if (flModifications & IsModified_SharedFolders)9415 that->onSharedFolderChange();9416 9417 if (flModifications & IsModified_VRDPServer)9418 that->onVRDPServerChange(/* aRestart */ TRUE);9419 if (flModifications & IsModified_USB)9420 that->onUSBControllerChange();9421 9422 for (ULONG slot = 0; slot < RT_ELEMENTS(networkAdapters); slot ++)9423 if (networkAdapters[slot])9424 that->onNetworkAdapterChange(networkAdapters[slot], FALSE);9425 for (ULONG slot = 0; slot < RT_ELEMENTS(serialPorts); slot ++)9426 if (serialPorts[slot])9427 that->onSerialPortChange(serialPorts[slot]);9428 for (ULONG slot = 0; slot < RT_ELEMENTS(parallelPorts); slot ++)9429 if (parallelPorts[slot])9430 that->onParallelPortChange(parallelPorts[slot]);9431 9432 if (flModifications & IsModified_Storage)9433 that->onStorageControllerChange();9434 }9435 }9436 9437 /**9438 * Commits all the changes to machine settings.9439 *9440 * Note that this operation is supposed to never fail.9441 *9442 * @note Locks this object and children for writing.9443 */9444 void Machine::commit()9445 {9446 AutoCaller autoCaller(this);9447 AssertComRCReturnVoid(autoCaller.rc());9448 9449 AutoCaller peerCaller(mPeer);9450 AssertComRCReturnVoid(peerCaller.rc());9451 9452 AutoMultiWriteLock2 alock(mPeer, this COMMA_LOCKVAL_SRC_POS);9453 9454 /*9455 * use safe commit to ensure Snapshot machines (that share mUserData)9456 * will still refer to a valid memory location9457 */9458 mUserData.commitCopy();9459 9460 mHWData.commit();9461 9462 if (mMediaData.isBackedUp())9463 commitMedia();9464 9465 mBIOSSettings->commit();9466 #ifdef VBOX_WITH_VRDP9467 mVRDPServer->commit();9468 #endif9469 mAudioAdapter->commit();9470 mUSBController->commit();9471 9472 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)9473 mNetworkAdapters[slot]->commit();9474 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)9475 mSerialPorts[slot]->commit();9476 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)9477 mParallelPorts[slot]->commit();9478 9479 bool commitStorageControllers = false;9480 9481 if (mStorageControllers.isBackedUp())9482 {9483 mStorageControllers.commit();9484 9485 if (mPeer)9486 {9487 AutoWriteLock peerlock(mPeer COMMA_LOCKVAL_SRC_POS);9488 9489 /* Commit all changes to new controllers (this will reshare data with9490 * peers for thos who have peers) */9491 StorageControllerList *newList = new StorageControllerList();9492 StorageControllerList::const_iterator it = mStorageControllers->begin();9493 while (it != mStorageControllers->end())9494 {9495 (*it)->commit();9496 9497 /* look if this controller has a peer device */9498 ComObjPtr<StorageController> peer = (*it)->getPeer();9499 if (!peer)9500 {9501 /* no peer means the device is a newly created one;9502 * create a peer owning data this device share it with */9503 peer.createObject();9504 peer->init(mPeer, *it, true /* aReshare */);9505 }9506 else9507 {9508 /* remove peer from the old list */9509 mPeer->mStorageControllers->remove(peer);9510 }9511 /* and add it to the new list */9512 newList->push_back(peer);9513 9514 ++it;9515 }9516 9517 /* uninit old peer's controllers that are left */9518 it = mPeer->mStorageControllers->begin();9519 while (it != mPeer->mStorageControllers->end())9520 {9521 (*it)->uninit();9522 ++it;9523 }9524 9525 /* attach new list of controllers to our peer */9526 mPeer->mStorageControllers.attach(newList);9527 }9528 else9529 {9530 /* we have no peer (our parent is the newly created machine);9531 * just commit changes to devices */9532 commitStorageControllers = true;9533 }9534 }9535 else9536 {9537 /* the list of controllers itself is not changed,9538 * just commit changes to controllers themselves */9539 commitStorageControllers = true;9540 }9541 9542 if (commitStorageControllers)9543 {9544 StorageControllerList::const_iterator it = mStorageControllers->begin();9545 while (it != mStorageControllers->end())9546 {9547 (*it)->commit();9548 ++it;9549 }9550 }9551 9552 if (isSessionMachine())9553 {9554 /* attach new data to the primary machine and reshare it */9555 mPeer->mUserData.attach(mUserData);9556 mPeer->mHWData.attach(mHWData);9557 /* mMediaData is reshared by fixupMedia */9558 // mPeer->mMediaData.attach(mMediaData);9559 Assert(mPeer->mMediaData.data() == mMediaData.data());9560 }9561 }9562 9563 /**9564 * Copies all the hardware data from the given machine.9565 *9566 * Currently, only called when the VM is being restored from a snapshot. In9567 * particular, this implies that the VM is not running during this method's9568 * call.9569 *9570 * @note This method must be called from under this object's lock.9571 *9572 * @note This method doesn't call #commit(), so all data remains backed up and9573 * unsaved.9574 */9575 void Machine::copyFrom(Machine *aThat)9576 {9577 AssertReturnVoid(!isSnapshotMachine());9578 AssertReturnVoid(aThat->isSnapshotMachine());9579 9580 AssertReturnVoid(!Global::IsOnline(mData->mMachineState));9581 9582 mHWData.assignCopy(aThat->mHWData);9583 9584 // create copies of all shared folders (mHWData after attiching a copy9585 // contains just references to original objects)9586 for (HWData::SharedFolderList::iterator it = mHWData->mSharedFolders.begin();9587 it != mHWData->mSharedFolders.end();9588 ++it)9589 {9590 ComObjPtr<SharedFolder> folder;9591 folder.createObject();9592 HRESULT rc = folder->initCopy(getMachine(), *it);9593 AssertComRC(rc);9594 *it = folder;9595 }9596 9597 mBIOSSettings->copyFrom(aThat->mBIOSSettings);9598 #ifdef VBOX_WITH_VRDP9599 mVRDPServer->copyFrom(aThat->mVRDPServer);9600 #endif9601 mAudioAdapter->copyFrom(aThat->mAudioAdapter);9602 mUSBController->copyFrom(aThat->mUSBController);9603 9604 /* create private copies of all controllers */9605 mStorageControllers.backup();9606 mStorageControllers->clear();9607 for (StorageControllerList::iterator it = aThat->mStorageControllers->begin();9608 it != aThat->mStorageControllers->end();9609 ++it)9610 {9611 ComObjPtr<StorageController> ctrl;9612 ctrl.createObject();9613 ctrl->initCopy(this, *it);9614 mStorageControllers->push_back(ctrl);9615 }9616 9617 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)9618 mNetworkAdapters[slot]->copyFrom(aThat->mNetworkAdapters[slot]);9619 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)9620 mSerialPorts[slot]->copyFrom(aThat->mSerialPorts[slot]);9621 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)9622 mParallelPorts[slot]->copyFrom(aThat->mParallelPorts[slot]);9623 }9624 9625 #ifdef VBOX_WITH_RESOURCE_USAGE_API9626 9627 void Machine::registerMetrics(PerformanceCollector *aCollector, Machine *aMachine, RTPROCESS pid)9628 {9629 AssertReturnVoid(isWriteLockOnCurrentThread());9630 AssertPtrReturnVoid(aCollector);9631 9632 pm::CollectorHAL *hal = aCollector->getHAL();9633 /* Create sub metrics */9634 pm::SubMetric *cpuLoadUser = new pm::SubMetric("CPU/Load/User",9635 "Percentage of processor time spent in user mode by the VM process.");9636 pm::SubMetric *cpuLoadKernel = new pm::SubMetric("CPU/Load/Kernel",9637 "Percentage of processor time spent in kernel mode by the VM process.");9638 pm::SubMetric *ramUsageUsed = new pm::SubMetric("RAM/Usage/Used",9639 "Size of resident portion of VM process in memory.");9640 /* Create and register base metrics */9641 pm::BaseMetric *cpuLoad = new pm::MachineCpuLoadRaw(hal, aMachine, pid,9642 cpuLoadUser, cpuLoadKernel);9643 aCollector->registerBaseMetric(cpuLoad);9644 pm::BaseMetric *ramUsage = new pm::MachineRamUsage(hal, aMachine, pid,9645 ramUsageUsed);9646 aCollector->registerBaseMetric(ramUsage);9647 9648 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser, 0));9649 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,9650 new pm::AggregateAvg()));9651 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,9652 new pm::AggregateMin()));9653 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadUser,9654 new pm::AggregateMax()));9655 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel, 0));9656 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,9657 new pm::AggregateAvg()));9658 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,9659 new pm::AggregateMin()));9660 aCollector->registerMetric(new pm::Metric(cpuLoad, cpuLoadKernel,9661 new pm::AggregateMax()));9662 9663 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed, 0));9664 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,9665 new pm::AggregateAvg()));9666 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,9667 new pm::AggregateMin()));9668 aCollector->registerMetric(new pm::Metric(ramUsage, ramUsageUsed,9669 new pm::AggregateMax()));9670 9671 9672 /* Guest metrics */9673 mGuestHAL = new pm::CollectorGuestHAL(this, hal);9674 9675 /* Create sub metrics */9676 pm::SubMetric *guestLoadUser = new pm::SubMetric("Guest/CPU/Load/User",9677 "Percentage of processor time spent in user mode as seen by the guest.");9678 pm::SubMetric *guestLoadKernel = new pm::SubMetric("Guest/CPU/Load/Kernel",9679 "Percentage of processor time spent in kernel mode as seen by the guest.");9680 pm::SubMetric *guestLoadIdle = new pm::SubMetric("Guest/CPU/Load/Idle",9681 "Percentage of processor time spent idling as seen by the guest.");9682 9683 /* The total amount of physical ram is fixed now, but we'll support dynamic guest ram configurations in the future. */9684 pm::SubMetric *guestMemTotal = new pm::SubMetric("Guest/RAM/Usage/Total", "Total amount of physical guest RAM.");9685 pm::SubMetric *guestMemFree = new pm::SubMetric("Guest/RAM/Usage/Free", "Free amount of physical guest RAM.");9686 pm::SubMetric *guestMemBalloon = new pm::SubMetric("Guest/RAM/Usage/Balloon", "Amount of ballooned physical guest RAM.");9687 pm::SubMetric *guestMemShared = new pm::SubMetric("Guest/RAM/Usage/Shared", "Amount of shared physical guest RAM.");9688 pm::SubMetric *guestMemCache = new pm::SubMetric("Guest/RAM/Usage/Cache", "Total amount of guest (disk) cache memory.");9689 9690 pm::SubMetric *guestPagedTotal = new pm::SubMetric("Guest/Pagefile/Usage/Total", "Total amount of space in the page file.");9691 9692 /* Create and register base metrics */9693 pm::BaseMetric *guestCpuLoad = new pm::GuestCpuLoad(mGuestHAL, aMachine, guestLoadUser, guestLoadKernel, guestLoadIdle);9694 aCollector->registerBaseMetric(guestCpuLoad);9695 9696 pm::BaseMetric *guestCpuMem = new pm::GuestRamUsage(mGuestHAL, aMachine, guestMemTotal, guestMemFree, guestMemBalloon, guestMemShared,9697 guestMemCache, guestPagedTotal);9698 aCollector->registerBaseMetric(guestCpuMem);9699 9700 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, 0));9701 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateAvg()));9702 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMin()));9703 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadUser, new pm::AggregateMax()));9704 9705 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, 0));9706 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateAvg()));9707 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMin()));9708 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadKernel, new pm::AggregateMax()));9709 9710 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, 0));9711 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateAvg()));9712 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMin()));9713 aCollector->registerMetric(new pm::Metric(guestCpuLoad, guestLoadIdle, new pm::AggregateMax()));9714 9715 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, 0));9716 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateAvg()));9717 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMin()));9718 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemTotal, new pm::AggregateMax()));9719 9720 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, 0));9721 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateAvg()));9722 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMin()));9723 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemFree, new pm::AggregateMax()));9724 9725 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, 0));9726 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateAvg()));9727 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMin()));9728 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemBalloon, new pm::AggregateMax()));9729 9730 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, 0));9731 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateAvg()));9732 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMin()));9733 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemShared, new pm::AggregateMax()));9734 9735 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, 0));9736 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateAvg()));9737 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMin()));9738 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestMemCache, new pm::AggregateMax()));9739 9740 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, 0));9741 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateAvg()));9742 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMin()));9743 aCollector->registerMetric(new pm::Metric(guestCpuMem, guestPagedTotal, new pm::AggregateMax()));9744 }9745 9746 void Machine::unregisterMetrics(PerformanceCollector *aCollector, Machine *aMachine)9747 {9748 AssertReturnVoid(isWriteLockOnCurrentThread());9749 9750 if (aCollector)9751 {9752 aCollector->unregisterMetricsFor(aMachine);9753 aCollector->unregisterBaseMetricsFor(aMachine);9754 }9755 9756 if (mGuestHAL)9757 {9758 delete mGuestHAL;9759 mGuestHAL = NULL;9760 }9761 }9762 9763 #endif /* VBOX_WITH_RESOURCE_USAGE_API */9764 9765 9766 ////////////////////////////////////////////////////////////////////////////////9767 9768 DEFINE_EMPTY_CTOR_DTOR(SessionMachine)9769 9770 HRESULT SessionMachine::FinalConstruct()9771 {9772 LogFlowThisFunc(("\n"));9773 9774 #if defined(RT_OS_WINDOWS)9775 mIPCSem = NULL;9776 #elif defined(RT_OS_OS2)9777 mIPCSem = NULLHANDLE;9778 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)9779 mIPCSem = -1;9780 #else9781 # error "Port me!"9782 #endif9783 9784 return S_OK;9785 }9786 9787 void SessionMachine::FinalRelease()9788 {9789 LogFlowThisFunc(("\n"));9790 9791 uninit(Uninit::Unexpected);9792 }9793 9794 /**9795 * @note Must be called only by Machine::openSession() from its own write lock.9796 */9797 HRESULT SessionMachine::init(Machine *aMachine)9798 {9799 LogFlowThisFuncEnter();9800 LogFlowThisFunc(("mName={%s}\n", aMachine->mUserData->s.strName.c_str()));9801 9802 AssertReturn(aMachine, E_INVALIDARG);9803 9804 AssertReturn(aMachine->lockHandle()->isWriteLockOnCurrentThread(), E_FAIL);9805 9806 /* Enclose the state transition NotReady->InInit->Ready */9807 AutoInitSpan autoInitSpan(this);9808 AssertReturn(autoInitSpan.isOk(), E_FAIL);9809 9810 /* create the interprocess semaphore */9811 #if defined(RT_OS_WINDOWS)9812 mIPCSemName = aMachine->mData->m_strConfigFileFull;9813 for (size_t i = 0; i < mIPCSemName.length(); i++)9814 if (mIPCSemName[i] == '\\')9815 mIPCSemName[i] = '/';9816 mIPCSem = ::CreateMutex(NULL, FALSE, mIPCSemName);9817 ComAssertMsgRet(mIPCSem,9818 ("Cannot create IPC mutex '%ls', err=%d",9819 mIPCSemName.raw(), ::GetLastError()),9820 E_FAIL);9821 #elif defined(RT_OS_OS2)9822 Utf8Str ipcSem = Utf8StrFmt("\\SEM32\\VBOX\\VM\\{%RTuuid}",9823 aMachine->mData->mUuid.raw());9824 mIPCSemName = ipcSem;9825 APIRET arc = ::DosCreateMutexSem((PSZ)ipcSem.raw(), &mIPCSem, 0, FALSE);9826 ComAssertMsgRet(arc == NO_ERROR,9827 ("Cannot create IPC mutex '%s', arc=%ld",9828 ipcSem.raw(), arc),9829 E_FAIL);9830 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)9831 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN9832 # if defined(RT_OS_FREEBSD) && (HC_ARCH_BITS == 64)9833 /** @todo Check that this still works correctly. */9834 AssertCompileSize(key_t, 8);9835 # else9836 AssertCompileSize(key_t, 4);9837 # endif9838 key_t key;9839 mIPCSem = -1;9840 mIPCKey = "0";9841 for (uint32_t i = 0; i < 1 << 24; i++)9842 {9843 key = ((uint32_t)'V' << 24) | i;9844 int sem = ::semget(key, 1, S_IRUSR | S_IWUSR | IPC_CREAT | IPC_EXCL);9845 if (sem >= 0 || (errno != EEXIST && errno != EACCES))9846 {9847 mIPCSem = sem;9848 if (sem >= 0)9849 mIPCKey = BstrFmt("%u", key);9850 break;9851 }9852 }9853 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */9854 Utf8Str semName = aMachine->mData->m_strConfigFileFull;9855 char *pszSemName = NULL;9856 RTStrUtf8ToCurrentCP(&pszSemName, semName);9857 key_t key = ::ftok(pszSemName, 'V');9858 RTStrFree(pszSemName);9859 9860 mIPCSem = ::semget(key, 1, S_IRWXU | S_IRWXG | S_IRWXO | IPC_CREAT);9861 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */9862 9863 int errnoSave = errno;9864 if (mIPCSem < 0 && errnoSave == ENOSYS)9865 {9866 setError(E_FAIL,9867 tr("Cannot create IPC semaphore. Most likely your host kernel lacks "9868 "support for SysV IPC. Check the host kernel configuration for "9869 "CONFIG_SYSVIPC=y"));9870 return E_FAIL;9871 }9872 /* ENOSPC can also be the result of VBoxSVC crashes without properly freeing9873 * the IPC semaphores */9874 if (mIPCSem < 0 && errnoSave == ENOSPC)9875 {9876 #ifdef RT_OS_LINUX9877 setError(E_FAIL,9878 tr("Cannot create IPC semaphore because the system limit for the "9879 "maximum number of semaphore sets (SEMMNI), or the system wide "9880 "maximum number of sempahores (SEMMNS) would be exceeded. The "9881 "current set of SysV IPC semaphores can be determined from "9882 "the file /proc/sysvipc/sem"));9883 #else9884 setError(E_FAIL,9885 tr("Cannot create IPC semaphore because the system-imposed limit "9886 "on the maximum number of allowed semaphores or semaphore "9887 "identifiers system-wide would be exceeded"));9888 #endif9889 return E_FAIL;9890 }9891 ComAssertMsgRet(mIPCSem >= 0, ("Cannot create IPC semaphore, errno=%d", errnoSave),9892 E_FAIL);9893 /* set the initial value to 1 */9894 int rv = ::semctl(mIPCSem, 0, SETVAL, 1);9895 ComAssertMsgRet(rv == 0, ("Cannot init IPC semaphore, errno=%d", errno),9896 E_FAIL);9897 #else9898 # error "Port me!"9899 #endif9900 9901 /* memorize the peer Machine */9902 unconst(mPeer) = aMachine;9903 /* share the parent pointer */9904 unconst(mParent) = aMachine->mParent;9905 9906 /* take the pointers to data to share */9907 mData.share(aMachine->mData);9908 mSSData.share(aMachine->mSSData);9909 9910 mUserData.share(aMachine->mUserData);9911 mHWData.share(aMachine->mHWData);9912 mMediaData.share(aMachine->mMediaData);9913 9914 mStorageControllers.allocate();9915 for (StorageControllerList::const_iterator it = aMachine->mStorageControllers->begin();9916 it != aMachine->mStorageControllers->end();9917 ++it)9918 {9919 ComObjPtr<StorageController> ctl;9920 ctl.createObject();9921 ctl->init(this, *it);9922 mStorageControllers->push_back(ctl);9923 }9924 9925 unconst(mBIOSSettings).createObject();9926 mBIOSSettings->init(this, aMachine->mBIOSSettings);9927 #ifdef VBOX_WITH_VRDP9928 /* create another VRDPServer object that will be mutable */9929 unconst(mVRDPServer).createObject();9930 mVRDPServer->init(this, aMachine->mVRDPServer);9931 #endif9932 /* create another audio adapter object that will be mutable */9933 unconst(mAudioAdapter).createObject();9934 mAudioAdapter->init(this, aMachine->mAudioAdapter);9935 /* create a list of serial ports that will be mutable */9936 for (ULONG slot = 0; slot < RT_ELEMENTS(mSerialPorts); slot++)9937 {9938 unconst(mSerialPorts[slot]).createObject();9939 mSerialPorts[slot]->init(this, aMachine->mSerialPorts[slot]);9940 }9941 /* create a list of parallel ports that will be mutable */9942 for (ULONG slot = 0; slot < RT_ELEMENTS(mParallelPorts); slot++)9943 {9944 unconst(mParallelPorts[slot]).createObject();9945 mParallelPorts[slot]->init(this, aMachine->mParallelPorts[slot]);9946 }9947 /* create another USB controller object that will be mutable */9948 unconst(mUSBController).createObject();9949 mUSBController->init(this, aMachine->mUSBController);9950 9951 /* create a list of network adapters that will be mutable */9952 for (ULONG slot = 0; slot < RT_ELEMENTS(mNetworkAdapters); slot++)9953 {9954 unconst(mNetworkAdapters[slot]).createObject();9955 mNetworkAdapters[slot]->init(this, aMachine->mNetworkAdapters[slot]);9956 }9957 9958 /* default is to delete saved state on Saved -> PoweredOff transition */9959 mRemoveSavedState = true;9960 9961 /* Confirm a successful initialization when it's the case */9962 autoInitSpan.setSucceeded();9963 9964 LogFlowThisFuncLeave();9965 return S_OK;9966 }9967 9968 /**9969 * Uninitializes this session object. If the reason is other than9970 * Uninit::Unexpected, then this method MUST be called from #checkForDeath().9971 *9972 * @param aReason uninitialization reason9973 *9974 * @note Locks mParent + this object for writing.9975 */9976 void SessionMachine::uninit(Uninit::Reason aReason)9977 {9978 LogFlowThisFuncEnter();9979 LogFlowThisFunc(("reason=%d\n", aReason));9980 9981 /*9982 * Strongly reference ourselves to prevent this object deletion after9983 * mData->mSession.mMachine.setNull() below (which can release the last9984 * reference and call the destructor). Important: this must be done before9985 * accessing any members (and before AutoUninitSpan that does it as well).9986 * This self reference will be released as the very last step on return.9987 */9988 ComObjPtr<SessionMachine> selfRef = this;9989 9990 /* Enclose the state transition Ready->InUninit->NotReady */9991 AutoUninitSpan autoUninitSpan(this);9992 if (autoUninitSpan.uninitDone())9993 {9994 LogFlowThisFunc(("Already uninitialized\n"));9995 LogFlowThisFuncLeave();9996 return;9997 }9998 9999 if (autoUninitSpan.initFailed())10000 {10001 /* We've been called by init() because it's failed. It's not really10002 * necessary (nor it's safe) to perform the regular uninit sequense10003 * below, the following is enough.10004 */10005 LogFlowThisFunc(("Initialization failed.\n"));10006 #if defined(RT_OS_WINDOWS)10007 if (mIPCSem)10008 ::CloseHandle(mIPCSem);10009 mIPCSem = NULL;10010 #elif defined(RT_OS_OS2)10011 if (mIPCSem != NULLHANDLE)10012 ::DosCloseMutexSem(mIPCSem);10013 mIPCSem = NULLHANDLE;10014 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)10015 if (mIPCSem >= 0)10016 ::semctl(mIPCSem, 0, IPC_RMID);10017 mIPCSem = -1;10018 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN10019 mIPCKey = "0";10020 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */10021 #else10022 # error "Port me!"10023 #endif10024 uninitDataAndChildObjects();10025 mData.free();10026 unconst(mParent) = NULL;10027 unconst(mPeer) = NULL;10028 LogFlowThisFuncLeave();10029 return;10030 }10031 10032 MachineState_T lastState;10033 {10034 AutoReadLock tempLock(this COMMA_LOCKVAL_SRC_POS);10035 lastState = mData->mMachineState;10036 }10037 NOREF(lastState);10038 10039 #ifdef VBOX_WITH_USB10040 // release all captured USB devices, but do this before requesting the locks below10041 if (aReason == Uninit::Abnormal && Global::IsOnline(lastState))10042 {10043 /* Console::captureUSBDevices() is called in the VM process only after10044 * setting the machine state to Starting or Restoring.10045 * Console::detachAllUSBDevices() will be called upon successful10046 * termination. So, we need to release USB devices only if there was10047 * an abnormal termination of a running VM.10048 *10049 * This is identical to SessionMachine::DetachAllUSBDevices except10050 * for the aAbnormal argument. */10051 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);10052 AssertComRC(rc);10053 NOREF(rc);10054 10055 USBProxyService *service = mParent->host()->usbProxyService();10056 if (service)10057 service->detachAllDevicesFromVM(this, true /* aDone */, true /* aAbnormal */);10058 }10059 #endif /* VBOX_WITH_USB */10060 10061 // we need to lock this object in uninit() because the lock is shared10062 // with mPeer (as well as data we modify below). mParent->addProcessToReap()10063 // and others need mParent lock, and USB needs host lock.10064 AutoMultiWriteLock3 multilock(mParent, mParent->host(), this COMMA_LOCKVAL_SRC_POS);10065 10066 // Trigger async cleanup tasks, avoid doing things here which are not10067 // vital to be done immediately and maybe need more locks. This calls10068 // Machine::unregisterMetrics().10069 mParent->onMachineUninit(mPeer);10070 10071 if (aReason == Uninit::Abnormal)10072 {10073 LogWarningThisFunc(("ABNORMAL client termination! (wasBusy=%d)\n",10074 Global::IsOnlineOrTransient(lastState)));10075 10076 /* reset the state to Aborted */10077 if (mData->mMachineState != MachineState_Aborted)10078 setMachineState(MachineState_Aborted);10079 }10080 10081 // any machine settings modified?10082 if (mData->flModifications)10083 {10084 LogWarningThisFunc(("Discarding unsaved settings changes!\n"));10085 rollback(false /* aNotify */);10086 }10087 10088 Assert(mSnapshotData.mStateFilePath.isEmpty() || !mSnapshotData.mSnapshot);10089 if (!mSnapshotData.mStateFilePath.isEmpty())10090 {10091 LogWarningThisFunc(("canceling failed save state request!\n"));10092 endSavingState(FALSE /* aSuccess */);10093 }10094 else if (!mSnapshotData.mSnapshot.isNull())10095 {10096 LogWarningThisFunc(("canceling untaken snapshot!\n"));10097 10098 /* delete all differencing hard disks created (this will also attach10099 * their parents back by rolling back mMediaData) */10100 rollbackMedia();10101 /* delete the saved state file (it might have been already created) */10102 if (mSnapshotData.mSnapshot->stateFilePath().length())10103 RTFileDelete(mSnapshotData.mSnapshot->stateFilePath().c_str());10104 10105 mSnapshotData.mSnapshot->uninit();10106 }10107 10108 if (!mData->mSession.mType.isEmpty())10109 {10110 /* mType is not null when this machine's process has been started by10111 * Machine::launchVMProcess(), therefore it is our child. We10112 * need to queue the PID to reap the process (and avoid zombies on10113 * Linux). */10114 Assert(mData->mSession.mPid != NIL_RTPROCESS);10115 mParent->addProcessToReap(mData->mSession.mPid);10116 }10117 10118 mData->mSession.mPid = NIL_RTPROCESS;10119 10120 if (aReason == Uninit::Unexpected)10121 {10122 /* Uninitialization didn't come from #checkForDeath(), so tell the10123 * client watcher thread to update the set of machines that have open10124 * sessions. */10125 mParent->updateClientWatcher();10126 }10127 10128 /* uninitialize all remote controls */10129 if (mData->mSession.mRemoteControls.size())10130 {10131 LogFlowThisFunc(("Closing remote sessions (%d):\n",10132 mData->mSession.mRemoteControls.size()));10133 10134 Data::Session::RemoteControlList::iterator it =10135 mData->mSession.mRemoteControls.begin();10136 while (it != mData->mSession.mRemoteControls.end())10137 {10138 LogFlowThisFunc((" Calling remoteControl->Uninitialize()...\n"));10139 HRESULT rc = (*it)->Uninitialize();10140 LogFlowThisFunc((" remoteControl->Uninitialize() returned %08X\n", rc));10141 if (FAILED(rc))10142 LogWarningThisFunc(("Forgot to close the remote session?\n"));10143 ++it;10144 }10145 mData->mSession.mRemoteControls.clear();10146 }10147 10148 /*10149 * An expected uninitialization can come only from #checkForDeath().10150 * Otherwise it means that something's got really wrong (for examlple,10151 * the Session implementation has released the VirtualBox reference10152 * before it triggered #OnSessionEnd(), or before releasing IPC semaphore,10153 * etc). However, it's also possible, that the client releases the IPC10154 * semaphore correctly (i.e. before it releases the VirtualBox reference),10155 * but the VirtualBox release event comes first to the server process.10156 * This case is practically possible, so we should not assert on an10157 * unexpected uninit, just log a warning.10158 */10159 10160 if ((aReason == Uninit::Unexpected))10161 LogWarningThisFunc(("Unexpected SessionMachine uninitialization!\n"));10162 10163 if (aReason != Uninit::Normal)10164 {10165 mData->mSession.mDirectControl.setNull();10166 }10167 else10168 {10169 /* this must be null here (see #OnSessionEnd()) */10170 Assert(mData->mSession.mDirectControl.isNull());10171 Assert(mData->mSession.mState == SessionState_Unlocking);10172 Assert(!mData->mSession.mProgress.isNull());10173 }10174 if (mData->mSession.mProgress)10175 {10176 if (aReason == Uninit::Normal)10177 mData->mSession.mProgress->notifyComplete(S_OK);10178 else10179 mData->mSession.mProgress->notifyComplete(E_FAIL,10180 COM_IIDOF(ISession),10181 getComponentName(),10182 tr("The VM session was aborted"));10183 mData->mSession.mProgress.setNull();10184 }10185 10186 /* remove the association between the peer machine and this session machine */10187 Assert( (SessionMachine*)mData->mSession.mMachine == this10188 || aReason == Uninit::Unexpected);10189 10190 /* reset the rest of session data */10191 mData->mSession.mMachine.setNull();10192 mData->mSession.mState = SessionState_Unlocked;10193 mData->mSession.mType.setNull();10194 10195 /* close the interprocess semaphore before leaving the exclusive lock */10196 #if defined(RT_OS_WINDOWS)10197 if (mIPCSem)10198 ::CloseHandle(mIPCSem);10199 mIPCSem = NULL;10200 #elif defined(RT_OS_OS2)10201 if (mIPCSem != NULLHANDLE)10202 ::DosCloseMutexSem(mIPCSem);10203 mIPCSem = NULLHANDLE;10204 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)10205 if (mIPCSem >= 0)10206 ::semctl(mIPCSem, 0, IPC_RMID);10207 mIPCSem = -1;10208 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN10209 mIPCKey = "0";10210 # endif /* VBOX_WITH_NEW_SYS_V_KEYGEN */10211 #else10212 # error "Port me!"10213 #endif10214 10215 /* fire an event */10216 mParent->onSessionStateChange(mData->mUuid, SessionState_Unlocked);10217 10218 uninitDataAndChildObjects();10219 10220 /* free the essential data structure last */10221 mData.free();10222 10223 #if 1 /** @todo Please review this change! (bird) */10224 /* drop the exclusive lock before setting the below two to NULL */10225 multilock.release();10226 #else10227 /* leave the exclusive lock before setting the below two to NULL */10228 multilock.leave();10229 #endif10230 10231 unconst(mParent) = NULL;10232 unconst(mPeer) = NULL;10233 10234 LogFlowThisFuncLeave();10235 }10236 10237 // util::Lockable interface10238 ////////////////////////////////////////////////////////////////////////////////10239 10240 /**10241 * Overrides VirtualBoxBase::lockHandle() in order to share the lock handle10242 * with the primary Machine instance (mPeer).10243 */10244 RWLockHandle *SessionMachine::lockHandle() const10245 {10246 AssertReturn(mPeer != NULL, NULL);10247 return mPeer->lockHandle();10248 }10249 10250 // IInternalMachineControl methods10251 ////////////////////////////////////////////////////////////////////////////////10252 10253 /**10254 * @note Locks this object for writing.10255 */10256 STDMETHODIMP SessionMachine::SetRemoveSavedStateFile(BOOL aRemove)10257 {10258 AutoCaller autoCaller(this);10259 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10260 10261 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);10262 10263 mRemoveSavedState = aRemove;10264 10265 return S_OK;10266 }10267 10268 /**10269 * @note Locks the same as #setMachineState() does.10270 */10271 STDMETHODIMP SessionMachine::UpdateState(MachineState_T aMachineState)10272 {10273 return setMachineState(aMachineState);10274 }10275 10276 /**10277 * @note Locks this object for reading.10278 */10279 STDMETHODIMP SessionMachine::GetIPCId(BSTR *aId)10280 {10281 AutoCaller autoCaller(this);10282 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10283 10284 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);10285 10286 #if defined(RT_OS_WINDOWS) || defined(RT_OS_OS2)10287 mIPCSemName.cloneTo(aId);10288 return S_OK;10289 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)10290 # ifdef VBOX_WITH_NEW_SYS_V_KEYGEN10291 mIPCKey.cloneTo(aId);10292 # else /* !VBOX_WITH_NEW_SYS_V_KEYGEN */10293 mData->m_strConfigFileFull.cloneTo(aId);10294 # endif /* !VBOX_WITH_NEW_SYS_V_KEYGEN */10295 return S_OK;10296 #else10297 # error "Port me!"10298 #endif10299 }10300 10301 /**10302 * @note Locks this object for writing.10303 */10304 STDMETHODIMP SessionMachine::BeginPowerUp(IProgress *aProgress)10305 {10306 LogFlowThisFunc(("aProgress=%p\n", aProgress));10307 AutoCaller autoCaller(this);10308 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10309 10310 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);10311 10312 if (mData->mSession.mState != SessionState_Locked)10313 return VBOX_E_INVALID_OBJECT_STATE;10314 10315 if (!mData->mSession.mProgress.isNull())10316 mData->mSession.mProgress->setOtherProgressObject(aProgress);10317 10318 LogFlowThisFunc(("returns S_OK.\n"));10319 return S_OK;10320 }10321 10322 10323 /**10324 * @note Locks this object for writing.10325 */10326 STDMETHODIMP SessionMachine::EndPowerUp(LONG iResult)10327 {10328 AutoCaller autoCaller(this);10329 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10330 10331 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);10332 10333 if (mData->mSession.mState != SessionState_Locked)10334 return VBOX_E_INVALID_OBJECT_STATE;10335 10336 /* Finalize the openRemoteSession progress object. */10337 if (mData->mSession.mProgress)10338 {10339 mData->mSession.mProgress->notifyComplete((HRESULT)iResult);10340 mData->mSession.mProgress.setNull();10341 10342 if (SUCCEEDED((HRESULT)iResult))10343 {10344 #ifdef VBOX_WITH_RESOURCE_USAGE_API10345 /* The VM has been powered up successfully, so it makes sense10346 * now to offer the performance metrics for a running machine10347 * object. Doing it earlier wouldn't be safe. */10348 registerMetrics(mParent->performanceCollector(), mPeer,10349 mData->mSession.mPid);10350 #endif /* VBOX_WITH_RESOURCE_USAGE_API */10351 10352 }10353 }10354 return S_OK;10355 }10356 10357 /**10358 * Goes through the USB filters of the given machine to see if the given10359 * device matches any filter or not.10360 *10361 * @note Locks the same as USBController::hasMatchingFilter() does.10362 */10363 STDMETHODIMP SessionMachine::RunUSBDeviceFilters(IUSBDevice *aUSBDevice,10364 BOOL *aMatched,10365 ULONG *aMaskedIfs)10366 {10367 LogFlowThisFunc(("\n"));10368 10369 CheckComArgNotNull(aUSBDevice);10370 CheckComArgOutPointerValid(aMatched);10371 10372 AutoCaller autoCaller(this);10373 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10374 10375 #ifdef VBOX_WITH_USB10376 *aMatched = mUSBController->hasMatchingFilter(aUSBDevice, aMaskedIfs);10377 #else10378 NOREF(aUSBDevice);10379 NOREF(aMaskedIfs);10380 *aMatched = FALSE;10381 #endif10382 10383 return S_OK;10384 }10385 10386 /**10387 * @note Locks the same as Host::captureUSBDevice() does.10388 */10389 STDMETHODIMP SessionMachine::CaptureUSBDevice(IN_BSTR aId)10390 {10391 LogFlowThisFunc(("\n"));10392 10393 AutoCaller autoCaller(this);10394 AssertComRCReturnRC(autoCaller.rc());10395 10396 #ifdef VBOX_WITH_USB10397 /* if captureDeviceForVM() fails, it must have set extended error info */10398 MultiResult rc = mParent->host()->checkUSBProxyService();10399 if (FAILED(rc)) return rc;10400 10401 USBProxyService *service = mParent->host()->usbProxyService();10402 AssertReturn(service, E_FAIL);10403 return service->captureDeviceForVM(this, Guid(aId));10404 #else10405 NOREF(aId);10406 return E_NOTIMPL;10407 #endif10408 }10409 10410 /**10411 * @note Locks the same as Host::detachUSBDevice() does.10412 */10413 STDMETHODIMP SessionMachine::DetachUSBDevice(IN_BSTR aId, BOOL aDone)10414 {10415 LogFlowThisFunc(("\n"));10416 10417 AutoCaller autoCaller(this);10418 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10419 10420 #ifdef VBOX_WITH_USB10421 USBProxyService *service = mParent->host()->usbProxyService();10422 AssertReturn(service, E_FAIL);10423 return service->detachDeviceFromVM(this, Guid(aId), !!aDone);10424 #else10425 NOREF(aId);10426 NOREF(aDone);10427 return E_NOTIMPL;10428 #endif10429 }10430 10431 /**10432 * Inserts all machine filters to the USB proxy service and then calls10433 * Host::autoCaptureUSBDevices().10434 *10435 * Called by Console from the VM process upon VM startup.10436 *10437 * @note Locks what called methods lock.10438 */10439 STDMETHODIMP SessionMachine::AutoCaptureUSBDevices()10440 {10441 LogFlowThisFunc(("\n"));10442 10443 AutoCaller autoCaller(this);10444 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10445 10446 #ifdef VBOX_WITH_USB10447 HRESULT rc = mUSBController->notifyProxy(true /* aInsertFilters */);10448 AssertComRC(rc);10449 NOREF(rc);10450 10451 USBProxyService *service = mParent->host()->usbProxyService();10452 AssertReturn(service, E_FAIL);10453 return service->autoCaptureDevicesForVM(this);10454 #else10455 return S_OK;10456 #endif10457 }10458 10459 /**10460 * Removes all machine filters from the USB proxy service and then calls10461 * Host::detachAllUSBDevices().10462 *10463 * Called by Console from the VM process upon normal VM termination or by10464 * SessionMachine::uninit() upon abnormal VM termination (from under the10465 * Machine/SessionMachine lock).10466 *10467 * @note Locks what called methods lock.10468 */10469 STDMETHODIMP SessionMachine::DetachAllUSBDevices(BOOL aDone)10470 {10471 LogFlowThisFunc(("\n"));10472 10473 AutoCaller autoCaller(this);10474 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10475 10476 #ifdef VBOX_WITH_USB10477 HRESULT rc = mUSBController->notifyProxy(false /* aInsertFilters */);10478 AssertComRC(rc);10479 NOREF(rc);10480 10481 USBProxyService *service = mParent->host()->usbProxyService();10482 AssertReturn(service, E_FAIL);10483 return service->detachAllDevicesFromVM(this, !!aDone, false /* aAbnormal */);10484 #else10485 NOREF(aDone);10486 return S_OK;10487 #endif10488 }10489 10490 /**10491 * @note Locks this object for writing.10492 */10493 STDMETHODIMP SessionMachine::OnSessionEnd(ISession *aSession,10494 IProgress **aProgress)10495 {10496 LogFlowThisFuncEnter();10497 10498 AssertReturn(aSession, E_INVALIDARG);10499 AssertReturn(aProgress, E_INVALIDARG);10500 10501 AutoCaller autoCaller(this);10502 10503 LogFlowThisFunc(("callerstate=%d\n", autoCaller.state()));10504 /*10505 * We don't assert below because it might happen that a non-direct session10506 * informs us it is closed right after we've been uninitialized -- it's ok.10507 */10508 if (FAILED(autoCaller.rc())) return autoCaller.rc();10509 10510 /* get IInternalSessionControl interface */10511 ComPtr<IInternalSessionControl> control(aSession);10512 10513 ComAssertRet(!control.isNull(), E_INVALIDARG);10514 10515 /* Creating a Progress object requires the VirtualBox lock, and10516 * thus locking it here is required by the lock order rules. */10517 AutoMultiWriteLock2 alock(mParent->lockHandle(), this->lockHandle() COMMA_LOCKVAL_SRC_POS);10518 10519 if (control == mData->mSession.mDirectControl)10520 {10521 ComAssertRet(aProgress, E_POINTER);10522 10523 /* The direct session is being normally closed by the client process10524 * ----------------------------------------------------------------- */10525 10526 /* go to the closing state (essential for all open*Session() calls and10527 * for #checkForDeath()) */10528 Assert(mData->mSession.mState == SessionState_Locked);10529 mData->mSession.mState = SessionState_Unlocking;10530 10531 /* set direct control to NULL to release the remote instance */10532 mData->mSession.mDirectControl.setNull();10533 LogFlowThisFunc(("Direct control is set to NULL\n"));10534 10535 if (mData->mSession.mProgress)10536 {10537 /* finalize the progress, someone might wait if a frontend10538 * closes the session before powering on the VM. */10539 mData->mSession.mProgress->notifyComplete(E_FAIL,10540 COM_IIDOF(ISession),10541 getComponentName(),10542 tr("The VM session was closed before any attempt to power it on"));10543 mData->mSession.mProgress.setNull();10544 }10545 10546 /* Create the progress object the client will use to wait until10547 * #checkForDeath() is called to uninitialize this session object after10548 * it releases the IPC semaphore.10549 * Note! Because we're "reusing" mProgress here, this must be a proxy10550 * object just like for openRemoteSession. */10551 Assert(mData->mSession.mProgress.isNull());10552 ComObjPtr<ProgressProxy> progress;10553 progress.createObject();10554 ComPtr<IUnknown> pPeer(mPeer);10555 progress->init(mParent, pPeer,10556 Bstr(tr("Closing session")),10557 FALSE /* aCancelable */);10558 progress.queryInterfaceTo(aProgress);10559 mData->mSession.mProgress = progress;10560 }10561 else10562 {10563 /* the remote session is being normally closed */10564 Data::Session::RemoteControlList::iterator it =10565 mData->mSession.mRemoteControls.begin();10566 while (it != mData->mSession.mRemoteControls.end())10567 {10568 if (control == *it)10569 break;10570 ++it;10571 }10572 BOOL found = it != mData->mSession.mRemoteControls.end();10573 ComAssertMsgRet(found, ("The session is not found in the session list!"),10574 E_INVALIDARG);10575 mData->mSession.mRemoteControls.remove(*it);10576 }10577 10578 LogFlowThisFuncLeave();10579 return S_OK;10580 }10581 10582 /**10583 * @note Locks this object for writing.10584 */10585 STDMETHODIMP SessionMachine::BeginSavingState(IProgress *aProgress, BSTR *aStateFilePath)10586 {10587 LogFlowThisFuncEnter();10588 10589 AssertReturn(aProgress, E_INVALIDARG);10590 AssertReturn(aStateFilePath, E_POINTER);10591 10592 AutoCaller autoCaller(this);10593 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10594 10595 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);10596 10597 AssertReturn( mData->mMachineState == MachineState_Paused10598 && mSnapshotData.mLastState == MachineState_Null10599 && mSnapshotData.mProgressId.isEmpty()10600 && mSnapshotData.mStateFilePath.isEmpty(),10601 E_FAIL);10602 10603 /* memorize the progress ID and add it to the global collection */10604 Bstr progressId;10605 HRESULT rc = aProgress->COMGETTER(Id)(progressId.asOutParam());10606 AssertComRCReturn(rc, rc);10607 rc = mParent->addProgress(aProgress);10608 AssertComRCReturn(rc, rc);10609 10610 Bstr stateFilePath;10611 /* stateFilePath is null when the machine is not running */10612 if (mData->mMachineState == MachineState_Paused)10613 {10614 stateFilePath = Utf8StrFmt("%s%c{%RTuuid}.sav",10615 mUserData->m_strSnapshotFolderFull.c_str(),10616 RTPATH_DELIMITER, mData->mUuid.raw());10617 }10618 10619 /* fill in the snapshot data */10620 mSnapshotData.mLastState = mData->mMachineState;10621 mSnapshotData.mProgressId = Guid(progressId);10622 mSnapshotData.mStateFilePath = stateFilePath;10623 10624 /* set the state to Saving (this is expected by Console::SaveState()) */10625 setMachineState(MachineState_Saving);10626 10627 stateFilePath.cloneTo(aStateFilePath);10628 10629 return S_OK;10630 }10631 10632 /**10633 * @note Locks mParent + this object for writing.10634 */10635 STDMETHODIMP SessionMachine::EndSavingState(BOOL aSuccess)10636 {10637 LogFlowThisFunc(("\n"));10638 10639 AutoCaller autoCaller(this);10640 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10641 10642 /* endSavingState() need mParent lock */10643 AutoMultiWriteLock2 alock(mParent, this COMMA_LOCKVAL_SRC_POS);10644 10645 AssertReturn( mData->mMachineState == MachineState_Saving10646 && mSnapshotData.mLastState != MachineState_Null10647 && !mSnapshotData.mProgressId.isEmpty()10648 && !mSnapshotData.mStateFilePath.isEmpty(),10649 E_FAIL);10650 10651 /*10652 * on success, set the state to Saved;10653 * on failure, set the state to the state we had when BeginSavingState() was10654 * called (this is expected by Console::SaveState() and10655 * Console::saveStateThread())10656 */10657 if (aSuccess)10658 setMachineState(MachineState_Saved);10659 else10660 setMachineState(mSnapshotData.mLastState);10661 10662 return endSavingState(aSuccess);10663 }10664 10665 /**10666 * @note Locks this object for writing.10667 */10668 STDMETHODIMP SessionMachine::AdoptSavedState(IN_BSTR aSavedStateFile)10669 {10670 LogFlowThisFunc(("\n"));10671 10672 CheckComArgStrNotEmptyOrNull(aSavedStateFile);10673 10674 AutoCaller autoCaller(this);10675 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10676 10677 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);10678 10679 AssertReturn( mData->mMachineState == MachineState_PoweredOff10680 || mData->mMachineState == MachineState_Teleported10681 || mData->mMachineState == MachineState_Aborted10682 , E_FAIL); /** @todo setError. */10683 10684 Utf8Str stateFilePathFull = aSavedStateFile;10685 int vrc = calculateFullPath(stateFilePathFull, stateFilePathFull);10686 if (RT_FAILURE(vrc))10687 return setError(VBOX_E_FILE_ERROR,10688 tr("Invalid saved state file path '%ls' (%Rrc)"),10689 aSavedStateFile,10690 vrc);10691 10692 mSSData->mStateFilePath = stateFilePathFull;10693 10694 /* The below setMachineState() will detect the state transition and will10695 * update the settings file */10696 10697 return setMachineState(MachineState_Saved);10698 }10699 10700 STDMETHODIMP SessionMachine::PullGuestProperties(ComSafeArrayOut(BSTR, aNames),10701 ComSafeArrayOut(BSTR, aValues),10702 ComSafeArrayOut(LONG64, aTimestamps),10703 ComSafeArrayOut(BSTR, aFlags))10704 {10705 LogFlowThisFunc(("\n"));10706 10707 #ifdef VBOX_WITH_GUEST_PROPS10708 using namespace guestProp;10709 10710 AutoCaller autoCaller(this);10711 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10712 10713 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);10714 10715 AssertReturn(!ComSafeArrayOutIsNull(aNames), E_POINTER);10716 AssertReturn(!ComSafeArrayOutIsNull(aValues), E_POINTER);10717 AssertReturn(!ComSafeArrayOutIsNull(aTimestamps), E_POINTER);10718 AssertReturn(!ComSafeArrayOutIsNull(aFlags), E_POINTER);10719 10720 size_t cEntries = mHWData->mGuestProperties.size();10721 com::SafeArray<BSTR> names(cEntries);10722 com::SafeArray<BSTR> values(cEntries);10723 com::SafeArray<LONG64> timestamps(cEntries);10724 com::SafeArray<BSTR> flags(cEntries);10725 unsigned i = 0;10726 for (HWData::GuestPropertyList::iterator it = mHWData->mGuestProperties.begin();10727 it != mHWData->mGuestProperties.end();10728 ++it)10729 {10730 char szFlags[MAX_FLAGS_LEN + 1];10731 it->strName.cloneTo(&names[i]);10732 it->strValue.cloneTo(&values[i]);10733 timestamps[i] = it->mTimestamp;10734 /* If it is NULL, keep it NULL. */10735 if (it->mFlags)10736 {10737 writeFlags(it->mFlags, szFlags);10738 Bstr(szFlags).cloneTo(&flags[i]);10739 }10740 else10741 flags[i] = NULL;10742 ++i;10743 }10744 names.detachTo(ComSafeArrayOutArg(aNames));10745 values.detachTo(ComSafeArrayOutArg(aValues));10746 timestamps.detachTo(ComSafeArrayOutArg(aTimestamps));10747 flags.detachTo(ComSafeArrayOutArg(aFlags));10748 return S_OK;10749 #else10750 ReturnComNotImplemented();10751 #endif10752 }10753 10754 STDMETHODIMP SessionMachine::PushGuestProperty(IN_BSTR aName,10755 IN_BSTR aValue,10756 LONG64 aTimestamp,10757 IN_BSTR aFlags)10758 {10759 LogFlowThisFunc(("\n"));10760 10761 #ifdef VBOX_WITH_GUEST_PROPS10762 using namespace guestProp;10763 10764 CheckComArgStrNotEmptyOrNull(aName);10765 if (aValue != NULL && (!VALID_PTR(aValue) || !VALID_PTR(aFlags)))10766 return E_POINTER; /* aValue can be NULL to indicate deletion */10767 10768 try10769 {10770 /*10771 * Convert input up front.10772 */10773 Utf8Str utf8Name(aName);10774 uint32_t fFlags = NILFLAG;10775 if (aFlags)10776 {10777 Utf8Str utf8Flags(aFlags);10778 int vrc = validateFlags(utf8Flags.c_str(), &fFlags);10779 AssertRCReturn(vrc, E_INVALIDARG);10780 }10781 10782 /*10783 * Now grab the object lock, validate the state and do the update.10784 */10785 AutoCaller autoCaller(this);10786 if (FAILED(autoCaller.rc())) return autoCaller.rc();10787 10788 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);10789 10790 switch (mData->mMachineState)10791 {10792 case MachineState_Paused:10793 case MachineState_Running:10794 case MachineState_Teleporting:10795 case MachineState_TeleportingPausedVM:10796 case MachineState_LiveSnapshotting:10797 case MachineState_DeletingSnapshotOnline:10798 case MachineState_DeletingSnapshotPaused:10799 case MachineState_Saving:10800 break;10801 10802 default:10803 AssertMsgFailedReturn(("%s\n", Global::stringifyMachineState(mData->mMachineState)),10804 VBOX_E_INVALID_VM_STATE);10805 }10806 10807 setModified(IsModified_MachineData);10808 mHWData.backup();10809 10810 /** @todo r=bird: The careful memory handling doesn't work out here because10811 * the catch block won't undo any damange we've done. So, if push_back throws10812 * bad_alloc then you've lost the value.10813 *10814 * Another thing. Doing a linear search here isn't extremely efficient, esp.10815 * since values that changes actually bubbles to the end of the list. Using10816 * something that has an efficient lookup and can tollerate a bit of updates10817 * would be nice. RTStrSpace is one suggestion (it's not perfect). Some10818 * combination of RTStrCache (for sharing names and getting uniqueness into10819 * the bargain) and hash/tree is another. */10820 for (HWData::GuestPropertyList::iterator iter = mHWData->mGuestProperties.begin();10821 iter != mHWData->mGuestProperties.end();10822 ++iter)10823 if (utf8Name == iter->strName)10824 {10825 mHWData->mGuestProperties.erase(iter);10826 mData->mGuestPropertiesModified = TRUE;10827 break;10828 }10829 if (aValue != NULL)10830 {10831 HWData::GuestProperty property = { aName, aValue, aTimestamp, fFlags };10832 mHWData->mGuestProperties.push_back(property);10833 mData->mGuestPropertiesModified = TRUE;10834 }10835 10836 /*10837 * Send a callback notification if appropriate10838 */10839 if ( mHWData->mGuestPropertyNotificationPatterns.isEmpty()10840 || RTStrSimplePatternMultiMatch(mHWData->mGuestPropertyNotificationPatterns.c_str(),10841 RTSTR_MAX,10842 utf8Name.c_str(),10843 RTSTR_MAX, NULL)10844 )10845 {10846 alock.leave();10847 10848 mParent->onGuestPropertyChange(mData->mUuid,10849 aName,10850 aValue,10851 aFlags);10852 }10853 }10854 catch (...)10855 {10856 return VirtualBox::handleUnexpectedExceptions(RT_SRC_POS);10857 }10858 return S_OK;10859 #else10860 ReturnComNotImplemented();10861 #endif10862 }10863 10864 // public methods only for internal purposes10865 /////////////////////////////////////////////////////////////////////////////10866 10867 /**10868 * Called from the client watcher thread to check for expected or unexpected10869 * death of the client process that has a direct session to this machine.10870 *10871 * On Win32 and on OS/2, this method is called only when we've got the10872 * mutex (i.e. the client has either died or terminated normally) so it always10873 * returns @c true (the client is terminated, the session machine is10874 * uninitialized).10875 *10876 * On other platforms, the method returns @c true if the client process has10877 * terminated normally or abnormally and the session machine was uninitialized,10878 * and @c false if the client process is still alive.10879 *10880 * @note Locks this object for writing.10881 */10882 bool SessionMachine::checkForDeath()10883 {10884 Uninit::Reason reason;10885 bool terminated = false;10886 10887 /* Enclose autoCaller with a block because calling uninit() from under it10888 * will deadlock. */10889 {10890 AutoCaller autoCaller(this);10891 if (!autoCaller.isOk())10892 {10893 /* return true if not ready, to cause the client watcher to exclude10894 * the corresponding session from watching */10895 LogFlowThisFunc(("Already uninitialized!\n"));10896 return true;10897 }10898 10899 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);10900 10901 /* Determine the reason of death: if the session state is Closing here,10902 * everything is fine. Otherwise it means that the client did not call10903 * OnSessionEnd() before it released the IPC semaphore. This may happen10904 * either because the client process has abnormally terminated, or10905 * because it simply forgot to call ISession::Close() before exiting. We10906 * threat the latter also as an abnormal termination (see10907 * Session::uninit() for details). */10908 reason = mData->mSession.mState == SessionState_Unlocking ?10909 Uninit::Normal :10910 Uninit::Abnormal;10911 10912 #if defined(RT_OS_WINDOWS)10913 10914 AssertMsg(mIPCSem, ("semaphore must be created"));10915 10916 /* release the IPC mutex */10917 ::ReleaseMutex(mIPCSem);10918 10919 terminated = true;10920 10921 #elif defined(RT_OS_OS2)10922 10923 AssertMsg(mIPCSem, ("semaphore must be created"));10924 10925 /* release the IPC mutex */10926 ::DosReleaseMutexSem(mIPCSem);10927 10928 terminated = true;10929 10930 #elif defined(VBOX_WITH_SYS_V_IPC_SESSION_WATCHER)10931 10932 AssertMsg(mIPCSem >= 0, ("semaphore must be created"));10933 10934 int val = ::semctl(mIPCSem, 0, GETVAL);10935 if (val > 0)10936 {10937 /* the semaphore is signaled, meaning the session is terminated */10938 terminated = true;10939 }10940 10941 #else10942 # error "Port me!"10943 #endif10944 10945 } /* AutoCaller block */10946 10947 if (terminated)10948 uninit(reason);10949 10950 return terminated;10951 }10952 10953 /**10954 * @note Locks this object for reading.10955 */10956 HRESULT SessionMachine::onNetworkAdapterChange(INetworkAdapter *networkAdapter, BOOL changeAdapter)10957 {10958 LogFlowThisFunc(("\n"));10959 10960 AutoCaller autoCaller(this);10961 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10962 10963 ComPtr<IInternalSessionControl> directControl;10964 {10965 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);10966 directControl = mData->mSession.mDirectControl;10967 }10968 10969 /* ignore notifications sent after #OnSessionEnd() is called */10970 if (!directControl)10971 return S_OK;10972 10973 return directControl->OnNetworkAdapterChange(networkAdapter, changeAdapter);10974 }10975 10976 /**10977 * @note Locks this object for reading.10978 */10979 HRESULT SessionMachine::onSerialPortChange(ISerialPort *serialPort)10980 {10981 LogFlowThisFunc(("\n"));10982 10983 AutoCaller autoCaller(this);10984 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());10985 10986 ComPtr<IInternalSessionControl> directControl;10987 {10988 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);10989 directControl = mData->mSession.mDirectControl;10990 }10991 10992 /* ignore notifications sent after #OnSessionEnd() is called */10993 if (!directControl)10994 return S_OK;10995 10996 return directControl->OnSerialPortChange(serialPort);10997 }10998 10999 /**11000 * @note Locks this object for reading.11001 */11002 HRESULT SessionMachine::onParallelPortChange(IParallelPort *parallelPort)11003 {11004 LogFlowThisFunc(("\n"));11005 11006 AutoCaller autoCaller(this);11007 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11008 11009 ComPtr<IInternalSessionControl> directControl;11010 {11011 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11012 directControl = mData->mSession.mDirectControl;11013 }11014 11015 /* ignore notifications sent after #OnSessionEnd() is called */11016 if (!directControl)11017 return S_OK;11018 11019 return directControl->OnParallelPortChange(parallelPort);11020 }11021 11022 /**11023 * @note Locks this object for reading.11024 */11025 HRESULT SessionMachine::onStorageControllerChange()11026 {11027 LogFlowThisFunc(("\n"));11028 11029 AutoCaller autoCaller(this);11030 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11031 11032 ComPtr<IInternalSessionControl> directControl;11033 {11034 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11035 directControl = mData->mSession.mDirectControl;11036 }11037 11038 /* ignore notifications sent after #OnSessionEnd() is called */11039 if (!directControl)11040 return S_OK;11041 11042 return directControl->OnStorageControllerChange();11043 }11044 11045 /**11046 * @note Locks this object for reading.11047 */11048 HRESULT SessionMachine::onMediumChange(IMediumAttachment *aAttachment, BOOL aForce)11049 {11050 LogFlowThisFunc(("\n"));11051 11052 AutoCaller autoCaller(this);11053 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11054 11055 ComPtr<IInternalSessionControl> directControl;11056 {11057 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11058 directControl = mData->mSession.mDirectControl;11059 }11060 11061 /* ignore notifications sent after #OnSessionEnd() is called */11062 if (!directControl)11063 return S_OK;11064 11065 return directControl->OnMediumChange(aAttachment, aForce);11066 }11067 11068 /**11069 * @note Locks this object for reading.11070 */11071 HRESULT SessionMachine::onCPUChange(ULONG aCPU, BOOL aRemove)11072 {11073 LogFlowThisFunc(("\n"));11074 11075 AutoCaller autoCaller(this);11076 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());11077 11078 ComPtr<IInternalSessionControl> directControl;11079 {11080 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11081 directControl = mData->mSession.mDirectControl;11082 }11083 11084 /* ignore notifications sent after #OnSessionEnd() is called */11085 if (!directControl)11086 return S_OK;11087 11088 return directControl->OnCPUChange(aCPU, aRemove);11089 }11090 11091 HRESULT SessionMachine::onCPUPriorityChange(ULONG aCpuPriority)11092 {11093 LogFlowThisFunc(("\n"));11094 11095 AutoCaller autoCaller(this);11096 AssertComRCReturn (autoCaller.rc(), autoCaller.rc());11097 11098 ComPtr<IInternalSessionControl> directControl;11099 {11100 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11101 directControl = mData->mSession.mDirectControl;11102 }11103 11104 /* ignore notifications sent after #OnSessionEnd() is called */11105 if (!directControl)11106 return S_OK;11107 11108 return directControl->OnCPUPriorityChange(aCpuPriority);11109 }11110 11111 /**11112 * @note Locks this object for reading.11113 */11114 HRESULT SessionMachine::onVRDPServerChange(BOOL aRestart)11115 {11116 LogFlowThisFunc(("\n"));11117 11118 AutoCaller autoCaller(this);11119 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11120 11121 ComPtr<IInternalSessionControl> directControl;11122 {11123 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11124 directControl = mData->mSession.mDirectControl;11125 }11126 11127 /* ignore notifications sent after #OnSessionEnd() is called */11128 if (!directControl)11129 return S_OK;11130 11131 return directControl->OnVRDPServerChange(aRestart);11132 }11133 11134 /**11135 * @note Locks this object for reading.11136 */11137 HRESULT SessionMachine::onUSBControllerChange()11138 {11139 LogFlowThisFunc(("\n"));11140 11141 AutoCaller autoCaller(this);11142 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11143 11144 ComPtr<IInternalSessionControl> directControl;11145 {11146 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11147 directControl = mData->mSession.mDirectControl;11148 }11149 11150 /* ignore notifications sent after #OnSessionEnd() is called */11151 if (!directControl)11152 return S_OK;11153 11154 return directControl->OnUSBControllerChange();11155 }11156 11157 /**11158 * @note Locks this object for reading.11159 */11160 HRESULT SessionMachine::onSharedFolderChange()11161 {11162 LogFlowThisFunc(("\n"));11163 11164 AutoCaller autoCaller(this);11165 AssertComRCReturnRC(autoCaller.rc());11166 11167 ComPtr<IInternalSessionControl> directControl;11168 {11169 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11170 directControl = mData->mSession.mDirectControl;11171 }11172 11173 /* ignore notifications sent after #OnSessionEnd() is called */11174 if (!directControl)11175 return S_OK;11176 11177 return directControl->OnSharedFolderChange(FALSE /* aGlobal */);11178 }11179 11180 /**11181 * Returns @c true if this machine's USB controller reports it has a matching11182 * filter for the given USB device and @c false otherwise.11183 *11184 * @note Caller must have requested machine read lock.11185 */11186 bool SessionMachine::hasMatchingUSBFilter(const ComObjPtr<HostUSBDevice> &aDevice, ULONG *aMaskedIfs)11187 {11188 AutoCaller autoCaller(this);11189 /* silently return if not ready -- this method may be called after the11190 * direct machine session has been called */11191 if (!autoCaller.isOk())11192 return false;11193 11194 11195 #ifdef VBOX_WITH_USB11196 switch (mData->mMachineState)11197 {11198 case MachineState_Starting:11199 case MachineState_Restoring:11200 case MachineState_TeleportingIn:11201 case MachineState_Paused:11202 case MachineState_Running:11203 /** @todo Live Migration: snapshoting & teleporting. Need to fend things of11204 * elsewhere... */11205 return mUSBController->hasMatchingFilter(aDevice, aMaskedIfs);11206 default: break;11207 }11208 #else11209 NOREF(aDevice);11210 NOREF(aMaskedIfs);11211 #endif11212 return false;11213 }11214 11215 /**11216 * @note The calls shall hold no locks. Will temporarily lock this object for reading.11217 */11218 HRESULT SessionMachine::onUSBDeviceAttach(IUSBDevice *aDevice,11219 IVirtualBoxErrorInfo *aError,11220 ULONG aMaskedIfs)11221 {11222 LogFlowThisFunc(("\n"));11223 11224 AutoCaller autoCaller(this);11225 11226 /* This notification may happen after the machine object has been11227 * uninitialized (the session was closed), so don't assert. */11228 if (FAILED(autoCaller.rc())) return autoCaller.rc();11229 11230 ComPtr<IInternalSessionControl> directControl;11231 {11232 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11233 directControl = mData->mSession.mDirectControl;11234 }11235 11236 /* fail on notifications sent after #OnSessionEnd() is called, it is11237 * expected by the caller */11238 if (!directControl)11239 return E_FAIL;11240 11241 /* No locks should be held at this point. */11242 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));11243 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));11244 11245 return directControl->OnUSBDeviceAttach(aDevice, aError, aMaskedIfs);11246 }11247 11248 /**11249 * @note The calls shall hold no locks. Will temporarily lock this object for reading.11250 */11251 HRESULT SessionMachine::onUSBDeviceDetach(IN_BSTR aId,11252 IVirtualBoxErrorInfo *aError)11253 {11254 LogFlowThisFunc(("\n"));11255 11256 AutoCaller autoCaller(this);11257 11258 /* This notification may happen after the machine object has been11259 * uninitialized (the session was closed), so don't assert. */11260 if (FAILED(autoCaller.rc())) return autoCaller.rc();11261 11262 ComPtr<IInternalSessionControl> directControl;11263 {11264 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11265 directControl = mData->mSession.mDirectControl;11266 }11267 11268 /* fail on notifications sent after #OnSessionEnd() is called, it is11269 * expected by the caller */11270 if (!directControl)11271 return E_FAIL;11272 11273 /* No locks should be held at this point. */11274 AssertMsg(RTLockValidatorWriteLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorWriteLockGetCount(RTThreadSelf())));11275 AssertMsg(RTLockValidatorReadLockGetCount(RTThreadSelf()) == 0, ("%d\n", RTLockValidatorReadLockGetCount(RTThreadSelf())));11276 11277 return directControl->OnUSBDeviceDetach(aId, aError);11278 }11279 11280 // protected methods11281 /////////////////////////////////////////////////////////////////////////////11282 11283 /**11284 * Helper method to finalize saving the state.11285 *11286 * @note Must be called from under this object's lock.11287 *11288 * @param aSuccess TRUE if the snapshot has been taken successfully11289 *11290 * @note Locks mParent + this objects for writing.11291 */11292 HRESULT SessionMachine::endSavingState(BOOL aSuccess)11293 {11294 LogFlowThisFuncEnter();11295 11296 AutoCaller autoCaller(this);11297 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11298 11299 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);11300 11301 HRESULT rc = S_OK;11302 11303 if (aSuccess)11304 {11305 mSSData->mStateFilePath = mSnapshotData.mStateFilePath;11306 11307 /* save all VM settings */11308 rc = saveSettings(NULL);11309 // no need to check whether VirtualBox.xml needs saving also since11310 // we can't have a name change pending at this point11311 }11312 else11313 {11314 /* delete the saved state file (it might have been already created) */11315 RTFileDelete(mSnapshotData.mStateFilePath.c_str());11316 }11317 11318 /* remove the completed progress object */11319 mParent->removeProgress(mSnapshotData.mProgressId);11320 11321 /* clear out the temporary saved state data */11322 mSnapshotData.mLastState = MachineState_Null;11323 mSnapshotData.mProgressId.clear();11324 mSnapshotData.mStateFilePath.setNull();11325 11326 LogFlowThisFuncLeave();11327 return rc;11328 }11329 11330 /**11331 * Locks the attached media.11332 *11333 * All attached hard disks are locked for writing and DVD/floppy are locked for11334 * reading. Parents of attached hard disks (if any) are locked for reading.11335 *11336 * This method also performs accessibility check of all media it locks: if some11337 * media is inaccessible, the method will return a failure and a bunch of11338 * extended error info objects per each inaccessible medium.11339 *11340 * Note that this method is atomic: if it returns a success, all media are11341 * locked as described above; on failure no media is locked at all (all11342 * succeeded individual locks will be undone).11343 *11344 * This method is intended to be called when the machine is in Starting or11345 * Restoring state and asserts otherwise.11346 *11347 * The locks made by this method must be undone by calling #unlockMedia() when11348 * no more needed.11349 */11350 HRESULT SessionMachine::lockMedia()11351 {11352 AutoCaller autoCaller(this);11353 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11354 11355 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);11356 11357 AssertReturn( mData->mMachineState == MachineState_Starting11358 || mData->mMachineState == MachineState_Restoring11359 || mData->mMachineState == MachineState_TeleportingIn, E_FAIL);11360 /* bail out if trying to lock things with already set up locking */11361 AssertReturn(mData->mSession.mLockedMedia.IsEmpty(), E_FAIL);11362 11363 MultiResult mrc(S_OK);11364 11365 /* Collect locking information for all medium objects attached to the VM. */11366 for (MediaData::AttachmentList::const_iterator it = mMediaData->mAttachments.begin();11367 it != mMediaData->mAttachments.end();11368 ++it)11369 {11370 MediumAttachment* pAtt = *it;11371 DeviceType_T devType = pAtt->getType();11372 Medium *pMedium = pAtt->getMedium();11373 11374 MediumLockList *pMediumLockList(new MediumLockList());11375 // There can be attachments without a medium (floppy/dvd), and thus11376 // it's impossible to create a medium lock list. It still makes sense11377 // to have the empty medium lock list in the map in case a medium is11378 // attached later.11379 if (pMedium != NULL)11380 {11381 MediumType_T mediumType = pMedium->getType();11382 bool fIsReadOnlyImage = devType == DeviceType_DVD11383 || mediumType == MediumType_Shareable;11384 bool fIsVitalImage = (devType == DeviceType_HardDisk);11385 11386 mrc = pMedium->createMediumLockList(fIsVitalImage /* fFailIfInaccessible */,11387 !fIsReadOnlyImage /* fMediumLockWrite */,11388 NULL,11389 *pMediumLockList);11390 if (FAILED(mrc))11391 {11392 delete pMediumLockList;11393 mData->mSession.mLockedMedia.Clear();11394 break;11395 }11396 }11397 11398 HRESULT rc = mData->mSession.mLockedMedia.Insert(pAtt, pMediumLockList);11399 if (FAILED(rc))11400 {11401 mData->mSession.mLockedMedia.Clear();11402 mrc = setError(rc,11403 tr("Collecting locking information for all attached media failed"));11404 break;11405 }11406 }11407 11408 if (SUCCEEDED(mrc))11409 {11410 /* Now lock all media. If this fails, nothing is locked. */11411 HRESULT rc = mData->mSession.mLockedMedia.Lock();11412 if (FAILED(rc))11413 {11414 mrc = setError(rc,11415 tr("Locking of attached media failed"));11416 }11417 }11418 11419 return mrc;11420 }11421 11422 /**11423 * Undoes the locks made by by #lockMedia().11424 */11425 void SessionMachine::unlockMedia()11426 {11427 AutoCaller autoCaller(this);11428 AssertComRCReturnVoid(autoCaller.rc());11429 11430 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);11431 11432 /* we may be holding important error info on the current thread;11433 * preserve it */11434 ErrorInfoKeeper eik;11435 11436 HRESULT rc = mData->mSession.mLockedMedia.Clear();11437 AssertComRC(rc);11438 }11439 11440 /**11441 * Helper to change the machine state (reimplementation).11442 *11443 * @note Locks this object for writing.11444 */11445 HRESULT SessionMachine::setMachineState(MachineState_T aMachineState)11446 {11447 LogFlowThisFuncEnter();11448 LogFlowThisFunc(("aMachineState=%s\n", Global::stringifyMachineState(aMachineState) ));11449 11450 AutoCaller autoCaller(this);11451 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11452 11453 AutoWriteLock alock(this COMMA_LOCKVAL_SRC_POS);11454 11455 MachineState_T oldMachineState = mData->mMachineState;11456 11457 AssertMsgReturn(oldMachineState != aMachineState,11458 ("oldMachineState=%s, aMachineState=%s\n",11459 Global::stringifyMachineState(oldMachineState), Global::stringifyMachineState(aMachineState)),11460 E_FAIL);11461 11462 HRESULT rc = S_OK;11463 11464 int stsFlags = 0;11465 bool deleteSavedState = false;11466 11467 /* detect some state transitions */11468 11469 if ( ( oldMachineState == MachineState_Saved11470 && aMachineState == MachineState_Restoring)11471 || ( ( oldMachineState == MachineState_PoweredOff11472 || oldMachineState == MachineState_Teleported11473 || oldMachineState == MachineState_Aborted11474 )11475 && ( aMachineState == MachineState_TeleportingIn11476 || aMachineState == MachineState_Starting11477 )11478 )11479 )11480 {11481 /* The EMT thread is about to start */11482 11483 /* Nothing to do here for now... */11484 11485 /// @todo NEWMEDIA don't let mDVDDrive and other children11486 /// change anything when in the Starting/Restoring state11487 }11488 else if ( ( oldMachineState == MachineState_Running11489 || oldMachineState == MachineState_Paused11490 || oldMachineState == MachineState_Teleporting11491 || oldMachineState == MachineState_LiveSnapshotting11492 || oldMachineState == MachineState_Stuck11493 || oldMachineState == MachineState_Starting11494 || oldMachineState == MachineState_Stopping11495 || oldMachineState == MachineState_Saving11496 || oldMachineState == MachineState_Restoring11497 || oldMachineState == MachineState_TeleportingPausedVM11498 || oldMachineState == MachineState_TeleportingIn11499 )11500 && ( aMachineState == MachineState_PoweredOff11501 || aMachineState == MachineState_Saved11502 || aMachineState == MachineState_Teleported11503 || aMachineState == MachineState_Aborted11504 )11505 /* ignore PoweredOff->Saving->PoweredOff transition when taking a11506 * snapshot */11507 && ( mSnapshotData.mSnapshot.isNull()11508 || mSnapshotData.mLastState >= MachineState_Running /** @todo Live Migration: clean up (lazy bird) */11509 )11510 )11511 {11512 /* The EMT thread has just stopped, unlock attached media. Note that as11513 * opposed to locking that is done from Console, we do unlocking here11514 * because the VM process may have aborted before having a chance to11515 * properly unlock all media it locked. */11516 11517 unlockMedia();11518 }11519 11520 if (oldMachineState == MachineState_Restoring)11521 {11522 if (aMachineState != MachineState_Saved)11523 {11524 /*11525 * delete the saved state file once the machine has finished11526 * restoring from it (note that Console sets the state from11527 * Restoring to Saved if the VM couldn't restore successfully,11528 * to give the user an ability to fix an error and retry --11529 * we keep the saved state file in this case)11530 */11531 deleteSavedState = true;11532 }11533 }11534 else if ( oldMachineState == MachineState_Saved11535 && ( aMachineState == MachineState_PoweredOff11536 || aMachineState == MachineState_Aborted11537 || aMachineState == MachineState_Teleported11538 )11539 )11540 {11541 /*11542 * delete the saved state after Console::ForgetSavedState() is called11543 * or if the VM process (owning a direct VM session) crashed while the11544 * VM was Saved11545 */11546 11547 /// @todo (dmik)11548 // Not sure that deleting the saved state file just because of the11549 // client death before it attempted to restore the VM is a good11550 // thing. But when it crashes we need to go to the Aborted state11551 // which cannot have the saved state file associated... The only11552 // way to fix this is to make the Aborted condition not a VM state11553 // but a bool flag: i.e., when a crash occurs, set it to true and11554 // change the state to PoweredOff or Saved depending on the11555 // saved state presence.11556 11557 deleteSavedState = true;11558 mData->mCurrentStateModified = TRUE;11559 stsFlags |= SaveSTS_CurStateModified;11560 }11561 11562 if ( aMachineState == MachineState_Starting11563 || aMachineState == MachineState_Restoring11564 || aMachineState == MachineState_TeleportingIn11565 )11566 {11567 /* set the current state modified flag to indicate that the current11568 * state is no more identical to the state in the11569 * current snapshot */11570 if (!mData->mCurrentSnapshot.isNull())11571 {11572 mData->mCurrentStateModified = TRUE;11573 stsFlags |= SaveSTS_CurStateModified;11574 }11575 }11576 11577 if (deleteSavedState)11578 {11579 if (mRemoveSavedState)11580 {11581 Assert(!mSSData->mStateFilePath.isEmpty());11582 RTFileDelete(mSSData->mStateFilePath.c_str());11583 }11584 mSSData->mStateFilePath.setNull();11585 stsFlags |= SaveSTS_StateFilePath;11586 }11587 11588 /* redirect to the underlying peer machine */11589 mPeer->setMachineState(aMachineState);11590 11591 if ( aMachineState == MachineState_PoweredOff11592 || aMachineState == MachineState_Teleported11593 || aMachineState == MachineState_Aborted11594 || aMachineState == MachineState_Saved)11595 {11596 /* the machine has stopped execution11597 * (or the saved state file was adopted) */11598 stsFlags |= SaveSTS_StateTimeStamp;11599 }11600 11601 if ( ( oldMachineState == MachineState_PoweredOff11602 || oldMachineState == MachineState_Aborted11603 || oldMachineState == MachineState_Teleported11604 )11605 && aMachineState == MachineState_Saved)11606 {11607 /* the saved state file was adopted */11608 Assert(!mSSData->mStateFilePath.isEmpty());11609 stsFlags |= SaveSTS_StateFilePath;11610 }11611 11612 if ( aMachineState == MachineState_PoweredOff11613 || aMachineState == MachineState_Aborted11614 || aMachineState == MachineState_Teleported)11615 {11616 /* Make sure any transient guest properties get removed from the11617 * property store on shutdown. */11618 11619 HWData::GuestPropertyList::iterator it;11620 BOOL fNeedsSaving = mData->mGuestPropertiesModified;11621 if (!fNeedsSaving)11622 for (it = mHWData->mGuestProperties.begin();11623 it != mHWData->mGuestProperties.end(); ++it)11624 if (it->mFlags & guestProp::TRANSIENT)11625 {11626 fNeedsSaving = true;11627 break;11628 }11629 if (fNeedsSaving)11630 {11631 mData->mCurrentStateModified = TRUE;11632 stsFlags |= SaveSTS_CurStateModified;11633 SaveSettings(); // @todo r=dj why the public method? why first SaveSettings and then saveStateSettings?11634 }11635 }11636 11637 rc = saveStateSettings(stsFlags);11638 11639 if ( ( oldMachineState != MachineState_PoweredOff11640 && oldMachineState != MachineState_Aborted11641 && oldMachineState != MachineState_Teleported11642 )11643 && ( aMachineState == MachineState_PoweredOff11644 || aMachineState == MachineState_Aborted11645 || aMachineState == MachineState_Teleported11646 )11647 )11648 {11649 /* we've been shut down for any reason */11650 /* no special action so far */11651 }11652 11653 LogFlowThisFunc(("rc=%Rhrc [%s]\n", rc, Global::stringifyMachineState(mData->mMachineState) ));11654 LogFlowThisFuncLeave();11655 return rc;11656 }11657 11658 /**11659 * Sends the current machine state value to the VM process.11660 *11661 * @note Locks this object for reading, then calls a client process.11662 */11663 HRESULT SessionMachine::updateMachineStateOnClient()11664 {11665 AutoCaller autoCaller(this);11666 AssertComRCReturn(autoCaller.rc(), autoCaller.rc());11667 11668 ComPtr<IInternalSessionControl> directControl;11669 {11670 AutoReadLock alock(this COMMA_LOCKVAL_SRC_POS);11671 AssertReturn(!!mData, E_FAIL);11672 directControl = mData->mSession.mDirectControl;11673 11674 /* directControl may be already set to NULL here in #OnSessionEnd()11675 * called too early by the direct session process while there is still11676 * some operation (like deleting the snapshot) in progress. The client11677 * process in this case is waiting inside Session::close() for the11678 * "end session" process object to complete, while #uninit() called by11679 * #checkForDeath() on the Watcher thread is waiting for the pending11680 * operation to complete. For now, we accept this inconsitent behavior11681 * and simply do nothing here. */11682 11683 if (mData->mSession.mState == SessionState_Unlocking)11684 return S_OK;11685 11686 AssertReturn(!directControl.isNull(), E_FAIL);11687 }11688 11689 return directControl->UpdateMachineState(mData->mMachineState);11690 }
Note:
See TracChangeset
for help on using the changeset viewer.